use axum::extract::Multipart;
use chrono::{DateTime, Utc, NaiveDateTime, Weekday, Datelike, Timelike};
use crate::error::{ApiError, Result};

/// Helper struct for processing multipart form data
pub struct MultipartProcessor {
    pub fields: std::collections::HashMap<String, String>,
    pub files: std::collections::HashMap<String, Vec<u8>>,
}

impl MultipartProcessor {
    pub fn new() -> Self {
        Self {
            fields: std::collections::HashMap::new(),
            files: std::collections::HashMap::new(),
        }
    }

    /// Process all fields from multipart data
    pub async fn process_multipart(&mut self, mut multipart: Multipart) -> Result<()> {
        while let Some(field) = multipart.next_field().await.map_err(|e| {
            ApiError::ValidationError(format!("Failed to read multipart field: {}", e))
        })? {
            let name = field.name().unwrap_or("").to_string();
            
            if let Some(filename) = field.file_name() {
                // Handle file upload
                let filename = filename.to_string();
                let data = field.bytes().await.map_err(|e| {
                    ApiError::ValidationError(format!("Failed to read file {}: {}", filename, e))
                })?;
                self.files.insert(name, data.to_vec());
            } else {
                // Handle text field
                let content = field.text().await.map_err(|e| {
                    ApiError::ValidationError(format!("Failed to read field {}: {}", name, e))
                })?;
                self.fields.insert(name, content);
            }
        }
        Ok(())
    }

    /// Get a required string field
    pub fn get_required_string(&self, field_name: &str) -> Result<String> {
        self.fields
            .get(field_name)
            .ok_or_else(|| ApiError::ValidationError(format!("{} is required", field_name)))
            .map(|s| s.clone())
    }

    /// Get an optional string field
    pub fn get_optional_string(&self, field_name: &str) -> Option<String> {
        self.fields.get(field_name).map(|s| s.clone())
    }

    /// Get a required boolean field
    pub fn get_optional_bool(&self, field_name: &str) -> Result<Option<bool>> {
        if let Some(value) = self.fields.get(field_name) {
            match value.to_lowercase().as_str() {
                "true" | "1" | "yes" | "on" => Ok(Some(true)),
                "false" | "0" | "no" | "off" => Ok(Some(false)),
                _ => Err(ApiError::ValidationError(format!("Invalid boolean value for {}: {}", field_name, value))),
            }
        } else {
            Ok(None)
        }
    }

    /// Parse datetime field with multiple formats and proper timezone conversion
    pub fn get_datetime(&self, field_name: &str) -> Result<DateTime<Utc>> {
        let datetime_str = self.get_required_string(field_name)?;
        
        // First try the shared timezone-aware parsing function
        if let Ok(utc_time) = crate::utils::datetime::parse_event_datetime_to_utc(&datetime_str) {
            return Ok(utc_time);
        }
        
        // Fallback to legacy formats for backward compatibility
        // These will now be treated as EST/EDT times and converted to UTC
        let formats = [
            "%Y-%m-%dT%H:%M",
            "%Y-%m-%dT%H:%M:%S",
            "%Y-%m-%d %H:%M",
            "%Y-%m-%d %H:%M:%S",
            "%m/%d/%Y %H:%M",
            "%m/%d/%Y %I:%M %p",
        ];

        for format in &formats {
            if let Ok(naive_dt) = NaiveDateTime::parse_from_str(&datetime_str, format) {
                // Convert naive datetime as EST/EDT to UTC using shared function
                let formatted_for_conversion = naive_dt.format("%Y-%m-%dT%H:%M:%S").to_string();
                return crate::utils::datetime::parse_event_datetime_to_utc(&formatted_for_conversion);
            }
        }

        // Try parsing as RFC3339 (already has timezone info)
        if let Ok(dt) = DateTime::parse_from_rfc3339(&datetime_str) {
            return Ok(dt.to_utc());
        }

        Err(ApiError::ValidationError(format!(
            "Invalid datetime format for {}: {}. Expected formats: YYYY-MM-DDTHH:MM, YYYY-MM-DD HH:MM, MM/DD/YYYY HH:MM, etc.",
            field_name, datetime_str
        )))
    }


