//! `relicario import` — currently only LastPass CSV is supported.

use std::path::PathBuf;

use anyhow::{bail, Context, Result};

use crate::ImportAction;

pub fn cmd_import(action: ImportAction) -> Result<()> {
    match action {
        ImportAction::Lastpass { csv } => cmd_import_lastpass(csv),
    }
}

fn cmd_import_lastpass(csv_path: PathBuf) -> Result<()> {
    use std::fs;
    use relicario_core::import_lastpass::parse_lastpass_csv;

    let csv_bytes = fs::read(&csv_path)
        .with_context(|| format!("failed to read CSV {}", csv_path.display()))?;

    let (items, warnings) = parse_lastpass_csv(&csv_bytes)?;

    if items.is_empty() {
        // Print all warnings so the user sees why nothing imported.
        for w in &warnings {
            print_warning(w);
        }
        bail!(
            "imported 0 items from {} — see warnings above",
            csv_path.display()
        );
    }

    let vault = crate::session::UnlockedVault::unlock_interactive()?;
    let mut manifest = vault.load_manifest()?;

    let total = items.len();
    let mut written_paths: Vec<String> = Vec::with_capacity(items.len() + 1);

    for (idx, item) in items.iter().enumerate() {
        vault.save_item(item)?;
        manifest.upsert(item);
        written_paths.push(format!("items/{}.enc", item.id.as_str()));

        let n = idx + 1;
        if n % 50 == 0 || n == total {
            eprintln!("[{n}/{total}] importing...");
        }
    }

    vault.after_manifest_change(&manifest)?;
    written_paths.push("manifest.enc".into());

    let path_refs: Vec<&str> = written_paths.iter().map(String::as_str).collect();
    let csv_filename = csv_path
        .file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("lastpass.csv");
    super::commit_paths(
        &vault,
        &format!("import: {} items from LastPass ({})", total, csv_filename),
        &path_refs,
    )?;

    for w in &warnings {
        print_warning(w);
    }
    // Counts only true skips, not partial imports. Coupled by convention to
    // the parser's warning message strings: skip messages end in "— skipped",
    // partial-import messages say "imported without TOTP" / "imported without URL".
    // If a future warning uses the word "skipped" in any other sense, this filter
    // will need to switch to an enum tag (see ImportWarning::message).
    eprintln!(
        "Imported {}, skipped {} (see warnings above)",
        total,
        warnings.iter().filter(|w| w.message.contains("skipped")).count()
    );
    Ok(())
}

fn print_warning(w: &relicario_core::import_lastpass::ImportWarning) {
    let prefix = match &w.title {
        Some(t) => format!("row {} ({}):", w.row, t),
        None => format!("row {}:", w.row),
    };
    eprintln!("warning: {prefix} {}", w.message);
}
