//! The `export` sub-command.

use std::{
    fs::{read, write},
    path::PathBuf,
};

use clap::Parser;
use log::info;

use crate::{
    db::{Database, DatabaseError, EncryptedDatabase, EncryptedDatabaseError},
    Config, LeafCommand,
};

/// Export database in cleartext as JSON to the named file.
#[derive(Debug, Parser)]
pub struct ExportCommand {
    /// Write exported data to this file.
    filename: PathBuf,
}

impl LeafCommand for ExportCommand {
    type Error = ExportError;
    fn run(&self, config: &Config) -> Result<(), ExportError> {
        let db = EncryptedDatabase::new(config.sop(), config.database()).read_database()?;
        info!("loaded database OK");
        let json = serde_json::to_string_pretty(&db).map_err(ExportError::ToJson)?;
        write(&self.filename, &json)
            .map_err(|err| ExportError::WriteFile(self.filename.clone(), err))?;
        Ok(())
    }
}

/// Import data from an exported file.
///
/// This adds data to from the exported file without removing any. If
/// a value name is in both, the import replaces the existing value in
/// the database.
#[derive(Debug, Parser)]
pub struct ImportCommand {
    /// Read exported data from this file.
    filename: PathBuf,
}

impl LeafCommand for ImportCommand {
    type Error = ExportError;
    fn run(&self, config: &Config) -> Result<(), ExportError> {
        let encrypted = EncryptedDatabase::new(config.sop(), config.database());
        let mut db = encrypted.read_database()?;

        let data = read(&self.filename)
            .map_err(|err| ExportError::ReadFile(self.filename.clone(), err))?;
        let imported: Database = serde_json::from_slice(&data).map_err(ExportError::FromJson)?;
        db.import(&imported);

        encrypted.write_database(&db)?;

        Ok(())
    }
}

/// Possible errors from `export` sub-command.
#[derive(Debug, thiserror::Error)]
pub enum ExportError {
    /// Error from handling encrypted database.
    #[error(transparent)]
    Encrypted(#[from] EncryptedDatabaseError),

    /// Error from using cleartext database.
    #[error(transparent)]
    Database(#[from] DatabaseError),

    /// Can't serialize database as JSON.
    #[error("failed to serialize database as JSON")]
    ToJson(#[source] serde_json::Error),

    /// Can't de-serialize database from JSON.
    #[error("failed to parse exported data as JSON")]
    FromJson(#[source] serde_json::Error),

    /// Can't read file.
    #[error("failed to read exported data from file {0}")]
    ReadFile(PathBuf, #[source] std::io::Error),

    /// Can't write file.
    #[error("failed to write exported data to file {0}")]
    WriteFile(PathBuf, #[source] std::io::Error),
}
