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

const SERVICE_NAME: &str = "quicommit";
const ENV_API_KEY: &str = "QUICOMMIT_API_KEY";

const PAT_SERVICE_PREFIX: &str = "quicommit/pat";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyringStatus {
    Available,
    Unavailable,
}

pub struct KeyringManager {
    status: KeyringStatus,
}

impl KeyringManager {
    pub fn new() -> Self {
        let status = Self::check_keyring_availability();
        Self { status }
    }

    pub fn check_keyring_availability() -> KeyringStatus {
        #[cfg(target_os = "windows")]
        {
            KeyringStatus::Available
        }
        #[cfg(target_os = "macos")]
        {
            KeyringStatus::Available
        }
        #[cfg(target_os = "linux")]
        {
            Self::check_linux_keyring()
        }
        #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
        {
            KeyringStatus::Unavailable
        }
    }

    #[cfg(target_os = "linux")]
    fn check_linux_keyring() -> KeyringStatus {
        use std::path::Path;

        let has_dbus = Path::new("/usr/bin/dbus-daemon").exists()
            || Path::new("/bin/dbus-daemon").exists()
            || env::var("DBUS_SESSION_BUS_ADDRESS").is_ok();

        let has_keyring = Path::new("/usr/bin/gnome-keyring-daemon").exists()
            || Path::new("/usr/bin/gnome-keyring").exists()
            || Path::new("/usr/bin/kwalletd5").exists()
            || Path::new("/usr/bin/kwalletd6").exists()
            || env::var("SECRET_SERVICE").is_ok();

        if has_dbus && has_keyring {
            KeyringStatus::Available
        } else {
            KeyringStatus::Unavailable
        }
    }

    pub fn status(&self) -> KeyringStatus {
        self.status
    }

    pub fn is_available(&self) -> bool {
        self.status == KeyringStatus::Available
    }

    pub fn store_api_key(&self, provider: &str, api_key: &str) -> Result<()> {
        if !self.is_available() {
            bail!("Keyring is not available on this system");
        }

        let entry = keyring::Entry::new(SERVICE_NAME, provider)
            .context("Failed to create keyring entry")?;

        entry
            .set_password(api_key)
            .context("Failed to store API key")?;

        Ok(())
    }

    pub fn get_api_key(&self, provider: &str) -> Result<Option<String>> {
        if let Ok(key) = env::var(ENV_API_KEY)
            && !key.is_empty()
        {
            return Ok(Some(key));
        }

        if !self.is_available() {
            return Ok(None);
        }

        let entry = keyring::Entry::new(SERVICE_NAME, provider)
            .context("Failed to create keyring entry")?;

        match entry.get_password() {
            Ok(key) => Ok(Some(key)),
            Err(keyring::Error::NoEntry) => Ok(None),
            Err(e) => Err(e.into()),
        }
    }

    pub fn delete_api_key(&self, provider: &str) -> Result<()> {
        if !self.is_available() {
            bail!("Keyring is not available on this system");
        }

        let entry = keyring::Entry::new(SERVICE_NAME, provider)
            .context("Failed to create keyring entry")?;

        entry
            .delete_credential()
            .context("Failed to delete API key")?;

        Ok(())
    }

    pub fn has_api_key(&self, provider: &str) -> bool {
        self.get_api_key(provider).unwrap_or(None).is_some()
    }

    fn make_pat_service_name(profile_name: &str) -> String {
        format!("{}/{}", PAT_SERVICE_PREFIX, profile_name)
    }

    pub fn store_pat(
        &self,
        profile_name: &str,
        user_email: &str,
        service: &str,
        token: &str,
    ) -> Result<()> {
        if !self.is_available() {
            bail!("Keyring is not available on this system");
        }

        let keyring_service = Self::make_pat_service_name(profile_name);
        let keyring_user = format!("{}:{}", user_email, service);

        let entry = keyring::Entry::new(&keyring_service, &keyring_user)
            .context("Failed to create keyring entry for PAT")?;

        entry
            .set_password(token)
            .context("Failed to store PAT in keyring")?;

        eprintln!(
            "[DEBUG] PAT stored in keyring: service={}, user={}",
            keyring_service, keyring_user
        );

        Ok(())
    }

    pub fn get_pat(
        &self,
        profile_name: &str,
        user_email: &str,
        service: &str,
    ) -> Result<Option<String>> {
        if !self.is_available() {
            return Ok(None);
        }

        let keyring_service = Self::make_pat_service_name(profile_name);
        let keyring_user = format!("{}:{}", user_email, service);

        let entry = keyring::Entry::new(&keyring_service, &keyring_user)
            .context("Failed to create keyring entry for PAT")?;

        match entry.get_password() {
            Ok(token) => {
                eprintln!(
                    "[DEBUG] PAT retrieved from keyring: service={}, user={}",
                    keyring_service, keyring_user
                );
                Ok(Some(token))
            }
            Err(keyring::Error::NoEntry) => {
                eprintln!(
                    "[DEBUG] PAT not found in keyring: service={}, user={}",
                    keyring_service, keyring_user
                );
                Ok(None)
            }
            Err(e) => Err(e.into()),
        }
    }

