use anyhow::Result;
use colored::Colorize;
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Cell, ContentArrangement, Table};

use crate::api::{
    CycleOwnership, MemberId, ModuleLeadership, ModuleWorkflowPhase, PlaneApiClient, PlaneCycle,
    PlaneModule, PlaneState, PlaneWorkItem, ProjectId, ScheduledDate, StateGroup,
    WorkItemPriority,
};
use crate::config::{ProjectScope, resolve_plane_session};
use crate::terminal_ui::OutputFormat;

/// Which entity types to include in `plane mine` output
#[derive(Debug, Clone)]
pub enum MineSectionFilter {
    /// Show all three sections: work items, modules, cycles
    AllSections,
    /// Show only work items assigned to the user
    WorkItemsSection,
    /// Show only modules the user leads or belongs to
    ModulesSection,
    /// Show only cycles the user owns
    CyclesSection,
}

/// Parses the optional scope argument into a typed section filter
fn parse_section_filter(scope_argument: &Option<String>) -> Result<MineSectionFilter> {
    match scope_argument {
        Option::None => Ok(MineSectionFilter::AllSections),
        Option::Some(scope_text) => match scope_text.to_lowercase().as_str() {
            "items" | "work-items" => Ok(MineSectionFilter::WorkItemsSection),
            "modules" => Ok(MineSectionFilter::ModulesSection),
            "cycles" => Ok(MineSectionFilter::CyclesSection),
            unrecognized => anyhow::bail!(
                "unknown scope '{}' — use: items, modules, or cycles",
                unrecognized
            ),
        },
    }
}

/// Lists work items, modules, and cycles assigned to the current user.
/// Fetches the authenticated user's member ID, then filters across entity types
/// to show only items where the user is assigned, leads, or owns.
///
/// Usage: `plane mine` (all), `plane mine items`, `plane mine modules`, `plane mine cycles`
#[derive(clap::Args)]
pub struct MyWorkItemsArgs {
    /// Scope: items, modules, or cycles (omit for all)
    scope: Option<String>,
    /// Output format: table (default for TTY), json (default for pipes), csv
    #[arg(long)]
    format: Option<String>,
    /// Filter work items by state name (case-insensitive, e.g. "todo", "in progress")
    #[arg(long)]
    state: Option<String>,
    /// Filter work items by priority: urgent, high, medium, low, none
    #[arg(long)]
    priority: Option<String>,
    /// Filter work items within a specific module (by name)
    #[arg(long, name = "module")]
    module_filter: Option<String>,
    /// Filter work items within a specific cycle (by name)
    #[arg(long, name = "cycle")]
    cycle_filter: Option<String>,
}

/// Execution logic for the `plane mine` subcommand
impl MyWorkItemsArgs {
    /// Fetches the current user's member ID, resolves the target project,
    /// and collects work items, modules, and cycles assigned to the user
    pub async fn execute(self) -> Result<()> {
        let section_filter = parse_section_filter(&self.scope)?;

        let plane_session = resolve_plane_session()?;
        let api_client = PlaneApiClient::try_new(plane_session)?;
        let current_member_id = api_client.current_user_member_id().await?;

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

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

        let selected_format = match &self.format {
            Option::Some(format_text) => format_text.parse()?,
            Option::None => OutputFormat::detect_from_terminal(),
        };

        let mine_renderer = MineRenderer {
            api_client: &api_client,
            project_id: resolved_project.id(),
            project_identifier_code: resolved_project.identifier(),
            current_member_id: &current_member_id,
            selected_format: &selected_format,
        };

        match section_filter {
            MineSectionFilter::AllSections => {
                mine_renderer.render_work_items(&self).await?;
                println!();
                mine_renderer.render_modules().await?;
                println!();
                mine_renderer.render_cycles().await?;
            }
            MineSectionFilter::WorkItemsSection => {
                mine_renderer.render_work_items(&self).await?;
            }
            MineSectionFilter::ModulesSection => {
                mine_renderer.render_modules().await?;
            }
            MineSectionFilter::CyclesSection => {
                mine_renderer.render_cycles().await?;
            }
        }

        Ok(())
    }
}

