use anyhow::Result;
use colored::Colorize;

use crate::api::{PlaneApiClient, PlaneState, PlaneWorkItem, ScheduledDate, StateGroup};
use crate::config::{ProjectScope, resolve_plane_session};

/// Renders a terminal dashboard showing work items grouped by state, overdue counts,
/// in-progress items, and acceptance criteria completion rates across the active project.
#[derive(clap::Args)]
pub struct DashboardOverviewArgs {
    /// Project slug to display (overrides the configured default)
    #[arg(long)]
    project: Option<String>,
}

/// Execution logic for the `plane dashboard` subcommand
impl DashboardOverviewArgs {
    /// Aggregates work item data from the Plane API and renders
    /// a colored multi-section overview in the terminal
    pub async fn execute(self) -> Result<()> {
        let plane_session = resolve_plane_session()?;
        let api_client = PlaneApiClient::try_new(plane_session)?;

        let target_project_slug = match self.project {
            Option::Some(ref explicit_project) => explicit_project.parse()?,
            Option::None => match api_client.project_scope() {
                ProjectScope::Scoped(default_project) => default_project.as_str().parse()?,
                ProjectScope::ExplicitOnly => {
                    anyhow::bail!(
                        "no default project configured — pass --project or run: \
                         plane config --project <slug>"
                    );
                }
            },
        };

        let resolved_project = api_client
            .find_project_by_slug(&target_project_slug)
            .await?;

        let project_states = api_client.list_states(resolved_project.id()).await?;
        let all_work_items = api_client.list_work_items(resolved_project.id()).await?;

        let today_date = chrono::Local::now().format("%Y-%m-%d").to_string();

        let dashboard_renderer = DashboardRenderer {
            project_states: &project_states,
            work_items: &all_work_items,
            project_display_name: resolved_project.display_name(),
            today_date: &today_date,
        };

        dashboard_renderer.render();

        Ok(())
    }
}

/// Holds the fetched data needed to compute and render dashboard sections
struct DashboardRenderer<'a> {
    project_states: &'a [PlaneState],
    work_items: &'a [PlaneWorkItem],
    project_display_name: &'a str,
    today_date: &'a str,
}

/// Rendering logic that computes state distribution, overdue counts,
/// and displays a colored terminal overview
impl<'a> DashboardRenderer<'a> {
    fn render(&self) {
        println!(
            "\n{}  {}\n",
            "Dashboard:".bold(),
            self.project_display_name
        );

        self.render_state_distribution();
        self.render_overdue_section();
        self.render_summary_line();

        println!();
    }

    fn render_state_distribution(&self) {
        println!("{}", "Items by State".underline());

        let total_items = self.work_items.len();

        for candidate_state in self.project_states {
            let items_in_state = self.count_items_in_state(candidate_state);
            if items_in_state == 0 {
                continue;
            }

            let state_label = colorize_state_label(candidate_state);
            let bar_segment = self.render_progress_bar(items_in_state);

            println!("  {:>16}  {} {}", state_label, bar_segment, items_in_state);
        }

        println!("  {:>16}  {}", "Total".bold(), total_items);
        println!();
    }

    fn render_overdue_section(&self) {
        let mut overdue_count: usize = 0;

        for work_item in self.work_items {
            if self.is_work_item_overdue(work_item) {
                overdue_count += 1;
            }
        }

        if overdue_count > 0 {
            println!(
                "{}  {} items past target date",
                "Overdue:".red().bold(),
                overdue_count
            );
            println!();
        }
    }

    fn render_summary_line(&self) {
        let total_items = self.work_items.len();
        let completed_items = self.count_items_in_group(StateGroup::Completed);
        let in_progress_items = self.count_items_in_group(StateGroup::Started);
        let cancelled_items = self.count_items_in_group(StateGroup::Cancelled);

        let active_items = total_items.saturating_sub(cancelled_items);
        let completion_percentage = match active_items {
            0 => 0,
            denominator => (completed_items * 100) / denominator,
        };

        println!(
            "In Progress: {}  |  Done: {} ({}%)",
            in_progress_items, completed_items, completion_percentage
        );
    }

    fn count_items_in_state(&self, target_state: &PlaneState) -> usize {
        self.work_items
            .iter()
            .filter(|work_item| work_item.workflow_state_id() == target_state.id())
            .count()
    }

    fn count_items_in_group(&self, target_group: StateGroup) -> usize {
        self.work_items
            .iter()
            .filter(|work_item| {
                self.resolve_state_group(work_item) == Option::Some(target_group)
            })
            .count()
    }

    fn resolve_state_group(&self, target_work_item: &PlaneWorkItem) -> Option<StateGroup> {
        for candidate_state in self.project_states {
            if candidate_state.id() == target_work_item.workflow_state_id() {
                return Option::Some(candidate_state.state_group());
            }
        }
        Option::None
    }

    fn is_work_item_overdue(&self, work_item: &PlaneWorkItem) -> bool {
        let target_date_text = match work_item.target_date() {
            ScheduledDate::Scheduled(date_text) => date_text.as_str(),
            ScheduledDate::Unscheduled => return false,
        };

        if target_date_text >= self.today_date {
            return false;
        }

        match self.resolve_state_group(work_item) {
            Option::Some(StateGroup::Completed) | Option::Some(StateGroup::Cancelled) => false,
            _ => true,
        }
    }

    fn render_progress_bar(&self, item_count: usize) -> String {
        let bar_width: usize = 20;
        let total_count = self.work_items.len();
        if total_count == 0 {
            return " ".repeat(bar_width);
        }

        let filled_width = (item_count * bar_width) / total_count;
        let empty_width = bar_width.saturating_sub(filled_width);

        let mut bar_output = String::with_capacity(bar_width + 2);
        bar_output.push('[');
        for _ in 0..filled_width {
            bar_output.push('#');
        }
        for _ in 0..empty_width {
            bar_output.push(' ');
        }
        bar_output.push(']');
        bar_output
    }
}

fn colorize_state_label(resolved_state: &PlaneState) -> String {
    match resolved_state.state_group() {
        StateGroup::Backlog => resolved_state.display_name().dimmed().to_string(),
        StateGroup::Unstarted => resolved_state.display_name().white().to_string(),
        StateGroup::Started => resolved_state.display_name().yellow().to_string(),
        StateGroup::Completed => resolved_state.display_name().green().to_string(),
        StateGroup::Cancelled => resolved_state.display_name().red().to_string(),
    }
}
