use adw::prelude::*;
use chrono::Datelike;
use outlay_core::db::Database;
use std::cell::{Cell, RefCell};
use std::rc::Rc;

pub struct InsightsView {
    pub container: gtk::Box,
    db: Rc<Database>,
    streaks_list: gtk::ListBox,
    achievements_box: gtk::Box,
    recap_list: gtk::ListBox,
    anomalies_list: gtk::ListBox,
    recap_mode: Rc<Cell<bool>>, // false = monthly, true = yearly
    year: Rc<Cell<i32>>,
    month: Rc<Cell<u32>>,
    on_navigate_category: Rc<RefCell<Option<Rc<dyn Fn(i64)>>>>,
}

impl InsightsView {
    pub fn new(db: Rc<Database>) -> Self {
        let today = chrono::Local::now().date_naive();
        let year = Rc::new(Cell::new(today.year()));
        let month = Rc::new(Cell::new(today.month()));
        let recap_mode = Rc::new(Cell::new(false));

        let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
        container.set_margin_start(16);
        container.set_margin_end(16);
        container.set_margin_top(16);
        container.set_margin_bottom(16);

        let scroll = gtk::ScrolledWindow::builder()
            .hscrollbar_policy(gtk::PolicyType::Never)
            .vexpand(true)
            .build();

        let inner = gtk::Box::new(gtk::Orientation::Vertical, 20);
        inner.set_margin_start(8);
        inner.set_margin_end(8);
        inner.set_margin_top(8);
        inner.set_margin_bottom(8);

        // -- Streaks section --
        let streaks_group = adw::PreferencesGroup::builder()
            .title("Streaks")
            .build();
        let streaks_list = gtk::ListBox::new();
        streaks_list.set_selection_mode(gtk::SelectionMode::None);
        streaks_list.add_css_class("boxed-list");
        streaks_group.add(&streaks_list);
        inner.append(&streaks_group);

        // -- Achievements section --
        let achievements_group = adw::PreferencesGroup::builder()
            .title("Achievements")
            .build();
        let achievements_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
        achievements_group.add(&achievements_box);
        inner.append(&achievements_group);

        // -- Monthly/Yearly Recap section --
        let recap_group = adw::PreferencesGroup::builder()
            .title("Recap")
            .build();

        let toggle_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        toggle_box.add_css_class("linked");
        toggle_box.set_halign(gtk::Align::Center);
        toggle_box.set_margin_bottom(8);

        let month_btn = gtk::ToggleButton::with_label("This Month");
        month_btn.set_active(true);
        let year_btn = gtk::ToggleButton::with_label("This Year");
        year_btn.set_group(Some(&month_btn));
        toggle_box.append(&month_btn);
        toggle_box.append(&year_btn);
        recap_group.set_header_suffix(Some(&toggle_box));

        let recap_list = gtk::ListBox::new();
        recap_list.set_selection_mode(gtk::SelectionMode::None);
        recap_list.add_css_class("boxed-list");
        recap_group.add(&recap_list);

        inner.append(&recap_group);

        // -- Anomalies section --
        let anomalies_group = adw::PreferencesGroup::builder()
            .title("Spending Insights")
            .build();
        let anomalies_list = gtk::ListBox::new();
        anomalies_list.set_selection_mode(gtk::SelectionMode::None);
        anomalies_list.add_css_class("boxed-list");
        anomalies_group.add(&anomalies_list);
        inner.append(&anomalies_group);

        scroll.set_child(Some(&inner));
        container.append(&scroll);

        let on_navigate_category: Rc<RefCell<Option<Rc<dyn Fn(i64)>>>> = Rc::new(RefCell::new(None));

        let view = InsightsView {
            container,
            db,
            streaks_list,
            achievements_box,
            recap_list,
            anomalies_list,
            recap_mode,
            year,
            month,
            on_navigate_category,
        };

        // Wire toggle
        {
            let mode = view.recap_mode.clone();
            let db_ref = view.db.clone();
            let list_ref = view.recap_list.clone();
            let y = view.year.clone();
            let m = view.month.clone();
            month_btn.connect_toggled(move |btn| {
                if btn.is_active() {
                    mode.set(false);
                    Self::populate_recap_static(&db_ref, &list_ref, false, y.get(), m.get());
                }
            });
        }
        {
            let mode = view.recap_mode.clone();
            let db_ref = view.db.clone();
            let list_ref = view.recap_list.clone();
            let y = view.year.clone();
            let m = view.month.clone();
            year_btn.connect_toggled(move |btn| {
                if btn.is_active() {
                    mode.set(true);
                    Self::populate_recap_static(&db_ref, &list_ref, true, y.get(), m.get());
                }
            });
        }

        view.refresh();
        view
    }