/// Holds all dependencies needed to render sections of the `plane mine` output.
/// Methods on this struct satisfy the single-parameter rule via `&self`.
struct MineRenderer<'a> {
    api_client: &'a PlaneApiClient,
    project_id: &'a ProjectId,
    project_identifier_code: &'a str,
    current_member_id: &'a MemberId,
    selected_format: &'a OutputFormat,
}

/// All data needed to render a list of work items in any output format
struct WorkItemDisplayInput<'a> {
    work_items: &'a [&'a PlaneWorkItem],
    project_states: &'a [PlaneState],
    identifier_code: &'a str,
}

/// Resolves a work item's state UUID against a list of project states.
/// Used by display methods to convert raw UUIDs into human-readable names.
impl<'a> WorkItemDisplayInput<'a> {
    /// Returns a colorized state name for terminal table output
    fn resolve_state_display(&self, target_work_item: &PlaneWorkItem) -> String {
        for candidate_state in self.project_states {
            if candidate_state.id() == target_work_item.workflow_state_id() {
                return colorize_state_display(candidate_state);
            }
        }
        target_work_item.workflow_state_id().to_string()
    }

    /// Returns a plain text state name for JSON/CSV output
    fn find_state_name(&self, target_work_item: &PlaneWorkItem) -> String {
        for candidate_state in self.project_states {
            if candidate_state.id() == target_work_item.workflow_state_id() {
                return candidate_state.display_name().to_owned();
            }
        }
        target_work_item.workflow_state_id().to_string()
    }
}

/// Section rendering methods for the `plane mine` subcommand
impl<'a> MineRenderer<'a> {
    /// Fetches work items assigned to the current user, applies optional filters
    /// (state, priority, module, cycle), and renders in the selected format
    async fn render_work_items(&self, filter_args: &MyWorkItemsArgs) -> Result<()> {
        let project_states = self.api_client.list_states(self.project_id).await?;
        let all_work_items = self.api_client.list_work_items(self.project_id).await?;

        let mut my_items: Vec<&PlaneWorkItem> = all_work_items
            .iter()
            .filter(|work_item| {
                work_item
                    .assignee_ids()
                    .iter()
                    .any(|assignee_id| assignee_id == self.current_member_id)
            })
            .collect();

        if let Option::Some(ref state_filter) = filter_args.state {
            my_items.retain(|work_item| {
                project_states.iter().any(|candidate_state| {
                    candidate_state.id() == work_item.workflow_state_id()
                        && candidate_state
                            .display_name()
                            .eq_ignore_ascii_case(state_filter)
                })
            });
        }

        if let Option::Some(ref priority_filter) = filter_args.priority {
            my_items.retain(|work_item| {
                let priority_text = format!("{:?}", work_item.priority());
                priority_text.eq_ignore_ascii_case(priority_filter)
            });
        }

        if let Option::Some(ref module_name) = filter_args.module_filter {
            let project_modules = self.api_client.list_modules(self.project_id).await?;
            let mut module_item_ids: Vec<crate::api::WorkItemId> = Vec::new();

            for candidate_module in &project_modules {
                if candidate_module.display_name().eq_ignore_ascii_case(module_name) {
                    let project_client = self.api_client.project_operations(self.project_id);
                    let module_client = project_client.module_operations(candidate_module.id());
                    let module_work_items = module_client.list_work_items().await?;
                    for module_item in &module_work_items {
                        module_item_ids.push(module_item.id().to_owned());
                    }
                }
            }

            my_items.retain(|work_item| {
                module_item_ids.iter().any(|mid| mid == work_item.id())
            });
        }

        if let Option::Some(ref cycle_name) = filter_args.cycle_filter {
            let project_cycles = self.api_client.list_cycles(self.project_id).await?;
            let mut cycle_item_ids: Vec<crate::api::WorkItemId> = Vec::new();

            for candidate_cycle in &project_cycles {
                if candidate_cycle.display_name().eq_ignore_ascii_case(cycle_name) {
                    let project_client = self.api_client.project_operations(self.project_id);
                    let cycle_client = project_client.cycle_operations(candidate_cycle.id());
                    let cycle_work_items = cycle_client.list_work_items().await?;
                    for cycle_item in &cycle_work_items {
                        cycle_item_ids.push(cycle_item.id().to_owned());
                    }
                }
            }

            my_items.retain(|work_item| {
                cycle_item_ids.iter().any(|cid| cid == work_item.id())
            });
        }

        let work_item_display = WorkItemDisplayInput {
            work_items: &my_items,
            project_states: &project_states,
            identifier_code: self.project_identifier_code,
        };

        match self.selected_format {
            OutputFormat::Table => {
                println!("{}", "Work Items".bold().underline());
                print_work_items_table(&work_item_display);
            }
            OutputFormat::Json => print_work_items_json(&work_item_display),
            OutputFormat::Csv => print_work_items_csv(&work_item_display),
        }

        Ok(())
    }