    /// Get file data
    pub fn get_file(&self, field_name: &str) -> Option<&Vec<u8>> {
        self.files.get(field_name)
    }

    /// Check if field exists
    pub fn has_field(&self, field_name: &str) -> bool {
        self.fields.contains_key(field_name)
    }


    /// Validate required fields exist
    pub fn validate_required_fields(&self, required_fields: &[&str]) -> Result<()> {
        let missing_fields: Vec<&str> = required_fields
            .iter()
            .filter(|field| !self.has_field(field))
            .copied()
            .collect();

        if !missing_fields.is_empty() {
            return Err(ApiError::ValidationError(format!(
                "Missing required fields: {}",
                missing_fields.join(", ")
            )));
        }

        Ok(())
    }

}

/// Convenience macro for extracting multipart data
#[macro_export]
macro_rules! extract_multipart_field {
    ($processor:expr, $field:literal, required) => {
        $processor.get_required_string($field)?
    };
    ($processor:expr, $field:literal, optional) => {
        $processor.get_optional_string($field)
    };
    ($processor:expr, $field:literal, datetime) => {
        $processor.get_datetime($field)?
    };
    ($processor:expr, $field:literal, optional_datetime) => {
        $processor.get_optional_datetime($field)?
    };
    ($processor:expr, $field:literal, bool) => {
        $processor.get_optional_bool($field)?
    };
    ($processor:expr, $field:literal, file) => {
        $processor.get_file($field)
    };
}

/// Helper function to process common event multipart data
pub async fn process_event_multipart(
    multipart: Multipart,
) -> Result<(crate::models::SubmitEventRequest, Option<Vec<u8>>, Option<Vec<u8>>)> {
    let mut processor = MultipartProcessor::new();
    processor.process_multipart(multipart).await?;

    // Validate required fields (removed bulletin_week since we auto-calculate it)
    processor.validate_required_fields(&[
        "title", "description", "start_time", "end_time", "location", "category"
    ])?;

    // Auto-determine bulletin_week based on submission time
    // Before Friday 00:00 UTC (Thursday 7pm EST) = "current", after = "next"
    let now = chrono::Utc::now();
    let current_weekday = now.weekday();
    let current_hour = now.hour();
    
    let bulletin_week = match current_weekday {
        Weekday::Mon | Weekday::Tue | Weekday::Wed | Weekday::Thu => "current".to_string(),
        Weekday::Fri if current_hour == 0 => "current".to_string(),
        _ => "next".to_string(),
    };

    let request = crate::models::SubmitEventRequest {
        title: extract_multipart_field!(processor, "title", required),
        description: extract_multipart_field!(processor, "description", required),
        start_time: extract_multipart_field!(processor, "start_time", datetime),
        end_time: extract_multipart_field!(processor, "end_time", datetime),
        location: extract_multipart_field!(processor, "location", required),
        location_url: extract_multipart_field!(processor, "location_url", optional),
        category: extract_multipart_field!(processor, "category", required),
        is_featured: extract_multipart_field!(processor, "is_featured", bool),
        recurring_type: extract_multipart_field!(processor, "recurring_type", optional)
            .map(|rt| crate::utils::validation::normalize_recurring_type(&rt)),
        bulletin_week,
        submitter_email: extract_multipart_field!(processor, "submitter_email", optional),
        image: None, // Will be set after file processing
        thumbnail: None, // Will be set after file processing
    };

    let image_data = extract_multipart_field!(processor, "image", file).cloned();
    let thumbnail_data = extract_multipart_field!(processor, "thumbnail", file).cloned();

    Ok((request, image_data, thumbnail_data))
}