//! Checklist management handler

use crate::handlers::project_resolver;
use crate::ui_config;
use anyhow::{anyhow, Context, Result};
use chrono::Utc;
use colored::Colorize;
use inquire::Select;
use std::path::Path;
use syntaxis_core::persistence::{Database, DbChecklistItem, SqliteDatabase};
use syntaxis_core::ProjectConfig;
use tools_shared::find_config_path_warn_conflicts;
use uuid::Uuid;

/// Handler for checklist operations
#[derive(Debug)]
pub struct ChecklistHandler;

impl ChecklistHandler {
    /// Show checklist for a phase with optional type filtering
    pub async fn show_checklist(phase: Option<&str>) -> Result<()> {
        let phase_name = phase.unwrap_or("Creation");
        println!("✅ Checklist for {} Phase\n", phase_name.bold());

        // Load configuration and database
        let config_path = Self::find_config()?;
        let config = ProjectConfig::load_from_file(&config_path)
            .context("Failed to load project configuration")?;

        let project_root = config_path
            .parent()
            .and_then(|p| p.parent())
            .unwrap_or_else(|| Path::new("."));
        let db_path = project_root.join(&config.database_path);
        let db_path_str = db_path.to_string_lossy().to_string();

        let db = SqliteDatabase::new(&db_path_str)
            .await
            .context("Failed to initialize database")?;

        // Load items from database for this phase
        let items = db
            .get_checklist_items_by_phase(&config.project.name, phase_name)
            .await
            .unwrap_or_default();

        let completed = items.iter().filter(|item| item.completed).count();
        let total = items.len();
        let percentage = if total > 0 {
            (completed as f64 / total as f64) * 100.0
        } else {
            0.0
        };

        println!("  Progress: {}/{} ({:.0}%)\n", completed, total, percentage);

        if items.is_empty() {
            println!("  {}", "No checklist items for this phase yet".dimmed());
        } else {
            // Display items in table format with more details
            println!(
                "  {:<4} {:<20} {:<35} {:<10} {:<8} {:<8}",
                "№", "ID", "Description", "Type", "Priority", "Status"
            );
            println!("  {}", "─".repeat(110));

            for (idx, item) in items.iter().enumerate() {
                let status = if item.completed {
                    "✓".green()
                } else {
                    "○".yellow()
                };

                let task_type = if item.task_type.is_empty() {
                    "feature".dimmed().to_string()
                } else {
                    item.task_type.cyan().to_string()
                };

                let priority = if item.task_priority.is_empty() {
                    "normal".dimmed().to_string()
                } else {
                    match item.task_priority.as_str() {
                        "high" => "high".red().to_string(),
                        "low" => "low".blue().to_string(),
                        _ => item.task_priority.yellow().to_string(),
                    }
                };

                let task_desc = if item.description.len() > 35 {
                    format!("{}...", &item.description[..32])
                } else {
                    item.description.clone()
                };

                println!(
                    "  {:<4} {:<20} {:<35} {:<10} {:<8} {}",
                    idx + 1,
                    item.task_id,
                    task_desc,
                    task_type,
                    priority,
                    status
                );
            }

            // Show detailed information for each item if it has additional fields
            println!("\n  {}", "Details:".bold());
            for (idx, item) in items.iter().enumerate() {
                println!(
                    "    [{}] {} {}",
                    idx + 1,
                    if item.completed {
                        "✓".green()
                    } else {
                        "○".yellow()
                    },
                    item.task_id.cyan()
                );
                println!("        Description: {}", item.description);

                if let Some(name) = &item.task_name {
                    if !name.is_empty() {
                        println!("        Name: {}", name);
                    }
                }

                println!(
                    "        Type: {} | Priority: {}",
                    item.task_type.cyan(),
                    if item.task_priority.is_empty() {
                        "normal".dimmed().to_string()
                    } else {
                        item.task_priority.yellow().to_string()
                    }
                );

                if let Some(due) = &item.task_due {
                    if !due.is_empty() {
                        println!("        Due: {}", due.yellow());
                    }
                }

                if let Some(est) = &item.task_estimation {
                    if !est.is_empty() {
                        println!("        Estimation: {}", est.cyan());
                    }
                }

                if !item.task_deps.is_empty() {
                    println!("        Dependencies: {}", item.task_deps.dimmed());
                }

                if let Some(note) = &item.task_note {
                    if !note.is_empty() {
                        println!("        Notes: {}", note.dimmed());
                    }
                }
            }

            // Summary by type
            println!("\n  {} Items by Type:", "•".cyan());
            let mut types_map: std::collections::HashMap<String, (usize, usize)> =
                std::collections::HashMap::new();
            for item in &items {
                let item_type = if item.task_type.is_empty() {
                    "feature".to_string()
                } else {
                    item.task_type.clone()
                };
                let entry = types_map.entry(item_type).or_insert((0, 0));
                entry.0 += 1;
                if item.completed {
                    entry.1 += 1;
                }
            }

            for (item_type, (total_cnt, completed_cnt)) in types_map.iter() {
                let pct = if *total_cnt > 0 {
                    (*completed_cnt as f64 / *total_cnt as f64) * 100.0
                } else {
                    0.0
                };
                println!(
                    "    {} {:<12} {}/{} ({:.0}%)",
                    "→".blue(),
                    item_type.cyan(),
                    completed_cnt,
                    total_cnt,
                    pct
                );
            }
        }

        if ui_config::should_show_more() {
            println!("\n  Commands:");
            let app_name = ui_config::get_app_name();
            println!(
                "    {} checklist complete <id>     - Mark item complete",
                app_name
            );
            println!(
                "    {} checklist add '<description>' - Add new item",
                app_name
            );
            println!(
                "    {} checklist types              - Show available types",
                app_name
            );
        }

        Ok(())
    }

