use tauri::AppHandle;

const KEYRING_SERVICE: &str = "thisper";
const KEYRING_ACCOUNT: &str = "gemini_api_key";

pub fn load_api_key(app: &AppHandle) -> Result<Option<String>, String> {
    backend::load_api_key(app)
}

pub fn save_api_key(app: &AppHandle, key: &str) -> Result<(), String> {
    backend::save_api_key(app, key)
}

pub fn storage_description() -> &'static str {
    backend::storage_description()
}

#[cfg(target_os = "windows")]
mod backend {
    use super::{KEYRING_ACCOUNT, KEYRING_SERVICE};
    use std::fs;
    use std::path::PathBuf;
    use tauri::{AppHandle, Manager};
    use windows_dpapi::{Scope, decrypt_data, encrypt_data};

    const API_KEY_FILE: &str = "gemini_api_key.dpapi";

    fn encrypted_file_path(app: &AppHandle) -> Result<PathBuf, String> {
        let dir = app
            .path()
            .app_local_data_dir()
            .map_err(|err| format!("Failed to resolve local app data directory: {err}"))?;
        Ok(dir.join("credentials").join(API_KEY_FILE))
    }

    fn load_legacy_keyring_value() -> Result<Option<String>, String> {
        let entry = keyring::Entry::new(KEYRING_SERVICE, KEYRING_ACCOUNT)
            .map_err(|err| format!("Failed to access legacy credential store: {err}"))?;

        match entry.get_password() {
            Ok(password) if !password.trim().is_empty() => Ok(Some(password)),
            Ok(_) => Ok(None),
            Err(keyring::Error::NoEntry) => Ok(None),
            Err(err) => Err(format!("Failed to read legacy credential store: {err}")),
        }
    }

    fn delete_legacy_keyring_value() {
        if let Ok(entry) = keyring::Entry::new(KEYRING_SERVICE, KEYRING_ACCOUNT) {
            let _ = entry.delete_credential();
        }
    }

    pub fn load_api_key(app: &AppHandle) -> Result<Option<String>, String> {
        let path = encrypted_file_path(app)?;
        if path.exists() {
            let encrypted = fs::read(&path)
                .map_err(|err| format!("Failed to read encrypted API key: {err}"))?;
            if encrypted.is_empty() {
                return Ok(None);
            }

            let decrypted = decrypt_data(&encrypted, Scope::User, None)
                .map_err(|err| format!("Failed to decrypt API key: {err}"))?;
            let key = String::from_utf8(decrypted)
                .map_err(|err| format!("Failed to decode decrypted API key: {err}"))?;

            if key.trim().is_empty() {
                return Ok(None);
            }

            return Ok(Some(key));
        }

        if let Some(legacy_key) = load_legacy_keyring_value()? {
            save_api_key(app, &legacy_key)?;
            delete_legacy_keyring_value();
            return Ok(Some(legacy_key));
        }

        Ok(None)
    }

    pub fn save_api_key(app: &AppHandle, key: &str) -> Result<(), String> {
        let path = encrypted_file_path(app)?;
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent)
                .map_err(|err| format!("Failed to create credential directory: {err}"))?;
        }

        let encrypted = encrypt_data(key.as_bytes(), Scope::User, None)
            .map_err(|err| format!("Failed to encrypt API key with Windows DPAPI: {err}"))?;

        fs::write(&path, encrypted)
            .map_err(|err| format!("Failed to write encrypted API key: {err}"))?;

        Ok(())
    }

    pub fn storage_description() -> &'static str {
        "Stored securely using Windows DPAPI in Thisper's local app data directory. The key is encrypted for your Windows user account and is never written in plaintext."
    }
}

#[cfg(not(target_os = "windows"))]
mod backend {
    use super::{KEYRING_ACCOUNT, KEYRING_SERVICE};
    use tauri::AppHandle;

    pub fn load_api_key(_app: &AppHandle) -> Result<Option<String>, String> {
        let entry = keyring::Entry::new(KEYRING_SERVICE, KEYRING_ACCOUNT)
            .map_err(|err| format!("Failed to access native credential storage: {err}"))?;

        match entry.get_password() {
            Ok(password) if !password.trim().is_empty() => Ok(Some(password)),
            Ok(_) => Ok(None),
            Err(keyring::Error::NoEntry) => Ok(None),
            Err(err) => Err(format!("Failed to read native credential storage: {err}")),
        }
    }

    pub fn save_api_key(_app: &AppHandle, key: &str) -> Result<(), String> {
        let entry = keyring::Entry::new(KEYRING_SERVICE, KEYRING_ACCOUNT)
            .map_err(|err| format!("Failed to access native credential storage: {err}"))?;
        entry
            .set_password(key)
            .map_err(|err| format!("Failed to save API key securely: {err}"))?;
        Ok(())
    }

    pub fn storage_description() -> &'static str {
        "Stored securely using the native platform credential store. Thisper never writes the key in plaintext."
    }
}