    pub fn refresh(&self) {
        let today = chrono::Local::now().date_naive();
        self.year.set(today.year());
        self.month.set(today.month());

        self.populate_streaks();
        self.populate_achievements();
        Self::populate_recap_static(&self.db, &self.recap_list, self.recap_mode.get(), today.year(), today.month());
        self.populate_anomalies();
    }

    pub fn set_on_navigate_category(&self, cb: Rc<dyn Fn(i64)>) {
        *self.on_navigate_category.borrow_mut() = Some(cb);
    }

    fn clear_list(list: &gtk::ListBox) {
        while let Some(child) = list.first_child() {
            list.remove(&child);
        }
    }

    fn clear_box(bx: &gtk::Box) {
        while let Some(child) = bx.first_child() {
            bx.remove(&child);
        }
    }

    fn populate_streaks(&self) {
        Self::clear_list(&self.streaks_list);

        let today = chrono::Local::now().date_naive();

        // No-spend streak
        let streak = self.db.get_no_spend_streak(today).unwrap_or(0);
        let streak_row = adw::ActionRow::builder()
            .title(&format!("{} day{}", streak, if streak == 1 { "" } else { "s" }))
            .subtitle("No-Spend Streak")
            .build();
        let icon = gtk::Image::from_icon_name("tabler-flame");
        icon.set_pixel_size(24);
        streak_row.add_prefix(&icon);
        self.streaks_list.append(&streak_row);

        // Under-budget streak (months in a row where total expense < total budget)
        let budget_streak = self.count_under_budget_months(today);
        let budget_row = adw::ActionRow::builder()
            .title(&format!("{} month{}", budget_streak, if budget_streak == 1 { "" } else { "s" }))
            .subtitle("Under Budget")
            .build();
        let icon = gtk::Image::from_icon_name("tabler-shield-check");
        icon.set_pixel_size(24);
        budget_row.add_prefix(&icon);
        self.streaks_list.append(&budget_row);

        // Savings streak (months with positive net)
        let savings_streak = self.count_positive_net_months(today);
        let savings_row = adw::ActionRow::builder()
            .title(&format!("{} month{}", savings_streak, if savings_streak == 1 { "" } else { "s" }))
            .subtitle("Positive Savings")
            .build();
        let icon = gtk::Image::from_icon_name("tabler-trending-up");
        icon.set_pixel_size(24);
        savings_row.add_prefix(&icon);
        self.streaks_list.append(&savings_row);
    }

    fn count_under_budget_months(&self, today: chrono::NaiveDate) -> i32 {
        let mut streak = 0;
        let mut y = today.year();
        let mut m = today.month();
        // Check up to 24 months back
        for _ in 0..24 {
            let progress = self.db.get_all_budget_progress(y, m).unwrap_or_default();
            if progress.is_empty() { break; }
            let all_under = progress.iter().all(|(_, spent, budget, _, _)| *spent <= *budget);
            if all_under {
                streak += 1;
            } else {
                break;
            }
            // Previous month
            if m == 1 { y -= 1; m = 12; } else { m -= 1; }
        }
        streak
    }

    fn count_positive_net_months(&self, today: chrono::NaiveDate) -> i32 {
        use outlay_core::models::TransactionType;
        let mut streak = 0;
        let mut y = today.year();
        let mut m = today.month();
        for _ in 0..24 {
            let income = self.db.get_monthly_total(y, m, TransactionType::Income).unwrap_or(0.0);
            let expense = self.db.get_monthly_total(y, m, TransactionType::Expense).unwrap_or(0.0);
            if income <= 0.0 && expense <= 0.0 { break; }
            if income >= expense {
                streak += 1;
            } else {
                break;
            }
            if m == 1 { y -= 1; m = 12; } else { m -= 1; }
        }
        streak
    }

