use sqlx::PgPool;
use chrono::{NaiveDate, Timelike};
use uuid::Uuid;
use crate::{
    db,
    models::{Schedule, ScheduleV2, ScheduleData, ConferenceData, Personnel},
    error::{Result, ApiError},
    utils::{
        converters::{convert_schedules_to_v1, convert_schedule_to_v2},
        db_operations::ScheduleOperations,
    },
};

#[derive(Debug, serde::Deserialize)]
pub struct CreateScheduleRequest {
    pub date: String,
    pub song_leader: Option<String>,
    pub ss_teacher: Option<String>,
    pub ss_leader: Option<String>,
    pub mission_story: Option<String>,
    pub special_program: Option<String>,
    pub sermon_speaker: Option<String>,
    pub scripture: Option<String>,
    pub offering: Option<String>,
    pub deacons: Option<String>,
    pub special_music: Option<String>,
    pub childrens_story: Option<String>,
    pub afternoon_program: Option<String>,
}

/// Schedule business logic service
/// Contains all schedule-related business logic, keeping handlers thin and focused on HTTP concerns
pub struct ScheduleService;

impl ScheduleService {
    /// Get schedule by date with V1 format - returns ScheduleData format
    pub async fn get_schedule_data_v1(pool: &PgPool, date_str: &str) -> Result<ScheduleData> {
        let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
            .map_err(|_| ApiError::BadRequest("Invalid date format. Use YYYY-MM-DD".to_string()))?;
        
        let schedule = ScheduleOperations::get_by_date(pool, date).await?;
        
        let personnel = if let Some(s) = schedule {
            Personnel {
                ss_leader: s.ss_leader.unwrap_or_default(),
                ss_teacher: s.ss_teacher.unwrap_or_default(),
                mission_story: s.mission_story.unwrap_or_default(),
                song_leader: s.song_leader.unwrap_or_default(),
                announcements: s.scripture.unwrap_or_default(), // Map scripture to announcements
                offering: s.offering.unwrap_or_default(),
                special_music: s.special_music.unwrap_or_default(),
                speaker: s.sermon_speaker.unwrap_or_default(),
            }
        } else {
            // Return empty data if no schedule found
            Personnel {
                ss_leader: String::new(),
                ss_teacher: String::new(),
                mission_story: String::new(),
                song_leader: String::new(),
                announcements: String::new(),
                offering: String::new(),
                special_music: String::new(),
                speaker: String::new(),
            }
        };

        Ok(ScheduleData {
            date: date_str.to_string(),
            personnel,
        })
    }

    /// Get conference data for a specific date
    pub async fn get_conference_data_v1(pool: &PgPool, date_str: &str) -> Result<ConferenceData> {
        let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
            .map_err(|_| ApiError::BadRequest("Invalid date format. Use YYYY-MM-DD".to_string()))?;
        
        // Get offering for this date
        let offering = sqlx::query!("SELECT offering_type FROM conference_offerings WHERE date = $1", date)
            .fetch_optional(pool)
            .await?;
        
        // Get sunset for this date
        let sunset = sqlx::query!("SELECT sunset_time FROM sunset_times WHERE date = $1 AND city = 'Springfield'", date)
            .fetch_optional(pool)
            .await?;
        
        // Get sunset for next week (same date + 7 days)
        let next_week = date + chrono::Duration::days(7);
        let next_week_sunset = sqlx::query!("SELECT sunset_time FROM sunset_times WHERE date = $1 AND city = 'Springfield'", next_week)
            .fetch_optional(pool)
            .await?;
        
        Ok(ConferenceData {
            date: date_str.to_string(),
            offering_focus: offering.map(|o| o.offering_type).unwrap_or("Local Church Budget".to_string()),
            sunset_tonight: sunset.map(|s| format!("{}:{:02} pm", 
                if s.sunset_time.hour() > 12 { s.sunset_time.hour() - 12 } else { s.sunset_time.hour() }, 
                s.sunset_time.minute())).unwrap_or("8:00 pm".to_string()),
            sunset_next_friday: next_week_sunset.map(|s| format!("{}:{:02} pm", 
                if s.sunset_time.hour() > 12 { s.sunset_time.hour() - 12 } else { s.sunset_time.hour() }, 
                s.sunset_time.minute())).unwrap_or("8:00 pm".to_string()),
        })
    }

    /// Create or update a schedule
    pub async fn create_or_update_schedule(pool: &PgPool, request: CreateScheduleRequest) -> Result<Schedule> {
        let date = NaiveDate::parse_from_str(&request.date, "%Y-%m-%d")
            .map_err(|_| ApiError::BadRequest("Invalid date format. Use YYYY-MM-DD".to_string()))?;
        
        let schedule = Schedule {
            id: Uuid::new_v4(),
            date,
            song_leader: request.song_leader,
            ss_teacher: request.ss_teacher,
            ss_leader: request.ss_leader,
            mission_story: request.mission_story,
            special_program: request.special_program,
            sermon_speaker: request.sermon_speaker,
            scripture: request.scripture,
            offering: request.offering,
            deacons: request.deacons,
            special_music: request.special_music,
            childrens_story: request.childrens_story,
            afternoon_program: request.afternoon_program,
            created_at: None,
            updated_at: None,
        };
        
        db::schedule::insert_or_update(pool, &schedule).await
    }

    /// Delete schedule by date
    pub async fn delete_schedule(pool: &PgPool, date_str: &str) -> Result<()> {
        let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
            .map_err(|_| ApiError::BadRequest("Invalid date format. Use YYYY-MM-DD".to_string()))?;
        
        sqlx::query!("DELETE FROM schedule WHERE date = $1", date)
            .execute(pool)
            .await?;
        
        Ok(())
    }

    /// List all schedules with V1 format
    pub async fn list_schedules_v1(pool: &PgPool) -> Result<Vec<Schedule>> {
        let schedules = sqlx::query_as!(
            Schedule,
            "SELECT * FROM schedule ORDER BY date"
        )
        .fetch_all(pool)
        .await?;
        
        convert_schedules_to_v1(schedules)
    }

    // V2 API methods (UTC timezone as per shared converter)

    /// Get schedule by date with V2 format (UTC timestamps)
    pub async fn get_schedule_v2(pool: &PgPool, date: &NaiveDate) -> Result<Option<ScheduleV2>> {
        let schedule = ScheduleOperations::get_by_date(pool, *date).await?;
        
        match schedule {
            Some(s) => {
                let converted = convert_schedule_to_v2(s)?;
                Ok(Some(converted))
            },
            None => Ok(None),
        }
    }

    /// Get conference data for V2 (simplified version)
    pub async fn get_conference_data_v2(pool: &PgPool, date: &NaiveDate) -> Result<ConferenceData> {
        let schedule = ScheduleOperations::get_by_date(pool, *date).await?
            .ok_or_else(|| ApiError::NotFound("Schedule not found".to_string()))?;
        
        Ok(ConferenceData {
            date: date.format("%Y-%m-%d").to_string(),
            offering_focus: schedule.offering.unwrap_or_else(|| "General Church Budget".to_string()),
            sunset_tonight: "8:00 pm".to_string(),
            sunset_next_friday: "8:00 pm".to_string(),
        })
    }
}