use crate::html_parser::{SabbathSchoolItem, DivineWorshipItem};
use anyhow::Result;
use bulletin_shared::Event;
use serde::Serialize;
use std::path::Path;
use std::collections::HashMap;
use tera::{Tera, Context};
use regex::Regex;

#[derive(Debug, Serialize)]
pub struct TemplateContext {
    pub bulletin_date: String,
    pub bulletin_theme_title: String,
    pub church_name: String,
    pub cover_image_path: Option<String>,
    pub sabbath_school_items: Vec<SabbathSchoolItem>,
    pub divine_worship_items: Vec<DivineWorshipItem>,
    pub announcements: Vec<AnnouncementItem>,
    pub sunset_times: String,
    pub contact_info: ContactInfo,
}

#[derive(Debug, Serialize)]
pub struct ContactInfo {
    pub phone: String,
    pub website: String,
    pub address: String,
}

#[derive(Debug, Serialize)]
pub struct AnnouncementItem {
    pub title: String,
    pub description: String,
    pub start_time_formatted: Option<String>,
    pub end_time_formatted: Option<String>,
    pub time_display: Option<String>,
    pub location: Option<String>,
    pub is_multi_day: bool,
    pub is_grouped: bool,
    pub group_sessions: Vec<GroupSession>,
}

#[derive(Debug, Serialize)]
pub struct GroupSession {
    pub title: String,
    pub date: String,
    pub time: String,
    pub location: Option<String>,
}

impl AnnouncementItem {
    pub fn from_event(event: Event) -> Self {
        let (start_time_formatted, end_time_formatted, time_display, is_multi_day) = 
            Self::format_event_times(&event);
        
        Self {
            title: crate::html_parser::strip_html_tags(&event.name),
            description: crate::html_parser::strip_html_tags(&event.description.unwrap_or_default()),
            start_time_formatted,
            end_time_formatted,
            time_display,
            location: event.location.map(|loc| crate::html_parser::strip_html_tags(&loc)),
            is_multi_day,
            is_grouped: false,
            group_sessions: Vec::new(),
        }
    }
    
    pub fn from_grouped_events(series_name: &str, events: Vec<Event>) -> Self {
        let mut group_sessions = Vec::new();
        let description = events.first()
            .and_then(|e| e.description.as_ref())
            .map(|d| crate::html_parser::strip_html_tags(d))
            .unwrap_or_default();
        let location = events.first()
            .and_then(|e| e.location.as_ref())
            .map(|l| crate::html_parser::strip_html_tags(l));
        
        for event in events {
            let date = if let Some(start_dt) = &event.start_datetime {
                start_dt.format("%A, %B %d, %Y").to_string()
            } else {
                event.date.format("%A, %B %d, %Y").to_string()
            };
            
            let time = if let Some(start_dt) = &event.start_datetime {
                if let Some(end_dt) = &event.end_datetime {
                    if start_dt.date_naive() == end_dt.date_naive() {
                        format!("{} - {}", 
                            start_dt.format("%I:%M %p"), 
                            end_dt.format("%I:%M %p"))
                    } else {
                        start_dt.format("%I:%M %p").to_string()
                    }
                } else {
                    start_dt.format("%I:%M %p").to_string()
                }
            } else {
                event.time.clone()
            };
            
            group_sessions.push(GroupSession {
                title: crate::html_parser::strip_html_tags(&event.name),
                date,
                time,
                location: event.location.map(|loc| crate::html_parser::strip_html_tags(&loc)),
            });
        }
        
        Self {
            title: series_name.to_string(),
            description,
            start_time_formatted: None,
            end_time_formatted: None,
            time_display: Some("Multiple Sessions".to_string()),
            location,
            is_multi_day: true,
            is_grouped: true,
            group_sessions,
        }
    }
    