    pub fn delete_pat(&self, profile_name: &str, user_email: &str, service: &str) -> Result<()> {
        if !self.is_available() {
            bail!("Keyring is not available on this system");
        }

        let keyring_service = Self::make_pat_service_name(profile_name);
        let keyring_user = format!("{}:{}", user_email, service);

        let entry = keyring::Entry::new(&keyring_service, &keyring_user)
            .context("Failed to create keyring entry for PAT")?;

        entry
            .delete_credential()
            .context("Failed to delete PAT from keyring")?;

        eprintln!(
            "[DEBUG] PAT deleted from keyring: service={}, user={}",
            keyring_service, keyring_user
        );

        Ok(())
    }

    pub fn has_pat(&self, profile_name: &str, user_email: &str, service: &str) -> bool {
        self.get_pat(profile_name, user_email, service)
            .unwrap_or(None)
            .is_some()
    }

    pub fn delete_all_pats_for_profile(
        &self,
        profile_name: &str,
        user_email: &str,
        services: &[String],
    ) -> Result<()> {
        for service in services {
            if let Err(e) = self.delete_pat(profile_name, user_email, service) {
                eprintln!(
                    "[DEBUG] Failed to delete PAT for service '{}': {}",
                    service, e
                );
            }
        }
        Ok(())
    }

    pub fn get_status_message(&self) -> String {
        match self.status {
            KeyringStatus::Available => {
                #[cfg(target_os = "windows")]
                {
                    "Windows Credential Manager is available".to_string()
                }
                #[cfg(target_os = "macos")]
                {
                    "macOS Keychain is available".to_string()
                }
                #[cfg(target_os = "linux")]
                {
                    "Linux secret service is available".to_string()
                }
                #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
                {
                    "Keyring is available".to_string()
                }
            }
            KeyringStatus::Unavailable => {
                "Keyring is not available. Set QUICOMMIT_API_KEY environment variable.".to_string()
            }
        }
    }
}

impl Default for KeyringManager {
    fn default() -> Self {
        Self::new()
    }
}

pub fn get_default_base_url(provider: &str) -> &'static str {
    match provider {
        "openai" => "https://api.openai.com/v1",
        "anthropic" => "https://api.anthropic.com/v1",
        "kimi" => "https://api.moonshot.cn/v1",
        "deepseek" => "https://api.deepseek.com/v1",
        "openrouter" => "https://openrouter.ai/api/v1",
        "ollama" => "http://localhost:11434",
        _ => "",
    }
}

pub fn get_default_model(provider: &str) -> &'static str {
    match provider {
        "openai" => "gpt-4",
        "anthropic" => "claude-3-sonnet-20240229",
        "kimi" => "kimi-k2.6",
        "deepseek" => "deepseek-v4-flash",
        "openrouter" => "openai/gpt-3.5-turbo",
        "ollama" => "llama2",
        _ => "",
    }
}

pub fn get_supported_providers() -> &'static [&'static str] {
    &[
        "ollama",
        "openai",
        "anthropic",
        "kimi",
        "deepseek",
        "openrouter",
    ]
}

pub fn provider_needs_api_key(provider: &str) -> bool {
    provider != "ollama"
}

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

    #[test]
    fn test_get_default_base_url() {
        assert_eq!(get_default_base_url("openai"), "https://api.openai.com/v1");
        assert_eq!(
            get_default_base_url("anthropic"),
            "https://api.anthropic.com/v1"
        );
        assert_eq!(get_default_base_url("kimi"), "https://api.moonshot.cn/v1");
        assert_eq!(
            get_default_base_url("deepseek"),
            "https://api.deepseek.com/v1"
        );
        assert_eq!(
            get_default_base_url("openrouter"),
            "https://openrouter.ai/api/v1"
        );
        assert_eq!(get_default_base_url("ollama"), "http://localhost:11434");
    }

    #[test]
    fn test_get_default_model() {
        assert_eq!(get_default_model("openai"), "gpt-4");
        assert_eq!(get_default_model("anthropic"), "claude-3-sonnet-20240229");
        assert_eq!(get_default_model("ollama"), "llama2");
    }

    #[test]
    fn test_provider_needs_api_key() {
        assert!(provider_needs_api_key("openai"));
        assert!(provider_needs_api_key("anthropic"));
        assert!(!provider_needs_api_key("ollama"));
    }
}