    /// Fetches all modules and filters to those where the current user is lead or member
    async fn render_modules(&self) -> Result<()> {
        let project_modules = self.api_client.list_modules(self.project_id).await?;

        let my_modules: Vec<&PlaneModule> = project_modules
            .iter()
            .filter(|module_entry| {
                let is_lead = match module_entry.leadership() {
                    ModuleLeadership::Led(lead_id) => lead_id == self.current_member_id,
                    ModuleLeadership::Unleaded => false,
                };
                let is_member = module_entry
                    .member_ids()
                    .iter()
                    .any(|mid| mid == self.current_member_id);
                is_lead || is_member
            })
            .collect();

        match self.selected_format {
            OutputFormat::Table => {
                println!("{}", "Modules".bold().underline());
                print_modules_table(&my_modules);
            }
            OutputFormat::Json => print_modules_json(&my_modules),
            OutputFormat::Csv => print_modules_csv(&my_modules),
        }

        Ok(())
    }

    /// Fetches all cycles and filters to those owned by the current user
    async fn render_cycles(&self) -> Result<()> {
        let project_cycles = self.api_client.list_cycles(self.project_id).await?;

        let my_cycles: Vec<&PlaneCycle> = project_cycles
            .iter()
            .filter(|cycle_entry| match cycle_entry.ownership() {
                CycleOwnership::Owned(owner_id) => owner_id == self.current_member_id,
                CycleOwnership::Unowned => false,
            })
            .collect();

        match self.selected_format {
            OutputFormat::Table => {
                println!("{}", "Cycles".bold().underline());
                print_cycles_table(&my_cycles);
            }
            OutputFormat::Json => print_cycles_json(&my_cycles),
            OutputFormat::Csv => print_cycles_csv(&my_cycles),
        }

        Ok(())
    }
}

// ── WORK ITEMS DISPLAY ───────────────────────────────────────────────

/// Renders work items into a terminal table, resolving state IDs to display names
fn print_work_items_table(display_input: &WorkItemDisplayInput<'_>) {
    if display_input.work_items.is_empty() {
        println!("No items assigned to you.");
        return;
    }

    let mut terminal_table = Table::new();
    terminal_table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec![
            Cell::new("ID"),
            Cell::new("Title"),
            Cell::new("State"),
            Cell::new("Priority"),
            Cell::new("Due"),
        ]);

    for work_item in display_input.work_items {
        let identifier_cell = format!("{}-{}", display_input.identifier_code, work_item.sequence_id().as_u32());
        let state_cell = display_input.resolve_state_display(work_item);
        let priority_cell = format_priority_display(work_item.priority());
        let due_cell = match work_item.target_date() {
            ScheduledDate::Scheduled(date_text) => date_text.to_owned(),
            ScheduledDate::Unscheduled => String::from("—"),
        };

        terminal_table.add_row(vec![
            Cell::new(identifier_cell),
            Cell::new(work_item.title().as_str()),
            Cell::new(state_cell),
            Cell::new(priority_cell),
            Cell::new(due_cell),
        ]);
    }

    println!("{}", terminal_table);
}