    /// Show available task types with help
    pub fn show_types_help() -> Result<()> {
        println!("📋 Available Task Types\n");
        println!("  Use these types when adding checklist items:\n");

        let types = vec![
            (
                "arch",
                "Architecture & Design",
                "System architecture, design decisions, schemas",
            ),
            (
                "feature",
                "Feature Implementation",
                "New features, functionality, enhancements",
            ),
            (
                "test",
                "Testing & QA",
                "Unit tests, integration tests, QA verification",
            ),
            (
                "debug",
                "Debug & Troubleshooting",
                "Bug investigation, debugging, diagnostics",
            ),
            (
                "review",
                "Code Review",
                "Code review tasks, peer feedback, approval",
            ),
            (
                "docs",
                "Documentation",
                "Documentation updates, API docs, guides",
            ),
            (
                "refactor",
                "Refactoring",
                "Code refactoring, optimization, cleanup",
            ),
            ("bugfix", "Bug Fix", "Bug fixes, patches, issue resolution"),
        ];

        println!(
            "  {:<12} {:<25} Description",
            "Type ID".cyan(),
            "Category".bold()
        );
        println!("  {}", "─".repeat(90));

        for (id, category, desc) in &types {
            println!("  {:<12} {:<25} {}", id.green(), category, desc);
        }

        println!("\n  Example:");
        let app_name = ui_config::get_app_name();
        println!("    {} checklist add 'Implement user login'", app_name);
        println!("    → Prompts for type: {}", "feature".green());

        Ok(())
    }

    /// Mark checklist item as complete
    pub async fn mark_complete(item_id: &str) -> Result<()> {
        println!("✅ Marking item complete: {}\n", item_id.bold());

        // Load configuration and database
        let config_path = Self::find_config()?;
        let config = ProjectConfig::load_from_file(&config_path)
            .context("Failed to load project configuration")?;

        let project_root = config_path
            .parent()
            .and_then(|p| p.parent())
            .unwrap_or_else(|| Path::new("."));
        let db_path = project_root.join(&config.database_path);
        let db_path_str = db_path.to_string_lossy().to_string();

        let db = SqliteDatabase::new(&db_path_str)
            .await
            .context("Failed to initialize database")?;

        println!("  {} Updating checklist...", "•".cyan());

        // Load all items and find the one to mark
        let items = db
            .get_checklist_items(&config.project.name)
            .await
            .context("Failed to load checklist items")?;

        let mut found = false;
        for item in items {
            if item.task_id == item_id {
                found = true;
                println!("  {} Recording timestamp", "•".cyan());
                db.complete_checklist_item(&item.id)
                    .await
                    .context("Failed to mark item as complete")?;
                println!("  {} Saving changes", "•".cyan());
                break;
            }
        }

        if !found {
            return Err(anyhow!("Checklist item not found: {}", item_id));
        }

        println!("\n✅ Item '{}' marked as complete!", item_id);

        // Calculate new completion percentage - get current phase from config
        let current_phase = format!("{:?}", config.phase);
        let percentage = db
            .get_completion_percentage(&config.project.name, &current_phase)
            .await
            .unwrap_or(0.0);
        println!("  Completion: {:.0}%", percentage);

        Ok(())
    }