    fn format_event_times(event: &Event) -> (Option<String>, Option<String>, Option<String>, bool) {
        if let (Some(start_dt), Some(end_dt)) = (&event.start_datetime, &event.end_datetime) {
            let start_formatted = start_dt.format("%A, %B %d, %Y at %I:%M %p").to_string();
            let end_formatted = end_dt.format("%A, %B %d, %Y at %I:%M %p").to_string();
            
            // Check if it's a multi-day event
            let is_multi_day = start_dt.date_naive() != end_dt.date_naive();
            
            let time_display = if is_multi_day {
                Some(format!("{} to {}", start_formatted, end_formatted))
            } else {
                // Same day event
                let date_str = start_dt.format("%A, %B %d, %Y").to_string();
                let start_time = start_dt.format("%I:%M %p").to_string();
                let end_time = end_dt.format("%I:%M %p").to_string();
                Some(format!("{} from {} to {}", date_str, start_time, end_time))
            };
            
            (Some(start_formatted), Some(end_formatted), time_display, is_multi_day)
        } else {
            // Fallback to old format if datetime fields are missing
            let start_time_formatted = if let Some(start_dt) = &event.start_datetime {
                Some(start_dt.format("%A, %B %d, %Y at %I:%M %p").to_string())
            } else {
                // Use date and time fields as fallback
                Some(format!("{} at {}", event.date.format("%A, %B %d, %Y"), event.time))
            };
            
            (start_time_formatted.clone(), None, start_time_formatted, false)
        }
    }
}

pub fn render_template(template_dir: &Path, context: &TemplateContext) -> Result<String> {
    let mut tera = Tera::new(&format!("{}/**/*", template_dir.display()))?;
    
    tera.register_filter("lower", |value: &tera::Value, _: &std::collections::HashMap<String, tera::Value>| {
        match value {
            tera::Value::String(s) => Ok(tera::Value::String(s.to_lowercase())),
            _ => Ok(value.clone()),
        }
    });
    
    tera.register_filter("replace", |value: &tera::Value, args: &std::collections::HashMap<String, tera::Value>| {
        match value {
            tera::Value::String(s) => {
                let from = args.get("from").and_then(|v| v.as_str()).unwrap_or("");
                let to = args.get("to").and_then(|v| v.as_str()).unwrap_or("");
                Ok(tera::Value::String(s.replace(from, to)))
            }
            _ => Ok(value.clone()),
        }
    });
    
    let mut tera_context = Context::new();
    tera_context.insert("bulletin_date", &context.bulletin_date);
    tera_context.insert("bulletin_theme_title", &context.bulletin_theme_title);
    tera_context.insert("church_name", &context.church_name);
    tera_context.insert("cover_image_path", &context.cover_image_path);
    tera_context.insert("sabbath_school_items", &context.sabbath_school_items);
    tera_context.insert("divine_worship_items", &context.divine_worship_items);
    tera_context.insert("announcements", &context.announcements);
    tera_context.insert("sunset_times", &context.sunset_times);
    tera_context.insert("contact_info", &context.contact_info);
    
    // Calculate dynamic font size based on content amount
    let dynamic_font_size = calculate_dynamic_font_size(&context.announcements);
    tera_context.insert("dynamic_announcement_font_size", &dynamic_font_size);
    
    let html = tera.render("bulletin_template.html", &tera_context)?;
    Ok(html)
}

