use std::path::{Path, PathBuf};

use anyhow::{anyhow, Result};
use blizz::{run_wizard, Branch, WizardStep};
use colored::Colorize;

const CURSOR_RULE: &str = include_str!("../../.cursor/rules/use-insights.mdc");
const BOOTSTRAP_SCRIPT: &str = r#"#!/usr/bin/env bash
# insights shell bootstrap — generated by `insights setup`
# Edit freely; re-run `insights setup` to regenerate.

if command -v insights >/dev/null 2>&1; then
  export INSIGHTS_HOME="${INSIGHTS_HOME:-$HOME/.local/share/insights}"

  # Shell completions (if supported)
  if insights completions bash >/dev/null 2>&1; then
    eval "$(insights completions "${INSIGHTS_SHELL:-${SHELL##*/}}")"
  fi
fi
"#;
const RC_SHIM: &str = r#"# insights
[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/insights/init.sh" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/insights/init.sh""#;

pub fn setup() -> Result<()> {
    let default_rule_path = detect_default_rule_path();
    let detected_rc = detect_shell_rc();
    let rc_hint = detected_rc
        .as_ref()
        .map(|p| format!("Detected: {}", p.display()))
        .unwrap_or_else(|| "No shell RC found".to_string());

    let steps = build_wizard_steps(&default_rule_path, &rc_hint);
    let answers = run_wizard(&steps).map_err(|e| anyhow!("wizard failed: {e}"))?;

    let rule_path = resolve_rule_path(&answers, &default_rule_path);
    let install_bootstrap = is_yes(&answers, "INSTALL_BOOTSTRAP");
    let install_shim = is_yes(&answers, "INSTALL_SHIM");

    install_rule(&rule_path)?;

    if install_bootstrap {
        install_bootstrap_script()?;
    }

    if install_shim {
        if let Some(rc_path) = &detected_rc {
            install_shell_shim(rc_path)?;
        }
    }

    print_summary(&rule_path, install_bootstrap, install_shim, &detected_rc);
    Ok(())
}

fn build_wizard_steps(default_rule_path: &str, rc_hint: &str) -> Vec<WizardStep> {
    vec![
        WizardStep::dialog("Welcome! Let's set up insights for your environment."),
        WizardStep::prompt(
            "Where should the insights rule be installed?",
            "RULE_PATH",
            default_rule_path,
        ),
        WizardStep::prompt(
            "Install shell bootstrap to ~/.config/insights/init.sh?",
            "INSTALL_BOOTSTRAP",
            "Y",
        ),
        WizardStep::prompt(
            &format!("Add source line to shell RC? ({})", rc_hint),
            "INSTALL_SHIM",
            "Y",
        )
        .on_submit(|_| Branch::Finish),
    ]
}

fn detect_default_rule_path() -> String {
    let full = dirs::home_dir()
        .unwrap_or_else(|| PathBuf::from("~"))
        .join(".cursor/rules/use-insights.mdc");
    compress_home(&full)
}

fn compress_home(path: &Path) -> String {
    if let Some(home) = dirs::home_dir() {
        if let Ok(rest) = path.strip_prefix(&home) {
            return format!("~/{}", rest.display());
        }
    }
    path.display().to_string()
}

fn detect_shell_rc() -> Option<PathBuf> {
    let home = dirs::home_dir()?;
    let zshrc = home.join(".zshrc");
    if zshrc.exists() {
        return Some(zshrc);
    }
    let bashrc = home.join(".bashrc");
    if bashrc.exists() {
        return Some(bashrc);
    }
    None
}

fn resolve_rule_path(answers: &serde_json::Value, default: &str) -> PathBuf {
    let raw = answers
        .get("RULE_PATH")
        .and_then(|v| v.as_str())
        .unwrap_or(default);

    let path = PathBuf::from(shellexpand_tilde(raw));
    if path.extension().is_some() {
        path
    } else {
        path.join("use-insights.mdc")
    }
}

fn is_yes(answers: &serde_json::Value, key: &str) -> bool {
    answers
        .get(key)
        .and_then(|v| v.as_str())
        .map(|s| s.is_empty() || s.eq_ignore_ascii_case("y") || s.eq_ignore_ascii_case("yes"))
        .unwrap_or(true)
}

fn install_rule(dest: &Path) -> Result<()> {
    let content = if dest.extension().is_some_and(|e| e == "mdc") {
        CURSOR_RULE.to_string()
    } else {
        strip_frontmatter(CURSOR_RULE)
    };

    if let Some(parent) = dest.parent() {
        std::fs::create_dir_all(parent)?;
    }

    std::fs::write(dest, content)?;
    Ok(())
}

fn strip_frontmatter(content: &str) -> String {
    let mut lines = content.lines();
    if lines.next() != Some("---") {
        return content.to_string();
    }
    for line in lines.by_ref() {
        if line == "---" {
            break;
        }
    }
    lines
        .collect::<Vec<_>>()
        .join("\n")
        .trim_start()
        .to_string()
}

fn install_bootstrap_script() -> Result<()> {
    let config_dir = xdg_config_dir().join("insights");
    std::fs::create_dir_all(&config_dir)?;

    let dest = config_dir.join("init.sh");
    std::fs::write(&dest, BOOTSTRAP_SCRIPT)?;

    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::fs::set_permissions(&dest, std::fs::Permissions::from_mode(0o755))?;
    }

    Ok(())
}

fn install_shell_shim(rc_path: &Path) -> Result<()> {
    let existing = std::fs::read_to_string(rc_path).unwrap_or_default();
    if existing.contains("insights/init.sh") {
        return Ok(());
    }

    let mut content = existing;
    if !content.ends_with('\n') {
        content.push('\n');
    }
    content.push('\n');
    content.push_str(RC_SHIM);
    content.push('\n');

    std::fs::write(rc_path, content)?;
    Ok(())
}

fn print_summary(rule_path: &Path, bootstrap: bool, shim: bool, rc_path: &Option<PathBuf>) {
    println!();
    println!("{}", "Setup complete!".green().bold());
    println!();
    println!("  {} Rule: {}", "✓".green(), rule_path.display());
    if bootstrap {
        println!(
            "  {} Bootstrap: {}",
            "✓".green(),
            xdg_config_dir().join("insights/init.sh").display()
        );
    }
    if shim {
        if let Some(rc) = rc_path {
            println!("  {} Shell shim: {}", "✓".green(), rc.display());
        }
    }
    println!();
    println!("{}", "Hints:".cyan().bold());
    println!("  • Move the rule file anytime — just update your editor config.");
    println!("  • Edit ~/.config/insights/init.sh to customize shell integration.");
    println!("  • Re-run `insights setup` to regenerate with latest defaults.");
}

fn xdg_config_dir() -> PathBuf {
    std::env::var("XDG_CONFIG_HOME")
        .map(PathBuf::from)
        .unwrap_or_else(|_| {
            dirs::home_dir()
                .unwrap_or_else(|| PathBuf::from("~"))
                .join(".config")
        })
}

fn shellexpand_tilde(path: &str) -> String {
    if let Some(rest) = path.strip_prefix("~/") {
        let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("~"));
        home.join(rest).display().to_string()
    } else {
        path.to_string()
    }
}