    /// Add new item with project auto-detection
    #[allow(dead_code)]
    pub async fn add_item_with_project(description: &str, project_arg: Option<&str>) -> Result<()> {
        // Resolve project (with auto-detection fallback)
        let _project_id = project_resolver::resolve_project(project_arg).await?;

        // Use original add_item for now (will be enhanced later)
        // TODO: Support global DB with project resolution
        Self::add_item(description).await
    }

    /// Add new item to checklist with full task details
    pub async fn add_item(description: &str) -> Result<()> {
        println!("➕ Adding new checklist item\n");
        println!("  Description: {}", description.cyan());

        // Load configuration and database
        let config_path = Self::find_config()?;
        let config = ProjectConfig::load_from_file(&config_path)
            .context("Failed to load project configuration")?;

        let project_root = config_path
            .parent()
            .and_then(|p| p.parent())
            .unwrap_or_else(|| Path::new("."));
        let db_path = project_root.join(&config.database_path);
        let db_path_str = db_path.to_string_lossy().to_string();

        let db = SqliteDatabase::new(&db_path_str)
            .await
            .context("Failed to initialize database")?;

        // Get phase-specific defaults or global defaults
        let phase_str = format!("{:?}", config.phase);
        let phase_defaults = config.checklist.phase_defaults.get(&phase_str);

        // Task Type selection (use phase defaults if available, then global defaults)
        println!("\n  {} Task Type:", "•".cyan());
        let task_type_options = vec![
            ("arch", "Architecture & Design"),
            ("feature", "Feature Implementation"),
            ("test", "Testing & QA"),
            ("debug", "Debug & Troubleshooting"),
            ("review", "Code Review"),
            ("docs", "Documentation"),
            ("refactor", "Refactoring"),
            ("bugfix", "Bug Fix"),
        ];
        let default_type = phase_defaults
            .and_then(|pd| pd.default_task_type.as_ref())
            .unwrap_or(&config.checklist.default_task_type);
        let task_type = Self::read_enum("Select type", &task_type_options, default_type)
            .unwrap_or_else(|_| default_type.clone());

        // Priority selection (use phase defaults if available, then global defaults)
        println!("\n  {} Priority Level:", "•".cyan());
        let priority_options = vec![
            ("Low", "Low priority - can wait"),
            ("Medium", "Medium priority - normal"),
            ("High", "High priority - important"),
        ];
        let default_priority = phase_defaults
            .and_then(|pd| pd.default_priority.as_ref())
            .unwrap_or(&config.checklist.default_priority);
        let priority = Self::read_enum("Select priority", &priority_options, default_priority)
            .unwrap_or_else(|_| default_priority.clone());

        // Due date
        println!("\n  {} Due Date:", "•".cyan());
        let due_date = Self::read_date("Enter due date").unwrap_or(None);

        // Estimation
        println!("\n  {} Time Estimation:", "•".cyan());
        let estimation = Self::read_text("Estimation (e.g., 2d, 5h)", "", false)
            .ok()
            .filter(|s| !s.is_empty());

        // Dependencies
        println!("\n  {} Task Dependencies:", "•".cyan());
        let deps = Self::read_list("Enter dependency IDs (comma-separated)").unwrap_or_default();

        // Task Note (additional details)
        println!("\n  {} Additional Notes:", "•".cyan());
        let task_note = Self::read_text("Add any additional notes", "", false)
            .ok()
            .filter(|s| !s.is_empty());

        println!("\n  {} Generating item ID...", "•".cyan());
        let task_id = format!("task-{}", chrono::Local::now().timestamp());
        let item_id = Uuid::new_v4().to_string();
        println!("  ✓ ID: {}", task_id.cyan());

        println!("  {} Saving to checklist...", "•".cyan());

        // Create and save checklist item with full details
        let item = DbChecklistItem {
            id: item_id.clone(),
            project_id: config.project.name.clone(),
            phase: format!("{:?}", config.phase),
            task_id: task_id.clone(),
            description: description.to_string(),
            completed: false,
            completed_at: None,
            task_name: Some(description.to_string()),
            task_type: task_type.clone(),
            task_priority: priority.clone(),
            task_due: due_date.clone(),
            task_estimation: estimation.clone(),
            task_deps: deps.join(","),
            task_note: task_note.clone(),
            created_at: Utc::now().to_rfc3339(),
        };

        db.create_checklist_item(&item)
            .await
            .context("Failed to save checklist item")?;

        println!("\n✅ Item added successfully!");
        println!("  {:<15} {}", "ID:", task_id.cyan());
        println!(
            "  {:<15} {}",
            "Phase:",
            format!("{:?}", config.phase).cyan()
        );
        println!("  {:<15} {}", "Type:", task_type.cyan());
        println!("  {:<15} {}", "Priority:", priority.cyan());
        if let Some(ref due) = due_date {
            println!("  {:<15} {}", "Due Date:", due.cyan());
        }
        if let Some(ref est) = estimation {
            println!("  {:<15} {}", "Estimation:", est.cyan());
        }
        if !deps.is_empty() {
            println!("  {:<15} {}", "Dependencies:", deps.join(", ").cyan());
        }
        if let Some(ref note) = task_note {
            println!("  {:<15} {}", "Notes:", note.cyan());
        }

        Ok(())
    }

