use idxdb_store::IdxdbStore;
use miden_client::Word;
use miden_client::account::AccountFile as NativeAccountFile;
use miden_client::keystore::Keystore;
use miden_client::note::NoteId;
use wasm_bindgen::prelude::*;

use crate::models::account_file::AccountFile;
use crate::models::account_id::AccountId;
use crate::models::note_export_format::NoteExportFormat;
use crate::models::note_file::NoteFile;
use crate::{WebClient, js_error_with_context};

#[wasm_bindgen]
impl WebClient {
    #[wasm_bindgen(js_name = "exportNoteFile")]
    pub async fn export_note_file(
        &mut self,
        note_id: String,
        export_format: NoteExportFormat,
    ) -> Result<NoteFile, JsValue> {
        if let Some(client) = self.get_mut_inner() {
            let note_id = NoteId::from_raw(Word::try_from(note_id).map_err(|err| {
                js_error_with_context(
                    err,
                    "error exporting note file: failed to parse input note id",
                )
            })?);

            let output_note = client
                .get_output_note(note_id)
                .await
                .map_err(|err| {
                    js_error_with_context(
                        err,
                        "error exporting note file: failed to get output notes",
                    )
                })?
                .ok_or(JsValue::from_str("No output note found"))?;

            let export_type = export_format.into();

            let note_file = output_note.into_note_file(&export_type).map_err(|err| {
                js_error_with_context(err, "failed to convert output note to note file")
            })?;

            Ok(note_file.into())
        } else {
            Err(JsValue::from_str("Client not initialized"))
        }
    }

    #[wasm_bindgen(js_name = "exportAccountFile")]
    pub async fn export_account_file(
        &mut self,
        account_id: &AccountId,
    ) -> Result<AccountFile, JsValue> {
        let keystore = self.inner_keystore()?.clone();
        if let Some(client) = self.get_mut_inner() {
            let account = client
                .get_account(account_id.into())
                .await
                .map_err(|err| {
                    js_error_with_context(
                        err,
                        &format!(
                            "failed to get account for account id: {}",
                            account_id.to_string()
                        ),
                    )
                })?
                .ok_or_else(|| {
                    JsValue::from_str(&format!(
                        "Account with ID {} not found",
                        account_id.to_string()
                    ))
                })?;

            let key_pairs =
                keystore.get_keys_for_account(account_id.as_native()).await.map_err(|err| {
                    js_error_with_context(
                        err,
                        &format!("failed to get keys for account: {}", &account_id.to_string()),
                    )
                })?;

            let account_data = NativeAccountFile::new(account, key_pairs);

            Ok(AccountFile::from(account_data))
        } else {
            Err(JsValue::from_str("Client not initialized"))
        }
    }
}

/// Exports the entire contents of an `IndexedDB` store as a JSON string.
///
/// Use together with [`import_store`].
#[wasm_bindgen(js_name = "exportStore")]
pub async fn export_store(store_name: &str) -> Result<JsValue, JsValue> {
    let store = IdxdbStore::new(store_name.into())
        .await
        .map_err(|err| JsValue::from_str(&format!("failed to open store: {err:?}")))?;

    let json_string = store
        .export_store()
        .await
        .map_err(|err| JsValue::from_str(&format!("failed to export store: {err:?}")))?;

    Ok(JsValue::from_str(&json_string))
}
