use crate::daemon::SettingsKey;

use keyring::{Entry, Result};
use std::sync::Arc;
use tokio::sync::Mutex;

use kordophone::api::{http_client::Credentials, AuthenticationStore};
use kordophone_db::database::{Database, DatabaseAccess};

use async_trait::async_trait;

pub struct DatabaseAuthenticationStore {
    database: Arc<Mutex<Database>>,
}

impl DatabaseAuthenticationStore {
    pub fn new(database: Arc<Mutex<Database>>) -> Self {
        Self { database }
    }
}

#[async_trait]
impl AuthenticationStore for DatabaseAuthenticationStore {
    async fn get_credentials(&mut self) -> Option<Credentials> {
        self.database
            .lock()
            .await
            .with_settings(|settings| {
                let username: Option<String> = settings
                    .get::<String>(SettingsKey::USERNAME)
                    .unwrap_or_else(|e| {
                        log::warn!("error getting username from database: {}", e);
                        None
                    });

                match username {
                    Some(username) => {
                        let credential_res = Entry::new("net.buzzert.kordophonecd", &username);
                        let password: Result<String> = match credential_res {
                            Ok(credential) => credential.get_password(),
                            Err(e) => {
                                log::error!("error creating keyring credential: {}", e);
                                return None;
                            }
                        };

                        match password {
                            Ok(password) => Some(Credentials { username, password }),
                            Err(e) => {
                                log::error!("error getting password from keyring: {}", e);
                                None
                            }
                        }
                    }
                    None => None,
                }
            })
            .await
    }

    async fn get_token(&mut self) -> Option<String> {
        self.database
            .lock()
            .await
            .with_settings(
                |settings| match settings.get::<String>(SettingsKey::TOKEN) {
                    Ok(token) => token,
                    Err(e) => {
                        log::warn!("Failed to get token from settings: {}", e);
                        None
                    }
                },
            )
            .await
    }

    async fn set_token(&mut self, token: String) {
        self.database
            .lock()
            .await
            .with_settings(|settings| settings.put(SettingsKey::TOKEN, &token))
            .await
            .unwrap_or_else(|e| {
                log::error!("Failed to set token: {}", e);
            });
    }
}
