use crate::keyutil;
use crate::storage::Storage;
use bip39::Mnemonic;
use chrono::{DateTime, Utc};
use heed::types::{SerdeRmp, Str};
use heed::{Database, RoTxn};
use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize};
use std::error::Error;

#[derive(Debug, Serialize, Deserialize)]
pub struct NostrKeys<'a> {
    #[serde(borrow)]
    pub sec_key_hex: &'a str,
    pub datetime_created: DateTime<Utc>,
    pub active: bool,
    pub flags: u32,
}

type KeysDatabase<'a> = Database<Str, SerdeRmp<NostrKeys<'a>>>;

const DB_NAME: &str = "nostr_keys";
const KEYS_DEFAULT: &str = "default";

impl Storage {
    fn keys_db(
        &self,
    ) -> Result<KeysDatabase<'_>, Box<dyn Error + Send + Sync>> {
        let rtxn = self.get_read_txn()?;
        let dbo: Option<KeysDatabase> =
            self.env.open_database(&rtxn, Some(DB_NAME))?;

        if dbo.is_some() {
            Ok(dbo.unwrap())
        } else {
            Err(Box::from("Error opening nostr keys db"))
        }
    }

    pub fn create_keys_db(
        &self,
    ) -> Result<KeysDatabase<'_>, Box<dyn Error + Send + Sync>> {
        let mut wtxn = self.env.write_txn()?;
        let db: KeysDatabase =
            self.env.create_database(&mut wtxn, Some(DB_NAME))?;
        wtxn.commit()?;
        Ok(db)
    }

    pub fn get_keys(
        &self,
        rtxn: &mut RoTxn,
        idx: &str,
    ) -> Result<Keys, Box<dyn Error + Send + Sync>> {
        return match self.keys_db()?.get(rtxn, idx)? {
            Some(k) => {
                if let Ok(keys) = Keys::parse(k.sec_key_hex) {
                    return Ok(keys);
                }
                Err(Box::from("Cannot parse key!"))
            }
            None => Err(Box::from("No key")),
        };
    }

    pub fn get_default_keys(
        &self,
        rtxn: &mut RoTxn,
    ) -> Result<Keys, Box<dyn Error + Send + Sync>> {
        self.get_keys(rtxn, KEYS_DEFAULT)
    }

    pub fn create_default_keys(
        &self,
    ) -> Result<Keys, Box<dyn Error + Send + Sync>> {
        self.create_keys(KEYS_DEFAULT)
    }

    pub fn create_keys<'c>(
        &self,
        name: &str,
    ) -> Result<Keys, Box<dyn Error + Send + Sync>> {
        if let Ok(db) = self.keys_db() {
            let mut wtxn = self.env.write_txn()?;

            let keys = Keys::generate();
            let ks = keys.secret_key().to_secret_hex().clone();

            let k = NostrKeys {
                sec_key_hex: ks.as_str(),
                datetime_created: Utc::now(),
                active: true,
                flags: 0,
            };

            db.put(&mut wtxn, name, &k)?;
            wtxn.commit()?;

            Ok(keys)
        } else {
            Err(Box::from("No keys database"))
        }
    }

    pub fn create_keys_with_mnemonic<'c>(
        &self,
        name: &str,
    ) -> Result<(Keys, Mnemonic), Box<dyn Error + Send + Sync>> {
        if let Ok(db) = self.keys_db() {
            let mut wtxn = self.env.write_txn()?;
            let (keys, mnemo) = keyutil::genkeys_with_mnemonic().unwrap();

            let ks = keys.secret_key().to_secret_hex().clone();

            let k = NostrKeys {
                sec_key_hex: ks.as_str(),
                datetime_created: Utc::now(),
                active: true,
                flags: 0,
            };

            db.put(&mut wtxn, name, &k)?;
            wtxn.commit()?;

            Ok((keys, mnemo))
        } else {
            Err(Box::from("No keys db"))
        }
    }
}