    fn populate_achievements(&self) {
        Self::clear_box(&self.achievements_box);

        let achievements = self.db.list_achievements().unwrap_or_default();
        if achievements.is_empty() {
            let label = gtk::Label::new(Some("No achievements yet - keep tracking!"));
            label.add_css_class("dim-label");
            label.set_margin_top(8);
            label.set_margin_bottom(8);
            self.achievements_box.append(&label);
            return;
        }

        let flow = gtk::FlowBox::new();
        flow.set_selection_mode(gtk::SelectionMode::None);
        flow.set_max_children_per_line(4);
        flow.set_min_children_per_line(2);
        flow.set_column_spacing(8);
        flow.set_row_spacing(8);

        for ach in &achievements {
            let card = gtk::Box::new(gtk::Orientation::Vertical, 4);
            card.set_halign(gtk::Align::Center);
            card.set_margin_top(8);
            card.set_margin_bottom(8);
            card.set_margin_start(4);
            card.set_margin_end(4);

            let icon_name = Self::achievement_icon(&ach.name);
            let icon = gtk::Image::from_icon_name(icon_name);
            icon.set_pixel_size(32);

            if ach.earned_at.is_none() {
                icon.set_opacity(0.75);
            }

            let name_label = gtk::Label::new(Some(&ach.name));
            name_label.set_wrap(true);
            name_label.set_max_width_chars(12);
            name_label.set_justify(gtk::Justification::Center);
            if ach.earned_at.is_some() {
                name_label.add_css_class("heading");
            } else {
                name_label.add_css_class("dim-label");
            }

            card.append(&icon);
            card.append(&name_label);

            if !ach.description.is_empty() {
                let desc_label = gtk::Label::new(Some(&ach.description));
                desc_label.add_css_class("caption");
                desc_label.add_css_class("dim-label");
                desc_label.set_wrap(true);
                desc_label.set_max_width_chars(14);
                desc_label.set_justify(gtk::Justification::Center);
                card.append(&desc_label);
            }

            if let Some(ref date) = ach.earned_at {
                let short = date.split(' ').next().unwrap_or(date);
                let date_label = gtk::Label::new(Some(short));
                date_label.add_css_class("caption");
                date_label.add_css_class("success");
                card.append(&date_label);
            }

            flow.append(&card);
        }

        self.achievements_box.append(&flow);
    }

