//! Project initialization handler

use crate::ui_config;
use anyhow::Result;
use chrono::Local;
use colored::Colorize;
use std::path::{Path, PathBuf};
use syntaxis_core::config::ProjectConfig;
use syntaxis_core::persistence::SqliteDatabase;
use syntaxis_core::types::ProjectType;
use tools_shared::find_config_path_warn_conflicts;

/// Handler for project initialization
#[derive(Debug)]
pub struct InitHandler;

impl InitHandler {
    /// Initialize a new project
    ///
    /// # Arguments
    /// * `project_path` - Path to project directory (defaults to ".")
    /// * `project_type` - Project type (e.g., "rust-web", "rust-cli")
    pub async fn init(project_path: Option<&str>, project_type: Option<&str>) -> Result<()> {
        let path = project_path.unwrap_or(".");
        let project_name = extract_project_name(path);

        println!("🚀 Initializing project: {}", project_name.bold());
        println!("  Location: {}", path.cyan());

        // Check if config already exists and discover its location
        let original_dir = std::env::current_dir()?;
        std::env::set_current_dir(path)?;
        let config_discovery = find_config_path_warn_conflicts("config.toml", None)
            .map(|p| (p.clone(), ProjectConfig::load_from_file(&p).ok()));
        let existing_config = config_discovery.and_then(|(_, cfg)| cfg);
        let _ = std::env::set_current_dir(original_dir);

        let config_existed = existing_config.is_some();

        // Create or load config
        println!("\n  {} Loading project configuration...", "•".cyan());
        let mut config = if let Some(existing) = existing_config {
            println!("    Found existing configuration, merging with defaults...");
            // Load existing and merge with defaults to fill missing sections
            existing.merge_with_defaults()
        } else {
            println!("    Creating new configuration...");
            ProjectConfig::new(&project_name)
        };

        // Update type if provided
        if let Some(t) = project_type {
            config.project_type = parse_project_type(t);
            println!("  Type: {:?}", config.project_type);
        } else {
            println!("  Type: {:?}", config.project_type);
        }

        // Validate
        println!("  {} Validating configuration...", "•".cyan());
        config.validate()?;

        // Create directory structure
        println!("  {} Creating directory structure...", "•".cyan());
        let config_dir = create_project_structure(path, &config).await?;

        // Save configuration to the discovered/created config directory
        println!("  {} Saving configuration...", "•".cyan());
        let config_path = save_config_to_dir(path, &config_dir, &config)?;

        // Initialize database (preserves existing database)
        println!("  {} Initializing database...", "•".cyan());
        initialize_database_in_dir(&config_dir, &config).await?;

        println!("\n✅ Project structure initialized successfully!");

        if config_existed {
            println!("  ✓ Configuration updated with new defaults (SBOM, Export sections)");
        } else {
            println!("  ✓ New configuration created");
        }

        // Show actual paths that were created
        let db_path = config_dir.join(&config.database_path);
        println!("\n  Updated/Created:");
        println!("    • Configuration: {}", config_path.display());
        println!("    • Database: {}", db_path.display());
        println!("    • Config directory: {}/", config_dir.display());
        println!("    • Tracking directory: .coder/");

        // Explain backup strategy
        println!("\n  ℹ️  Backup Information:");
        println!("    • Previous configs archived in: archives/YYYY-MM-DD-HH-MM/");
        println!("    • Existing database is preserved (never overwritten)");
        println!("    • Restore from archives/ folder if needed");

        println!("\n  ⚠️  Next step:");
        let app_name = ui_config::get_app_name();
        println!("    → {} create", app_name);
        println!("\n  After that:");
        println!("    → {} phase current", app_name);
        println!("    → {} checklist show", app_name);
        println!("    → {} security profile <type>", app_name);

        Ok(())
    }

    /// Show init template options
    #[allow(dead_code)]
    pub fn show_templates() -> Result<()> {
        println!("📋 Available project templates:\n");

        let templates = vec![
            ("rust-web", "Rust Web application (Leptos + Axum)"),
            ("rust-cli", "Rust CLI application"),
            ("rust-embedded", "Rust Embedded/Systems project"),
            ("multi-lang", "Multi-language project"),
            ("nushell", "NuShell script project"),
        ];

        for (name, desc) in templates {
            println!("  {} - {}", name.green().bold(), desc);
        }

        let app_name = ui_config::get_app_name();
        println!("\nUsage: {} init --type <template>", app_name);
        Ok(())
    }
}

/// Extract project name from path
fn extract_project_name(path: &str) -> String {
    let path = Path::new(path);
    if path.to_string_lossy() == "." {
        std::env::current_dir()
            .ok()
            .and_then(|p| p.file_name().map(|n| n.to_string_lossy().to_string()))
            .unwrap_or_else(|| "project".to_string())
    } else {
        path.file_name()
            .map(|n| n.to_string_lossy().to_string())
            .unwrap_or_else(|| "project".to_string())
    }
}

/// Parse project type from string
fn parse_project_type(s: &str) -> ProjectType {
    match s.to_lowercase().as_str() {
        "rust-web" | "rustweb" | "web" => ProjectType::RustWeb,
        "rust-cli" | "rustcli" | "cli" => ProjectType::RustCli,
        "rust-embedded" | "rustembedded" | "embedded" => ProjectType::RustEmbedded,
        "multi-lang" | "multilang" => ProjectType::MultiLang,
        "nushell" | "nu" => ProjectType::NuShellScript,
        _ => ProjectType::Custom(s.to_string()),
    }
}