pub fn group_similar_events(events: Vec<Event>) -> Vec<AnnouncementItem> {
    if events.is_empty() {
        return Vec::new();
    }
    
    let mut event_groups: HashMap<String, Vec<Event>> = HashMap::new();
    let mut ungrouped_events = Vec::new();
    
    // Compile regex patterns for series detection
    let patterns = vec![
        // "Series Name Part X" or "Series Name Part X:"
        Regex::new(r"^(.*?)(?:\s+Part\s+\d+|:\s*Part\s+\d+)(?:\s*:.*)?$").unwrap(),
        // "Series Name Session X" or "Series Name Episode X"
        Regex::new(r"^(.*?)(?:\s+(?:Session|Episode)\s+\d+)(?:\s*:.*)?$").unwrap(),
        // "Series Name - Part X" or "Series Name: Part X"
        Regex::new(r"^(.*?)(?:\s*[-:]\s*Part\s+\d+)(?:\s*:.*)?$").unwrap(),
    ];
    
    for event in events {
        let title = event.name.trim();
        if title.is_empty() {
            ungrouped_events.push(event);
            continue;
        }
        
        let mut series_name = None;
        for pattern in &patterns {
            if let Some(captures) = pattern.captures(title) {
                if let Some(capture) = captures.get(1) {
                    let potential_series = capture.as_str().trim();
                    if potential_series.len() > 2 {
                        series_name = Some(potential_series.to_string());
                        break;
                    }
                }
            }
        }
        
        if let Some(series) = series_name {
            let series_key = series.to_lowercase();
            event_groups.entry(series_key).or_insert_with(Vec::new).push(event);
        } else {
            ungrouped_events.push(event);
        }
    }
    
    let mut result = Vec::new();
    
    // Process grouped events
    for (series_key, mut group_events) in event_groups {
        if group_events.len() > 1 {
            // Sort events by start time
            group_events.sort_by(|a, b| {
                match (&a.start_datetime, &b.start_datetime) {
                    (Some(a_dt), Some(b_dt)) => a_dt.cmp(b_dt),
                    (Some(_), None) => std::cmp::Ordering::Less,
                    (None, Some(_)) => std::cmp::Ordering::Greater,
                    (None, None) => a.date.cmp(&b.date),
                }
            });
            
            // Use the first event's series name extraction for display
            let display_name = if let Some(first_event) = group_events.first() {
                // Extract the clean series name from the first event
                for pattern in &patterns {
                    if let Some(captures) = pattern.captures(&first_event.name) {
                        if let Some(capture) = captures.get(1) {
                            let clean_name = capture.as_str().trim();
                            if clean_name.len() > 2 {
                                break;
                            }
                        }
                    }
                }
                // Fallback to the series_key if extraction fails
                series_key.split_whitespace()
                    .map(|word| {
                        let mut chars = word.chars();
                        match chars.next() {
                            None => String::new(),
                            Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
                        }
                    })
                    .collect::<Vec<_>>()
                    .join(" ")
            } else {
                series_key
            };
            
            let grouped_item = AnnouncementItem::from_grouped_events(&display_name, group_events);
            result.push(grouped_item);
        } else {
            // Single event, add to ungrouped
            ungrouped_events.extend(group_events);
        }
    }
    
    // Add ungrouped events
    for event in ungrouped_events {
        result.push(AnnouncementItem::from_event(event));
    }
    
    // Sort all items by their start time
    result.sort_by(|a, b| {
        match (&a.start_time_formatted, &b.start_time_formatted) {
            (Some(a_time), Some(b_time)) => a_time.cmp(b_time),
            (Some(_), None) => std::cmp::Ordering::Less,
            (None, Some(_)) => std::cmp::Ordering::Greater,
            (None, None) => std::cmp::Ordering::Equal,
        }
    });
    
    result
}

fn calculate_dynamic_font_size(announcements: &[AnnouncementItem]) -> String {
    // Estimate content density based on announcement count and complexity
    let mut content_score = 0.0;
    
    for announcement in announcements {
        // Base score per announcement
        content_score += 1.0;
        
        // Add score for grouped events (they take more space)
        if announcement.is_grouped {
            content_score += announcement.group_sessions.len() as f64 * 0.5;
        }
        
        // Add score for long descriptions
        if announcement.description.len() > 100 {
            content_score += 0.5;
        }
        
        // Add score for location info
        if announcement.location.is_some() {
            content_score += 0.3;
        }
        
        // Add score for time display
        if announcement.time_display.is_some() {
            content_score += 0.2;
        }
    }
    
    // Calculate font size based on content score
    // Target: fit roughly 12-15 announcements comfortably
    let font_size = if content_score <= 8.0 {
        0.85 // Larger font for few announcements
    } else if content_score <= 12.0 {
        0.75 // Standard font
    } else if content_score <= 16.0 {
        0.65 // Smaller font for more content
    } else if content_score <= 20.0 {
        0.58 // Much smaller font
    } else {
        0.52 // Ultra-compact for lots of content
    };
    
    format!("{}em", font_size)
}