use serde::{Deserialize, Serialize};
use std::path::PathBuf;

use super::{BandConfig, ProcessorConfig};

// ── Rutas ─────────────────────────────────────────────────────────────────────

fn presets_dir() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("."))
        .join("gradio")
        .join("presets")
}

// ── Struct ────────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Preset {
    pub name:              String,
    pub num_bands:         usize,
    pub crossover_freqs:   [f32; 5],
    pub input_gain_db:     f32,
    pub output_gain_db:    f32,
    pub limiter_threshold: f32,
    pub bands:             [BandConfig; 6],
}

impl Preset {
    /// Crea un preset desde el estado actual del config.
    pub fn from_config(name: &str, cfg: &ProcessorConfig) -> Self {
        Self {
            name:              name.to_string(),
            num_bands:         cfg.num_bands,
            crossover_freqs:   cfg.crossover_freqs,
            input_gain_db:     cfg.input_gain_db,
            output_gain_db:    cfg.output_gain_db,
            limiter_threshold: cfg.limiter_threshold,
            bands:             cfg.bands.clone(),
        }
    }

    /// Aplica el preset al config preservando sample_rate y enabled.
    pub fn apply(&self, cfg: &mut ProcessorConfig) {
        cfg.num_bands         = self.num_bands;
        cfg.crossover_freqs   = self.crossover_freqs;
        cfg.input_gain_db     = self.input_gain_db;
        cfg.output_gain_db    = self.output_gain_db;
        cfg.limiter_threshold = self.limiter_threshold;
        cfg.bands             = self.bands.clone();
    }

    /// Guarda el preset de usuario en disco.
    pub fn save(&self) -> anyhow::Result<()> {
        let dir = presets_dir();
        std::fs::create_dir_all(&dir)?;
        let fname = sanitize_name(&self.name) + ".json";
        let json  = serde_json::to_string_pretty(self)?;
        std::fs::write(dir.join(fname), json)?;
        Ok(())
    }

    /// Elimina el preset de usuario del disco.
    pub fn delete(name: &str) {
        let path = presets_dir().join(sanitize_name(name) + ".json");
        let _ = std::fs::remove_file(path);
    }
}

fn sanitize_name(name: &str) -> String {
    name.chars()
        .map(|c| if c.is_alphanumeric() || c == '-' || c == '_' { c } else { '_' })
        .collect()
}

// ── Carga ─────────────────────────────────────────────────────────────────────

/// Presets de usuario guardados en disco.
pub fn load_user_presets() -> Vec<Preset> {
    let dir = presets_dir();
    let Ok(entries) = std::fs::read_dir(&dir) else { return vec![]; };
    let mut v: Vec<Preset> = entries
        .filter_map(|e| {
            let path = e.ok()?.path();
            if path.extension()?.to_str()? != "json" { return None; }
            let data = std::fs::read_to_string(&path).ok()?;
            serde_json::from_str(&data).ok()
        })
        .collect();
    v.sort_by(|a, b| a.name.cmp(&b.name));
    v
}

/// Todos los presets disponibles: fábrica primero, usuario al final.
pub fn all_presets() -> Vec<Preset> {
    let mut v = builtin_presets();
    v.extend(load_user_presets());
    v
}

// ── Presets de fábrica ────────────────────────────────────────────────────────

pub fn builtin_presets() -> Vec<Preset> {
    vec![
        preset_rock(),
        preset_blues(),
        preset_classic(),
        preset_voice(),
        preset_hard_bass(),
        preset_strong(),
    ]
}

fn b(
    threshold: f32, ratio: f32,
    attack: f32,    release: f32,
    makeup: f32,    clip: f32,
) -> BandConfig {
    BandConfig {
        enabled: true, threshold_db: threshold, ratio,
        attack_ms: attack, release_ms: release, makeup_db: makeup,
        clip_enable: true, clip_level_db: clip,
        exp_enable: false, exp_threshold: -60.0, exp_ratio: 2.0,
    }
}

/// ROCK — Compresión agresiva, presencia mid, punch en graves.
fn preset_rock() -> Preset {
    Preset {
        name: "ROCK".into(),
        num_bands: 5,
        crossover_freqs: [120.0, 400.0, 2_000.0, 6_000.0, 0.0],
        input_gain_db: 0.0, output_gain_db: 0.0,
        limiter_threshold: -0.3,
        bands: [
            b(-24.0, 4.0, 15.0, 200.0, 4.0, -0.5),   // <120 Hz  — sub controlado
            b(-20.0, 4.0,  8.0, 150.0, 5.0, -0.5),   // 120–400  — bajo punch
            b(-18.0, 5.0,  5.0, 100.0, 6.0, -0.5),   // 400–2k   — presencia rock
            b(-20.0, 4.0,  3.0,  80.0, 4.0, -0.5),   // 2k–6k    — agresividad
            b(-24.0, 3.0,  2.0,  60.0, 3.0, -1.0),   // >6k      — aire controlado
            b(-30.0, 2.0, 30.0, 500.0, 0.0, -2.0),   // B6 inactiva
        ],
    }
}