/// Create project directory structure
/// Returns the config directory that was created/discovered
///
/// If no existing config is found, creates in `.project/` as the standard location
async fn create_project_structure(path: &str, _config: &ProjectConfig) -> Result<PathBuf> {
    let root = PathBuf::from(path);

    // Save current directory and change to target path
    let original_dir = std::env::current_dir()?;
    std::env::set_current_dir(&root)?;

    // Determine config directory using standard config discovery
    // Searches: ~/.vapora/ → ~/.syntaxis/ → ./.vapora/ → ./.syntaxis/
    // If none exist, creates new config in ./.syntaxis/ (default local location)
    let config_dir = find_config_path_warn_conflicts("config.toml", None)
        .and_then(|p| p.parent().map(|parent| parent.to_path_buf()))
        .unwrap_or_else(|| PathBuf::from(".syntaxis"));

    if !config_dir.exists() {
        tokio::fs::create_dir_all(&config_dir).await?;
    }

    // Create subdirectories under config directory
    tokio::fs::create_dir_all(config_dir.join("tracking")).await?;
    tokio::fs::create_dir_all(config_dir.join("sbom")).await?;
    tokio::fs::create_dir_all(config_dir.join("backups")).await?;
    tokio::fs::create_dir_all(config_dir.join("data")).await?;
    tokio::fs::create_dir_all(config_dir.join("exports")).await?;

    // Create .coder directory for tracking (always at project root)
    tokio::fs::create_dir_all(".coder").await?;

    // Restore original directory
    let _ = std::env::set_current_dir(original_dir);

    Ok(config_dir)
}

/// Initialize the SQLite database in the specified config directory
async fn initialize_database_in_dir(config_dir: &Path, config: &ProjectConfig) -> Result<()> {
    // Database path is relative to config directory
    let db_file = config_dir.join(&config.database_path);

    // Ensure parent directory exists
    if let Some(parent) = db_file.parent() {
        tokio::fs::create_dir_all(parent).await?;
    }

    // Initialize empty database file if it doesn't exist
    if !db_file.exists() {
        tokio::fs::write(&db_file, "").await?;
    }

    let db_path_str = db_file
        .canonicalize()
        .unwrap_or_else(|_| db_file.clone())
        .to_string_lossy()
        .to_string();

    // Initialize database with absolute path (creates tables)
    let _db = SqliteDatabase::new(&db_path_str).await?;

    Ok(())
}

/// Save configuration to the specified config directory
/// Backs up existing config.toml to archives/{timestamp}/ before saving new version
fn save_config_to_dir(
    project_path: &str,
    config_dir: &Path,
    config: &ProjectConfig,
) -> Result<PathBuf> {
    let root = PathBuf::from(project_path);

    // Save current directory and change to target path
    let original_dir = std::env::current_dir()?;
    std::env::set_current_dir(&root)?;

    // Ensure directory exists
    if !config_dir.exists() {
        std::fs::create_dir_all(config_dir)?;
    }

    let config_path = config_dir.join("config.toml");

    // Backup existing config before overwriting
    if config_path.exists() {
        let archive_dir = create_archive_directory(config_dir)?;
        let backup_path = archive_dir.join("config.toml");
        if let Err(e) = std::fs::rename(&config_path, &backup_path) {
            eprintln!("    ⚠️  Warning: Could not backup existing config: {}", e);
        } else {
            println!("    ⚠️  Previous version of config.toml exists");
            println!(
                "    📦 Archived to: archives/{}",
                archive_dir
                    .file_name()
                    .unwrap_or_default()
                    .to_string_lossy()
            );
            println!("    ℹ️  Previous version preserved - you can restore if needed");
        }
    }

    let toml_str = toml::to_string_pretty(config)?;
    std::fs::write(&config_path, toml_str)?;

    // Restore original directory
    let _ = std::env::set_current_dir(original_dir);

    Ok(config_path)
}

/// Create a timestamped archive directory (YYYY-MM-DD-HH-MM)
/// Returns the path to the newly created archive directory
fn create_archive_directory(config_dir: &Path) -> Result<PathBuf> {
    let archive_root = config_dir.join("archives");

    // Create archives folder if it doesn't exist
    if !archive_root.exists() {
        std::fs::create_dir_all(&archive_root)?;
    }

    // Create timestamp folder: YYYY-MM-DD-HH-MM
    let now = Local::now();
    let timestamp = now.format("%Y-%m-%d-%H-%M").to_string();
    let archive_dir = archive_root.join(&timestamp);

    std::fs::create_dir_all(&archive_dir)?;
    Ok(archive_dir)
}

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

    #[test]
    fn test_extract_project_name_from_path() {
        let name = extract_project_name("/path/to/my-project");
        assert_eq!(name, "my-project");
    }

    #[test]
    fn test_extract_project_name_current_dir() {
        let name = extract_project_name(".");
        assert!(!name.is_empty());
    }

    #[test]
    fn test_parse_rust_web_type() {
        let ptype = parse_project_type("rust-web");
        assert_eq!(ptype, ProjectType::RustWeb);
    }

    #[test]
    fn test_parse_rust_cli_type() {
        let ptype = parse_project_type("rust-cli");
        assert_eq!(ptype, ProjectType::RustCli);
    }

    #[test]
    fn test_parse_multilang_type() {
        let ptype = parse_project_type("multilang");
        assert_eq!(ptype, ProjectType::MultiLang);
    }

    #[test]
    fn test_parse_custom_type() {
        let ptype = parse_project_type("unknown");
        match ptype {
            ProjectType::Custom(s) => assert_eq!(s, "unknown"),
            _ => panic!("Expected Custom type"),
        }
    }

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

    #[tokio::test]
    async fn test_show_templates() {
        let result = InitHandler::show_templates();
        assert!(result.is_ok());
    }
}
