//! Lazy config loader for adom-tts.
//!
//! Reads `~/.adom/tts/config.toml` if present; otherwise returns defaults.
//! Schema is flat enough that a tiny hand-rolled parser beats pulling in
//! the `toml` crate. Currently only the `[play]` section exists.
//!
//! ```toml
//! [play]
//! surface     = "hydrogen"   # "hydrogen" | "pup"
//! pup_session = "adom-tts"
//! pup_profile = "adom-tts"
//! ```

use anyhow::Result;
use std::path::PathBuf;
use std::str::FromStr;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Surface {
    Hydrogen,
    Pup,
}

impl FromStr for Surface {
    type Err = anyhow::Error;
    fn from_str(s: &str) -> Result<Self> {
        match s.trim().to_ascii_lowercase().as_str() {
            "hydrogen" | "h" | "webview" => Ok(Surface::Hydrogen),
            "pup" | "p" | "browser" => Ok(Surface::Pup),
            other => anyhow::bail!("unknown surface '{other}' (want 'hydrogen' or 'pup')"),
        }
    }
}

impl Surface {
    pub fn as_str(self) -> &'static str {
        match self {
            Surface::Hydrogen => "hydrogen",
            Surface::Pup => "pup",
        }
    }
}

#[derive(Debug, Clone)]
pub struct PlayConfig {
    pub surface: Surface,
    pub pup_session: String,
    pub pup_profile: String,
}

impl Default for PlayConfig {
    fn default() -> Self {
        Self {
            surface: Surface::Hydrogen,
            pup_session: "adom-tts".to_string(),
            pup_profile: "adom-tts".to_string(),
        }
    }
}

#[derive(Debug, Clone, Default)]
pub struct Config {
    pub play: PlayConfig,
}

pub fn config_path() -> Option<PathBuf> {
    let home = std::env::var("HOME").ok()?;
    Some(PathBuf::from(home).join(".adom/tts/config.toml"))
}

/// Infallible — any error returns defaults.
pub fn load() -> Config {
    let path = match config_path() {
        Some(p) if p.exists() => p,
        _ => return Config::default(),
    };
    let src = match std::fs::read_to_string(&path) {
        Ok(s) => s,
        Err(_) => return Config::default(),
    };
    parse(&src)
}

/// Resolve effective surface following the documented precedence:
/// CLI flag → env var (`$ADOM_TTS_SURFACE`) → config file → default.
pub fn resolve_surface(cli: Option<Surface>, cfg: &PlayConfig) -> Surface {
    if let Some(s) = cli {
        return s;
    }
    if let Ok(s) = std::env::var("ADOM_TTS_SURFACE") {
        if let Ok(parsed) = Surface::from_str(&s) {
            return parsed;
        }
        eprintln!("adom-tts: ignoring invalid $ADOM_TTS_SURFACE='{s}'");
    }
    cfg.surface
}

/// Tiny TOML-ish reader. Recognises `[section]` headers and `key = "value"` /
/// `key = value` lines. Strips `#` line comments. Quoted strings only.
fn parse(src: &str) -> Config {
    let mut cfg = Config::default();
    let mut section = String::new();
    for raw in src.lines() {
        let line = match raw.split_once('#') {
            Some((before, _)) => before,
            None => raw,
        }.trim();
        if line.is_empty() {
            continue;
        }
        if let Some(name) = line.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
            section = name.trim().to_string();
            continue;
        }
        let Some((key, value)) = line.split_once('=') else { continue };
        let key = key.trim();
        let value = unquote(value.trim());
        match (section.as_str(), key) {
            ("play", "surface") => {
                if let Ok(s) = Surface::from_str(&value) {
                    cfg.play.surface = s;
                } else {
                    eprintln!("adom-tts: ignoring invalid surface '{value}' in config");
                }
            }
            ("play", "pup_session") => cfg.play.pup_session = value,
            ("play", "pup_profile") => cfg.play.pup_profile = value,
            _ => {}
        }
    }
    cfg
}

fn unquote(s: &str) -> String {
    let s = s.trim();
    if (s.starts_with('"') && s.ends_with('"') && s.len() >= 2)
        || (s.starts_with('\'') && s.ends_with('\'') && s.len() >= 2)
    {
        s[1..s.len() - 1].to_string()
    } else {
        s.to_string()
    }
}

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

    #[test]
    fn parse_full() {
        let src = r#"
# header
[play]
surface     = "pup"
pup_session = "my-session"
pup_profile = 'my-profile'
"#;
        let cfg = parse(src);
        assert_eq!(cfg.play.surface, Surface::Pup);
        assert_eq!(cfg.play.pup_session, "my-session");
        assert_eq!(cfg.play.pup_profile, "my-profile");
    }

    #[test]
    fn parse_empty_returns_defaults() {
        let cfg = parse("");
        assert_eq!(cfg.play.surface, Surface::Hydrogen);
        assert_eq!(cfg.play.pup_session, "adom-tts");
    }

    #[test]
    fn surface_from_str() {
        assert_eq!(Surface::from_str("hydrogen").unwrap(), Surface::Hydrogen);
        assert_eq!(Surface::from_str("Pup").unwrap(), Surface::Pup);
        assert!(Surface::from_str("nope").is_err());
    }
}