/// BLUE — Cálido, dinámico, jazz/blues. Menor compresión, sonido natural.
fn preset_blues() -> Preset {
    Preset {
        name: "BLUE".into(),
        num_bands: 5,
        crossover_freqs: [120.0, 400.0, 2_000.0, 6_000.0, 0.0],
        input_gain_db: 0.0, output_gain_db: 0.0,
        limiter_threshold: -0.5,
        bands: [
            b(-26.0, 2.5, 25.0, 300.0, 2.5, -1.0),
            b(-22.0, 3.0, 12.0, 200.0, 3.0, -1.0),
            b(-20.0, 2.5,  8.0, 150.0, 2.0, -1.5),
            b(-22.0, 2.0,  5.0, 100.0, 2.0, -2.0),
            b(-28.0, 2.0,  3.0,  80.0, 1.5, -2.0),
            b(-30.0, 2.0, 30.0, 500.0, 0.0, -2.0),
        ],
    }
}

/// CLASIC — Clásica/orquestal. Transparente, dinámica amplia, toque suave.
fn preset_classic() -> Preset {
    Preset {
        name: "CLASIC".into(),
        num_bands: 5,
        crossover_freqs: [120.0, 400.0, 2_000.0, 6_000.0, 0.0],
        input_gain_db: 0.0, output_gain_db: -1.0,
        limiter_threshold: -1.0,
        bands: [
            b(-16.0, 2.0, 50.0, 600.0, 1.0, -2.0),
            b(-16.0, 1.8, 30.0, 500.0, 1.0, -2.0),
            b(-14.0, 1.5, 20.0, 400.0, 1.0, -2.0),
            b(-18.0, 1.5, 10.0, 300.0, 1.0, -2.0),
            b(-22.0, 2.0,  5.0, 200.0, 2.0, -2.0),
            b(-30.0, 2.0, 30.0, 500.0, 0.0, -2.0),
        ],
    }
}

/// VOICE — Voz para radio FM: presencia 1–4kHz, de-essing, sub cortado.
fn preset_voice() -> Preset {
    Preset {
        name: "VOICE".into(),
        num_bands: 5,
        crossover_freqs: [120.0, 400.0, 2_000.0, 6_000.0, 0.0],
        input_gain_db: 0.0, output_gain_db: 0.0,
        limiter_threshold: -0.2,
        bands: [
            b(-18.0, 4.0, 10.0, 150.0, 2.0, -1.0),   // <120  — cortar boomy
            b(-16.0, 5.0,  6.0, 120.0, 3.0, -0.5),   // 120–400 — firmeza
            b(-14.0, 6.0,  4.0, 100.0, 7.0, -0.5),   // 400–2k  — presencia voz
            b(-16.0, 8.0,  2.0,  60.0, 5.0, -0.5),   // 2k–6k   — de-essing
            b(-22.0, 4.0, 1.5,   40.0, 2.0, -1.0),   // >6k     — aire suave
            b(-30.0, 2.0, 30.0, 500.0, 0.0, -2.0),
        ],
    }
}

/// HARD BASS — Electrónica/Hip-hop. Sub amplificado al máximo, 6 bandas.
fn preset_hard_bass() -> Preset {
    Preset {
        name: "HARD BASS".into(),
        num_bands: 6,
        crossover_freqs: [80.0, 320.0, 1_200.0, 4_800.0, 12_000.0],
        input_gain_db: 0.0, output_gain_db: -2.0,
        limiter_threshold: -0.2,
        bands: [
            b(-16.0, 8.0,  3.0,  80.0, 12.0, -0.3),  // <80    — sub bestial
            b(-18.0, 6.0,  5.0, 100.0,  9.0, -0.3),  // 80–320 — bajo punch
            b(-20.0, 4.0,  4.0, 100.0,  4.0, -0.5),  // 320–1.2k
            b(-22.0, 3.5,  3.0,  80.0,  3.0, -0.5),  // 1.2k–4.8k
            b(-26.0, 3.0,  2.0,  60.0,  2.0, -1.0),  // 4.8k–12k
            b(-28.0, 2.5,  2.0,  50.0,  1.5, -1.5),  // >12k
        ],
    }
}

/// STRONG — Máxima densidad/loudness, radio genérica, muro de sonido.
fn preset_strong() -> Preset {
    Preset {
        name: "STRONG".into(),
        num_bands: 5,
        crossover_freqs: [120.0, 400.0, 2_000.0, 6_000.0, 0.0],
        input_gain_db: 0.0, output_gain_db: 0.0,
        limiter_threshold: -0.1,
        bands: [
            b(-30.0, 8.0,  5.0, 100.0, 10.0, -0.3),
            b(-28.0, 8.0,  4.0,  80.0, 10.0, -0.3),
            b(-26.0, 8.0,  3.0,  70.0, 10.0, -0.3),
            b(-28.0, 6.0,  2.0,  60.0,  8.0, -0.3),
            b(-30.0, 5.0, 1.5,   50.0,  7.0, -0.3),
            b(-30.0, 2.0, 30.0, 500.0,  0.0, -2.0),
        ],
    }
}