/// Serializes work items to JSON with resolved state names
fn print_work_items_json(display_input: &WorkItemDisplayInput<'_>) {
    let json_items: Vec<serde_json::Value> = display_input.work_items
        .iter()
        .map(|work_item| {
            let state_display = display_input.find_state_name(work_item);
            serde_json::json!({
                "id": format!("{}-{}", display_input.identifier_code, work_item.sequence_id().as_u32()),
                "title": work_item.title().as_str(),
                "state": state_display,
                "priority": format!("{:?}", work_item.priority()),
                "due": work_item.target_date().as_date_string(),
            })
        })
        .collect();

    match serde_json::to_string_pretty(&json_items) {
        Ok(formatted_json) => println!("{}", formatted_json),
        Err(serialization_error) => eprintln!("error serializing JSON: {}", serialization_error),
    }
}

/// Outputs work items as CSV rows with resolved state names
fn print_work_items_csv(display_input: &WorkItemDisplayInput<'_>) {
    println!("id,title,state,priority,due");
    for work_item in display_input.work_items {
        let identifier_field = format!("{}-{}", display_input.identifier_code, work_item.sequence_id().as_u32());
        let state_field = display_input.find_state_name(work_item);
        let due_field = match work_item.target_date().as_date_string() {
            Option::Some(date_text) => date_text.to_owned(),
            Option::None => String::new(),
        };
        println!(
            "{},\"{}\",{},{:?},{}",
            identifier_field,
            work_item.title().as_str().replace('"', "\"\""),
            state_field,
            work_item.priority(),
            due_field,
        );
    }
}

// ── MODULES DISPLAY ──────────────────────────────────────────────────

/// Renders modules into a terminal table with workflow phase, role, and date range
fn print_modules_table(my_modules: &[&PlaneModule]) {
    if my_modules.is_empty() {
        println!("No modules assigned to you.");
        return;
    }

    let mut terminal_table = Table::new();
    terminal_table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec![
            Cell::new("Module"),
            Cell::new("Phase"),
            Cell::new("Role"),
            Cell::new("Dates"),
        ]);

    for module_entry in my_modules {
        let workflow_phase_cell = format_module_workflow_phase(module_entry.workflow_phase());
        let role_cell = match module_entry.leadership() {
            ModuleLeadership::Led(_) => "Lead".yellow().to_string(),
            ModuleLeadership::Unleaded => "Member".dimmed().to_string(),
        };
        let dates_cell = match (module_entry.start_date(), module_entry.target_date()) {
            ("", "") => String::from("—"),
            (start, "") => format!("{} →", start),
            ("", target) => format!("→ {}", target),
            (start, target) => format!("{} → {}", start, target),
        };

        terminal_table.add_row(vec![
            Cell::new(module_entry.display_name()),
            Cell::new(workflow_phase_cell),
            Cell::new(role_cell),
            Cell::new(dates_cell),
        ]);
    }

    println!("{}", terminal_table);
}

/// Serializes modules to JSON with workflow phase and date fields
fn print_modules_json(my_modules: &[&PlaneModule]) {
    let json_entries: Vec<serde_json::Value> = my_modules
        .iter()
        .map(|module_entry| {
            serde_json::json!({
                "name": module_entry.display_name(),
                "workflow_phase": format!("{:?}", module_entry.workflow_phase()),
                "start_date": module_entry.start_date(),
                "target_date": module_entry.target_date(),
            })
        })
        .collect();

    match serde_json::to_string_pretty(&json_entries) {
        Ok(formatted_json) => println!("{}", formatted_json),
        Err(serialization_error) => eprintln!("error serializing JSON: {}", serialization_error),
    }
}

