//! Deactivate command - remove fyi hooks from AI coding frameworks.

use std::path::Path;

use anyhow::{Context, Result, bail};

use fyi_core::init::get_settings_path;
use fyi_core::{ConfigFormat, FrameworkAdapter, all_adapters, get_adapter};

/// Options for the deactivate command
pub struct DeactivateOptions {
    /// Frameworks to deactivate (empty = all that have fyi active)
    pub frameworks: Vec<String>,
    /// Remove from project config instead of global
    pub local: bool,
    /// Remove from project local config
    pub settings_local: bool,
    /// Print what would be removed without modifying files
    pub print: bool,
}

/// Check if a framework has fyi hooks active in its config file
fn has_fyi_active(adapter: &dyn FrameworkAdapter, local: bool, settings_local: bool) -> bool {
    let Ok(settings_path) = get_settings_path(adapter, local, settings_local) else {
        return false;
    };

    if !settings_path.exists() {
        return false;
    }

    // Read and check if fyi hooks are present
    match adapter.config_format() {
        ConfigFormat::Json => {
            let contents = match std::fs::read_to_string(&settings_path) {
                Ok(c) => c,
                Err(_) => return false,
            };
            let json: serde_json::Value = match serde_json::from_str(&contents) {
                Ok(j) => j,
                Err(_) => return false,
            };
            // Check if remove_hooks would find anything to remove
            adapter.remove_hooks(json).is_some()
        }
        ConfigFormat::Toml => {
            let contents = match std::fs::read_to_string(&settings_path) {
                Ok(c) => c,
                Err(_) => return false,
            };
            let toml_val: toml::Value = match toml::from_str(&contents) {
                Ok(t) => t,
                Err(_) => return false,
            };
            let json: serde_json::Value = match serde_json::to_value(toml_val) {
                Ok(j) => j,
                Err(_) => return false,
            };
            adapter.remove_hooks(json).is_some()
        }
    }
}

/// Resolve which frameworks to deactivate.
/// Returns a list of framework adapters to deactivate.
fn resolve_frameworks(
    frameworks: &[String],
    local: bool,
    settings_local: bool,
) -> Result<Vec<&'static dyn FrameworkAdapter>> {
    if frameworks.is_empty() {
        // Find all frameworks that have fyi active
        let active: Vec<_> = all_adapters()
            .into_iter()
            .filter(|a| has_fyi_active(*a, local, settings_local))
            .collect();

        if active.is_empty() {
            bail!("No frameworks have fyi activated");
        }
        Ok(active)
    } else {
        // Resolve each specified framework
        let mut adapters = Vec::new();
        for name in frameworks {
            let adapter =
                get_adapter(name).ok_or_else(|| anyhow::anyhow!("unknown framework: {}", name))?;
            adapters.push(adapter);
        }
        Ok(adapters)
    }
}

/// Run the deactivate command.
pub fn run_deactivate(opts: DeactivateOptions) -> Result<()> {
    // Resolve which frameworks to deactivate
    let adapters = resolve_frameworks(&opts.frameworks, opts.local, opts.settings_local)?;

    for adapter in adapters {
        deactivate_framework(adapter, &opts)?;
    }

    Ok(())
}

/// Deactivate fyi for a single framework
fn deactivate_framework(adapter: &dyn FrameworkAdapter, opts: &DeactivateOptions) -> Result<()> {
    let settings_path = get_settings_path(adapter, opts.local, opts.settings_local)
        .context("failed to determine settings path")?;

    if !settings_path.exists() {
        eprintln!(
            "fyi is not activated for {} (no config file)",
            adapter.name()
        );
        return Ok(());
    }

    // Read existing settings
    let existing = read_settings(adapter, &settings_path)?;

    // Try to remove hooks
    let modified = adapter.remove_hooks(existing);

    match modified {
        Some(new_settings) => {
            if opts.print {
                eprintln!("Would deactivate fyi for {}:", adapter.name());
                eprintln!("  Would modify: {}", settings_path.display());
            } else {
                eprint!("Deactivating fyi for {}... ", adapter.name());

                // Write back the modified settings
                write_settings(adapter, &settings_path, &new_settings)?;

                eprintln!("done");
                eprintln!("  Removed hooks from: {}", settings_path.display());
                eprintln!("  Removed OTel configuration");
            }
        }
        None => {
            eprintln!("fyi is not activated for {}", adapter.name());
        }
    }

    Ok(())
}

fn read_settings(adapter: &dyn FrameworkAdapter, path: &Path) -> Result<serde_json::Value> {
    let contents = std::fs::read_to_string(path)
        .with_context(|| format!("failed to read {}", path.display()))?;

    match adapter.config_format() {
        ConfigFormat::Json => {
            serde_json::from_str(&contents).context("failed to parse JSON settings")
        }
        ConfigFormat::Toml => {
            let toml_val: toml::Value =
                toml::from_str(&contents).context("failed to parse TOML settings")?;
            serde_json::to_value(toml_val).context("failed to convert TOML to JSON")
        }
    }
}

fn write_settings(
    adapter: &dyn FrameworkAdapter,
    path: &Path,
    settings: &serde_json::Value,
) -> Result<()> {
    match adapter.config_format() {
        ConfigFormat::Json => {
            let output = serde_json::to_string_pretty(settings)?;
            std::fs::write(path, output)?;
        }
        ConfigFormat::Toml => {
            let toml_value: toml::Value = serde_json::from_value(settings.clone())
                .context("failed to convert JSON to TOML-compatible value")?;
            let output = toml::to_string_pretty(&toml_value).context("failed to serialize TOML")?;
            std::fs::write(path, output)?;
        }
    }
    Ok(())
}