    /// Show available task types
    #[allow(dead_code)]
    pub fn show_task_types() {
        let types = vec![
            ("arch", "Architecture & Design"),
            ("feature", "Feature Implementation"),
            ("test", "Testing & QA"),
            ("debug", "Debug & Troubleshooting"),
            ("review", "Code Review"),
            ("docs", "Documentation"),
            ("refactor", "Refactoring"),
            ("bugfix", "Bug Fix"),
        ];

        for (short_id, description) in &types {
            println!("    {} - {}", short_id.cyan(), description);
        }
    }

    /// Read enum input with validation using interactive selection with arrow keys
    fn read_enum(label: &str, options: &[(&str, &str)], default: &str) -> Result<String> {
        let formatted_options: Vec<String> = options
            .iter()
            .map(|(value, desc)| format!("{} - {}", value, desc))
            .collect();

        let default_index = options.iter().position(|(v, _)| *v == default).unwrap_or(0);

        let selection = Select::new(&format!("    {}", label), formatted_options)
            .with_starting_cursor(default_index)
            .prompt()
            .map_err(|e| anyhow!("Selection failed: {}", e))?;

        // Extract the value part (before the " - " separator)
        let selected_value = selection.split(" - ").next().unwrap_or(default).to_string();

        Ok(selected_value)
    }

    /// Read text input with optional validation
    fn read_text(label: &str, default: &str, required: bool) -> Result<String> {
        use std::io::Write;

        loop {
            let default_display = if default.is_empty() {
                "skip".to_string()
            } else {
                default.to_string()
            };
            print!("    {} (default: {})\n    > ", label, default_display);
            std::io::stdout().flush()?;

            let mut input = String::new();
            std::io::stdin().read_line(&mut input)?;
            let input = input.trim();

            if input.is_empty() {
                if default.is_empty() && required {
                    println!("    {} This field is required.", "✗".red());
                    continue;
                }
                return Ok(default.to_string());
            }

            return Ok(input.to_string());
        }
    }

    /// Read date with validation YYYY-MM-DD
    fn read_date(label: &str) -> Result<Option<String>> {
        use std::io::Write;

        loop {
            print!("    {} (YYYY-MM-DD, optional: press Enter)\n    > ", label);
            std::io::stdout().flush()?;

            let mut input = String::new();
            std::io::stdin().read_line(&mut input)?;
            let input = input.trim();

            if input.is_empty() {
                return Ok(None);
            }

            // Simple date validation YYYY-MM-DD
            if input.len() == 10 && input.chars().filter(|c| *c == '-').count() == 2 {
                let parts: Vec<&str> = input.split('-').collect();
                if parts.len() == 3
                    && parts[0].len() == 4
                    && parts[1].len() == 2
                    && parts[2].len() == 2
                    && parts[0].parse::<u32>().is_ok()
                    && parts[1].parse::<u32>().is_ok()
                    && parts[2].parse::<u32>().is_ok()
                {
                    return Ok(Some(input.to_string()));
                }
            }

            println!("    {} Invalid date format. Use YYYY-MM-DD.", "✗".red());
        }
    }

