use clap::{CommandFactory, ValueEnum};
use clap_complete::{generate_to, shells::{Bash, Zsh, Fish, PowerShell, Elvish}};
use std::env;
use std::path::PathBuf;
use crate::types::Result;
use crate::cli::Cli;

#[derive(ValueEnum, Clone, Debug)]
#[clap(rename_all = "lower")]
pub enum ShellKind {
    Bash,
    Zsh,
    Fish,
    Powershell,
    Elvish,
}

impl std::fmt::Display for ShellKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            ShellKind::Bash => "bash",
            ShellKind::Zsh => "zsh",
            ShellKind::Fish => "fish",
            ShellKind::Powershell => "powershell",
            ShellKind::Elvish => "elvish",
        };
        write!(f, "{}", s)
    }
}

#[derive(clap::Args, Debug)]
pub struct CompletionArgs {
    #[arg(long, value_enum)]
    pub shell: Option<ShellKind>,
    #[arg(long)]
    pub output_dir: Option<PathBuf>,
    #[arg(long)]
    pub print_only: bool,
}

 

pub async fn handle_completion_command(shell: Option<ShellKind>, output_dir: Option<PathBuf>, print_only: bool) -> Result<()> {
    let mut root_cmd = Cli::command();
    let bin_name = "l";

    let shell = shell
        .or_else(detect_shell)
        .unwrap_or(ShellKind::Bash);

    let dir = output_dir.unwrap_or_else(|| default_completion_dir(&shell));
    std::fs::create_dir_all(&dir)?;

    let path = match shell {
        ShellKind::Bash => generate_to(Bash, &mut root_cmd, bin_name, &dir)?,
        ShellKind::Zsh => generate_to(Zsh, &mut root_cmd, bin_name, &dir)?,
        ShellKind::Fish => generate_to(Fish, &mut root_cmd, bin_name, &dir)?,
        ShellKind::Powershell => generate_to(PowerShell, &mut root_cmd, bin_name, &dir)?,
        ShellKind::Elvish => generate_to(Elvish, &mut root_cmd, bin_name, &dir)?,
    };

    crate::utils::log::success(&format!("Completion generated for {} at {}", shell, path.display()));

    if print_only {
        let content = std::fs::read_to_string(&path)?;
        println!("{}", content);
        return Ok(());
    }

    if let Some(rc_path) = shell_rc_path(&shell) {
        let source_line = match shell {
            ShellKind::Bash => format!("source {}", path.display()),
            ShellKind::Zsh => format!("source {}", path.display()),
            ShellKind::Fish => format!("source {}", path.display()),
            ShellKind::Powershell => format!(". {}", path.display()),
            ShellKind::Elvish => format!("source {}", path.display()),
        };
        append_line_once(&rc_path, &source_line)?;
        crate::utils::log::info(&format!("Added sourcing to {}", rc_path.display()));
    } else {
        crate::utils::log::warning("Could not locate shell rc file automatically. Please source the file manually.");
    }

    Ok(())
}

fn detect_shell() -> Option<ShellKind> {
    if let Ok(shell) = env::var("SHELL") {
        if let Some(name) = shell.split('/').last() {
            return parse_shell_name(name);
        }
    }
    if let Ok(comspec) = env::var("COMSPEC") {
        let lower = comspec.to_lowercase();
        if lower.contains("powershell") { return Some(ShellKind::Powershell); }
    }
    None
}

fn default_completion_dir(shell: &ShellKind) -> PathBuf {
    let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
    match shell {
        ShellKind::Bash => PathBuf::from(format!("{}/.config/l/completions", home)),
        ShellKind::Zsh => PathBuf::from(format!("{}/.config/l/completions", home)),
        ShellKind::Fish => PathBuf::from(format!("{}/.config/fish/completions", home)),
        ShellKind::Powershell => PathBuf::from(format!("{}/.config/l/completions", home)),
        ShellKind::Elvish => PathBuf::from(format!("{}/.config/l/completions", home)),
    }
}

fn shell_rc_path(shell: &ShellKind) -> Option<PathBuf> {
    let home = env::var("HOME").ok()?;
    match shell {
        ShellKind::Bash => Some(PathBuf::from(format!("{}/.bashrc", home))),
        ShellKind::Zsh => Some(PathBuf::from(format!("{}/.zshrc", home))),
        ShellKind::Fish => Some(PathBuf::from(format!("{}/.config/fish/config.fish", home))),
        ShellKind::Powershell => Some(PathBuf::from(format!("{}/.config/powershell/Microsoft.PowerShell_profile.ps1", home))),
        ShellKind::Elvish => Some(PathBuf::from(format!("{}/.elvish/rc.elv", home))),
    }
}

fn append_line_once(file: &PathBuf, line: &str) -> std::io::Result<()> {
    use std::io::{Read, Write};
    let mut existing = String::new();
    if let Ok(mut f) = std::fs::File::open(file) {
        let _ = f.read_to_string(&mut existing);
    }
    if !existing.contains(line) {
        let mut f = std::fs::OpenOptions::new().create(true).append(true).open(file)?;
        writeln!(f, "{}", line)?;
    }
    Ok(())
}

fn parse_shell_name(name: &str) -> Option<ShellKind> {
    match name.to_lowercase().as_str() {
        "bash" => Some(ShellKind::Bash),
        "zsh" => Some(ShellKind::Zsh),
        "fish" => Some(ShellKind::Fish),
        "powershell" | "pwsh" => Some(ShellKind::Powershell),
        "elvish" => Some(ShellKind::Elvish),
        _ => None,
    }
}