/// Outputs modules as CSV rows with workflow phase and dates
fn print_modules_csv(my_modules: &[&PlaneModule]) {
    println!("name,workflow_phase,start_date,target_date");
    for module_entry in my_modules {
        println!(
            "\"{}\",{:?},{},{}",
            module_entry.display_name().replace('"', "\"\""),
            module_entry.workflow_phase(),
            module_entry.start_date(),
            module_entry.target_date(),
        );
    }
}

// ── CYCLES DISPLAY ───────────────────────────────────────────────────

/// Renders cycles into a terminal table with start and end dates
fn print_cycles_table(my_cycles: &[&PlaneCycle]) {
    if my_cycles.is_empty() {
        println!("No cycles owned by you.");
        return;
    }

    let mut terminal_table = Table::new();
    terminal_table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec![
            Cell::new("Cycle"),
            Cell::new("Start"),
            Cell::new("End"),
        ]);

    for cycle_entry in my_cycles {
        let start_cell = match cycle_entry.start_date() {
            "" => String::from("—"),
            date_text => date_text.to_owned(),
        };
        let end_cell = match cycle_entry.end_date() {
            "" => String::from("—"),
            date_text => date_text.to_owned(),
        };
        terminal_table.add_row(vec![
            Cell::new(cycle_entry.display_name()),
            Cell::new(start_cell),
            Cell::new(end_cell),
        ]);
    }

    println!("{}", terminal_table);
}

/// Serializes cycles to JSON with date fields
fn print_cycles_json(my_cycles: &[&PlaneCycle]) {
    let json_entries: Vec<serde_json::Value> = my_cycles
        .iter()
        .map(|cycle_entry| {
            serde_json::json!({
                "name": cycle_entry.display_name(),
                "start_date": cycle_entry.start_date(),
                "end_date": cycle_entry.end_date(),
            })
        })
        .collect();

    match serde_json::to_string_pretty(&json_entries) {
        Ok(formatted_json) => println!("{}", formatted_json),
        Err(serialization_error) => eprintln!("error serializing JSON: {}", serialization_error),
    }
}

/// Outputs cycles as CSV rows with date fields
fn print_cycles_csv(my_cycles: &[&PlaneCycle]) {
    println!("name,start_date,end_date");
    for cycle_entry in my_cycles {
        println!(
            "\"{}\",{},{}",
            cycle_entry.display_name().replace('"', "\"\""),
            cycle_entry.start_date(),
            cycle_entry.end_date(),
        );
    }
}

// ── SHARED DISPLAY HELPERS ───────────────────────────────────────────

/// Applies terminal color to a state name based on its workflow group
fn colorize_state_display(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(),
    }
}

/// Formats a work item priority level with terminal color coding
fn format_priority_display(work_item_priority: WorkItemPriority) -> String {
    match work_item_priority {
        WorkItemPriority::Urgent => "Urgent".red().bold().to_string(),
        WorkItemPriority::High => "High".red().to_string(),
        WorkItemPriority::Medium => "Medium".yellow().to_string(),
        WorkItemPriority::Low => "Low".dimmed().to_string(),
        WorkItemPriority::None => String::from("—"),
    }
}

/// Formats a module workflow phase with terminal color coding
fn format_module_workflow_phase(workflow_phase: &ModuleWorkflowPhase) -> String {
    match workflow_phase {
        ModuleWorkflowPhase::Backlog => "Backlog".dimmed().to_string(),
        ModuleWorkflowPhase::Planned => "Planned".white().to_string(),
        ModuleWorkflowPhase::InProgress => "In Progress".yellow().to_string(),
        ModuleWorkflowPhase::Paused => "Paused".red().to_string(),
        ModuleWorkflowPhase::Completed => "Completed".green().to_string(),
        ModuleWorkflowPhase::Cancelled => "Cancelled".red().dimmed().to_string(),
    }
}