    fn populate_recap_static(db: &Database, list: &gtk::ListBox, yearly: bool, year: i32, month: u32) {
        Self::clear_list(list);

        if yearly {
            // Yearly view: month-by-month summary
            let summaries = db.get_yearly_month_summaries(year).unwrap_or_default();

            // Year totals header
            let total_income: f64 = summaries.iter().map(|(_, i, _)| i).sum();
            let total_expense: f64 = summaries.iter().map(|(_, _, e)| e).sum();
            let net = total_income - total_expense;

            let header = adw::ActionRow::builder()
                .title(&format!("{} Summary", year))
                .subtitle(&format!(
                    "Income: {:.2} | Expenses: {:.2} | Net: {:.2}",
                    total_income, total_expense, net
                ))
                .build();
            header.add_css_class("property");
            list.append(&header);

            for (label, income, expense) in &summaries {
                if *income <= 0.0 && *expense <= 0.0 { continue; }
                let net = income - expense;
                let row = adw::ActionRow::builder()
                    .title(label)
                    .subtitle(&format!(
                        "In: {:.2}  Out: {:.2}  Net: {:.2}",
                        income, expense, net
                    ))
                    .build();
                let net_label = gtk::Label::new(Some(&format!("{:+.2}", net)));
                if net >= 0.0 {
                    net_label.add_css_class("success");
                } else {
                    net_label.add_css_class("error");
                }
                row.add_suffix(&net_label);
                list.append(&row);
            }
        } else {
            // Monthly recap with category breakdown
            let recap = match db.get_monthly_recap(year, month) {
                Ok(r) => r,
                Err(_) => return,
            };

            // Summary header
            let header = adw::ActionRow::builder()
                .title(&format!("{:04}-{:02} Summary", year, month))
                .subtitle(&format!(
                    "Income: {:.2} | Expenses: {:.2} | Net: {:.2} | {} transactions",
                    recap.total_income, recap.total_expenses, recap.net, recap.transaction_count
                ))
                .build();
            header.add_css_class("property");
            list.append(&header);

            // Category breakdown
            for cat in &recap.categories {
                let icon_name = cat.category_icon.as_deref().unwrap_or("folder-symbolic");
                let row = adw::ActionRow::builder()
                    .title(&cat.category_name)
                    .subtitle(&format!("{:.2} ({:.1}%)", cat.amount, cat.percentage))
                    .build();

                let icon = gtk::Image::from_icon_name(icon_name);
                icon.set_pixel_size(20);
                row.add_prefix(&icon);

                // Change badge
                if let Some(change) = cat.change_pct {
                    let badge_text = if change >= 0.0 {
                        format!("+{:.0}%", change)
                    } else {
                        format!("{:.0}%", change)
                    };
                    let badge = gtk::Label::new(Some(&badge_text));
                    badge.add_css_class("caption");
                    if change > 10.0 {
                        badge.add_css_class("error");
                    } else if change < -10.0 {
                        badge.add_css_class("success");
                    } else {
                        badge.add_css_class("dim-label");
                    }
                    row.add_suffix(&badge);
                } else {
                    let badge = gtk::Label::new(Some("new"));
                    badge.add_css_class("caption");
                    badge.add_css_class("dim-label");
                    row.add_suffix(&badge);
                }

                list.append(&row);
            }
        }
    }

    fn populate_anomalies(&self) {
        Self::clear_list(&self.anomalies_list);

        let today = chrono::Local::now().date_naive();
        let anomalies = self.db.detect_anomalies(today.year(), today.month());

        if anomalies.is_empty() {
            let row = adw::ActionRow::builder()
                .title("No spending anomalies this month")
                .build();
            row.add_css_class("dim-label");
            self.anomalies_list.append(&row);
            return;
        }

        for (message, deviation, cat_id) in &anomalies {
            let row = adw::ActionRow::builder()
                .title(message)
                .subtitle(&format!("Deviation: {:.2}", deviation))
                .activatable(cat_id.is_some())
                .build();

            let icon = if *deviation > 0.0 {
                let img = gtk::Image::from_icon_name("tabler-alert-triangle");
                img
            } else {
                gtk::Image::from_icon_name("tabler-info-circle")
            };
            icon.set_pixel_size(20);
            row.add_prefix(&icon);

            if let Some(cid) = cat_id {
                let nav = self.on_navigate_category.clone();
                let cid = *cid;
                row.connect_activated(move |_| {
                    if let Some(ref cb) = *nav.borrow() {
                        cb(cid);
                    }
                });
            }

            self.anomalies_list.append(&row);
        }
    }

    /// Map achievement names to unique tabler icon names.
    fn achievement_icon(name: &str) -> &'static str {
        match name {
            "First Transaction" => "tabler-sparkles",
            "7-Day No-Spend" => "tabler-calendar-check",
            "30-Day No-Spend" => "tabler-hourglass-high",
            "Month Under Budget" => "tabler-shield-check",
            "3 Months Under Budget" => "tabler-shield-star",
            "First Goal Completed" => "tabler-target-arrow",
            "100 Transactions" => "tabler-list-check",
            "Budget Streak 6mo" => "tabler-crown",
            "500 Transactions" => "tabler-receipt",
            "1000 Transactions" => "tabler-diamond",
            "First Recurring" => "tabler-clock-check",
            "5 Goals Completed" => "tabler-trophy",
            "Year Under Budget" => "tabler-rosette-discount-check",
            "Big Saver" => "tabler-moneybag",
            "Category Master" => "tabler-chart-arrows-vertical",
            "Streak Champion" => "tabler-rocket",
            _ => "tabler-award",
        }
    }
}