    /// Read comma-separated list
    fn read_list(label: &str) -> Result<Vec<String>> {
        use std::io::Write;

        print!(
            "    {} (comma-separated, optional: press Enter)\n    > ",
            label
        );
        std::io::stdout().flush()?;

        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;
        let input = input.trim();

        if input.is_empty() {
            return Ok(Vec::new());
        }

        let items: Vec<String> = input
            .split(',')
            .map(|s| s.trim().to_string())
            .filter(|s| !s.is_empty())
            .collect();

        Ok(items)
    }

    /// Read old-style input for backward compatibility
    #[allow(dead_code)]
    fn read_input(default: &str) -> Result<String> {
        Self::read_text("Value", default, false)
    }

    // Helper: Find config file using tools-shared
    fn find_config() -> Result<std::path::PathBuf> {
        find_config_path_warn_conflicts("config.toml", None).ok_or_else(|| {
            anyhow!(
                "Configuration not found. Please create:\n\
                 - .project/lifecycle.toml, or\n\
                 - .vapora/lifecycle.toml"
            )
        })
    }

    /// Show checklist statistics
    #[allow(dead_code)]
    pub fn show_stats() -> Result<()> {
        println!("📊 Checklist Statistics\n");

        println!("  Overall Progress");
        println!("    Creation:      {} (2/4 = 50%)", progress_bar(0.50));
        println!("    Devel:         {} (0/4 = 0%)", progress_bar(0.0));
        println!("    Review:        {} (0/4 = 0%)", progress_bar(0.0));
        println!("    Publish:       {} (0/2 = 0%)", progress_bar(0.0));

        println!("\n  Items by Status");
        println!("    ✓ Completed:   {}", "5".green());
        println!("    ○ Pending:     {}", "9".yellow());
        println!("    ✗ Blocked:     {}", "2".red());

        println!("\n  Timeline");
        println!("    Average completion time: 2 days");
        println!("    Days in current phase: 5");

        Ok(())
    }

    /// Export checklist to markdown
    #[allow(dead_code)]
    pub fn export_markdown() -> Result<()> {
        println!("📄 Exporting checklist to markdown...\n");

        let markdown = r#"
# Syntaxis Checklists

## Creation Phase (50% complete)

- [x] Setup repository
- [x] Create README.md
- [ ] Configure CI/CD
- [x] Add LICENSE file

## Devel Phase (0% complete)

- [ ] Implement core features
- [ ] Write unit tests
- [ ] Setup code linting
- [ ] Add code documentation

## Review Phase (0% complete)

- [ ] Complete code review
- [ ] Run security audit
- [ ] Performance testing
- [ ] Update documentation
"#;

        println!("{}", markdown);
        println!("  {} Saved to: .project/checklists.md", "✓".green());

        Ok(())
    }
}

/// Create a progress bar string
fn progress_bar(percentage: f64) -> String {
    let filled = (percentage * 10.0) as usize;
    let empty = 10 - filled;
    let bar = format!("[{}{}]", "█".repeat(filled), "░".repeat(empty));
    format!("{} {:.0}%", bar, percentage * 100.0)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_checklist_handler_creation() {
        let handler = ChecklistHandler;
        assert_eq!(format!("{:?}", handler), "ChecklistHandler");
    }

    #[test]
    fn test_show_stats() {
        let result = ChecklistHandler::show_stats();
        assert!(result.is_ok());
    }

    #[test]
    fn test_export_markdown() {
        let result = ChecklistHandler::export_markdown();
        assert!(result.is_ok());
    }

    #[test]
    fn test_progress_bar_empty() {
        let bar = progress_bar(0.0);
        assert!(bar.contains("0%"));
    }

    #[test]
    fn test_progress_bar_full() {
        let bar = progress_bar(1.0);
        assert!(bar.contains("100%"));
    }
}
