use log::LevelFilter;
use serde::Deserialize;
use serde::de::Deserializer;
use serde_json::Value;
use std::path::PathBuf;
use tower_lsp::lsp_types::DiagnosticSeverity;

fn default_log_level() -> LevelFilter {
    LevelFilter::Info
}

fn deserialize_log_level<'de, D>(deserializer: D) -> Result<LevelFilter, D::Error>
where
    D: Deserializer<'de>,
{
    let s: Option<String> = Option::deserialize(deserializer)?;
    match s.as_deref() {
        Some("trace") => Ok(LevelFilter::Trace),
        Some("debug") => Ok(LevelFilter::Debug),
        Some("warn") => Ok(LevelFilter::Warn),
        Some("error") => Ok(LevelFilter::Error),
        _ => Ok(LevelFilter::Info),
    }
}

fn deserialize_optional_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
    D: Deserializer<'de>,
{
    let s: Option<String> = Option::deserialize(deserializer)?;
    match s {
        Some(path) => {
            let trimmed = path.trim();
            if trimmed.is_empty() {
                Ok(None)
            } else {
                Ok(Some(PathBuf::from(trimmed)))
            }
        }
        None => Ok(None),
    }
}

fn default_check_while_typing() -> bool {
    true
}

fn default_diagnostic_severity() -> DiagnosticSeverity {
    DiagnosticSeverity::INFORMATION
}

fn deserialize_diagnostic_severity<'de, D>(deserializer: D) -> Result<DiagnosticSeverity, D::Error>
where
    D: Deserializer<'de>,
{
    let s: Option<String> = Option::deserialize(deserializer)?;
    match s.as_deref() {
        Some("error") => Ok(DiagnosticSeverity::ERROR),
        Some("warning") => Ok(DiagnosticSeverity::WARNING),
        Some("information") => Ok(DiagnosticSeverity::INFORMATION),
        Some("hint") => Ok(DiagnosticSeverity::HINT),
        _ => Ok(default_diagnostic_severity()),
    }
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ClientInitializationOptions {
    #[serde(
        default = "default_log_level",
        deserialize_with = "deserialize_log_level"
    )]
    pub(crate) log_level: LevelFilter,
    #[serde(default, deserialize_with = "deserialize_optional_path")]
    pub(crate) global_config_path: Option<PathBuf>,
    #[serde(default, deserialize_with = "deserialize_optional_path")]
    pub(crate) config_path: Option<PathBuf>,
    #[serde(default = "default_check_while_typing")]
    pub(crate) check_while_typing: bool,
    #[serde(
        default = "default_diagnostic_severity",
        deserialize_with = "deserialize_diagnostic_severity"
    )]
    pub(crate) diagnostic_severity: DiagnosticSeverity,
}

impl Default for ClientInitializationOptions {
    fn default() -> Self {
        ClientInitializationOptions {
            log_level: default_log_level(),
            global_config_path: None,
            config_path: None,
            check_while_typing: true,
            diagnostic_severity: default_diagnostic_severity(),
        }
    }
}

impl ClientInitializationOptions {
    pub(crate) fn from_value(options_value: Option<Value>) -> Self {
        match options_value {
            None => ClientInitializationOptions::default(),
            Some(value) => match serde_json::from_value(value) {
                Ok(options) => options,
                Err(err) => {
                    log::error!(
                        "Failed to deserialize client initialization options. Using default: {}",
                        err
                    );
                    ClientInitializationOptions::default()
                }
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_default() {
        let default_options = ClientInitializationOptions::default();
        assert_eq!(default_options.log_level, LevelFilter::Info);
        assert!(default_options.check_while_typing);
    }

    #[test]
    fn test_custom() {
        let custom_options = ClientInitializationOptions {
            log_level: LevelFilter::Debug,
            check_while_typing: false,
            ..Default::default()
        };
        assert_eq!(custom_options.log_level, LevelFilter::Debug);
        assert!(!custom_options.check_while_typing);
    }

    #[test]
    fn test_json() {
        let json =
            r#"{"logLevel": "debug", "checkWhileTyping": false, "diagnosticSeverity": "warning"}"#;
        let options: ClientInitializationOptions = serde_json::from_str(json).unwrap();
        assert_eq!(options.log_level, LevelFilter::Debug);
        assert!(!options.check_while_typing);
        assert_eq!(options.diagnostic_severity, DiagnosticSeverity::WARNING);
    }

    #[test]
    fn test_config_path() {
        let json = r#"{"configPath": "toolConfig/codebook.toml"}"#;
        let options: ClientInitializationOptions = serde_json::from_str(json).unwrap();
        assert_eq!(
            options.config_path,
            Some(PathBuf::from("toolConfig/codebook.toml"))
        );
        assert_eq!(options.global_config_path, None);
    }

    #[test]
    fn test_config_path_empty_string_is_none() {
        let json = r#"{"configPath": "  "}"#;
        let options: ClientInitializationOptions = serde_json::from_str(json).unwrap();
        assert_eq!(options.config_path, None);
    }
}
