//! Trash umbrella: `rm` (soft-delete), `restore`, `purge` (permanent),
//! `trash list` / `trash empty`.

use anyhow::Result;

use crate::TrashAction;

pub fn cmd_rm(query: String) -> Result<()> {
    let vault = crate::session::UnlockedVault::unlock_interactive()?;
    let mut manifest = vault.load_manifest()?;
    let entry = super::resolve_query(&manifest, &query)?;
    let id = entry.id.clone();
    let _ = entry;
    let mut item = vault.load_item(&id)?;
    item.soft_delete();
    vault.save_item(&item)?;
    manifest.upsert(&item);
    vault.after_manifest_change(&manifest)?;
    super::commit_paths(&vault, &format!("trash: {} ({})", crate::helpers::sanitize_for_commit(&item.title), item.id.as_str()),
                 &[&format!("items/{}.enc", item.id.as_str()), "manifest.enc"])?;
    eprintln!("Moved to trash: {}", item.title);
    Ok(())
}

pub fn cmd_restore(query: String) -> Result<()> {
    let vault = crate::session::UnlockedVault::unlock_interactive()?;
    let mut manifest = vault.load_manifest()?;
    let entry = super::resolve_query(&manifest, &query)?;
    let id = entry.id.clone();
    let _ = entry;
    let mut item = vault.load_item(&id)?;
    item.restore();
    vault.save_item(&item)?;
    manifest.upsert(&item);
    vault.after_manifest_change(&manifest)?;
    super::commit_paths(&vault, &format!("restore: {} ({})", crate::helpers::sanitize_for_commit(&item.title), item.id.as_str()),
                 &[&format!("items/{}.enc", item.id.as_str()), "manifest.enc"])?;
    eprintln!("Restored: {}", item.title);
    Ok(())
}

/// Filesystem-only purge: removes the item.enc, attachments/<id>/, and updates
/// the manifest in memory. Returns the relative paths the caller must stage
/// via `git rm` after the loop. Does NOT invoke any git commands — the caller
/// batches them.
pub(super) fn purge_item_filesystem(
    vault: &crate::session::UnlockedVault,
    manifest: &mut relicario_core::Manifest,
    id: &relicario_core::ItemId,
    title: &str,
) -> Result<Vec<String>> {
    use std::{fs, io::ErrorKind};

    let item_rel = format!("items/{}.enc", id.as_str());
    let att_rel = format!("attachments/{}", id.as_str());

    let ignore_missing = |r: std::io::Result<()>| -> Result<()> {
        match r {
            Ok(()) => Ok(()),
            Err(e) if e.kind() == ErrorKind::NotFound => Ok(()),
            Err(e) => Err(e.into()),
        }
    };
    ignore_missing(fs::remove_file(vault.item_path(id)))?;
    ignore_missing(fs::remove_dir_all(vault.root().join("attachments").join(id.as_str())))?;
    manifest.remove(id);

    eprintln!("Purged: {title}");
    Ok(vec![item_rel, att_rel])
}

pub fn cmd_purge(query: String) -> Result<()> {
    let vault = crate::session::UnlockedVault::unlock_interactive()?;
    let mut manifest = vault.load_manifest()?;
    let entry = super::resolve_query(&manifest, &query)?;
    let id = entry.id.clone();
    let title = entry.title.clone();
    let _ = entry;

    let paths = purge_item_filesystem(&vault, &mut manifest, &id, &title)?;
    vault.after_manifest_change(&manifest)?;

    let purge_ctx = format!("purge \"{}\" ({})", title, id.as_str());
    crate::helpers::git_rm(vault.root(), &paths, &format!("{purge_ctx}: git rm"))?;
    crate::helpers::git_run(
        vault.root(),
        &["add", "manifest.enc"],
        &format!("{purge_ctx}: git add manifest.enc"),
    )?;
    crate::helpers::git_run(
        vault.root(),
        &["commit", "-m", &format!("purge: {} ({})", title, id.as_str())],
        &format!("{purge_ctx}: git commit"),
    )?;
    Ok(())
}

pub fn cmd_trash(action: TrashAction) -> Result<()> {
    match action {
        TrashAction::List => super::list::cmd_list(None, None, None, true),
        TrashAction::Empty => cmd_trash_empty(),
    }
}

pub fn cmd_trash_empty() -> Result<()> {
    use relicario_core::time::now_unix;

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

    let purgeable: Vec<_> = manifest.items.values()
        .filter(|e| match e.trashed_at {
            Some(t) => settings.trash_retention.should_purge(t, now),
            None => false,
        })
        .map(|e| (e.id.clone(), e.title.clone()))
        .collect();

    if purgeable.is_empty() {
        eprintln!("nothing past retention window");
        return Ok(());
    }

    let mut all_paths: Vec<String> = Vec::new();
    let purged_count = purgeable.len();
    for (id, title) in purgeable {
        let mut paths = purge_item_filesystem(&vault, &mut manifest, &id, &title)?;
        all_paths.append(&mut paths);
    }

    vault.after_manifest_change(&manifest)?;

    crate::helpers::git_rm(vault.root(), &all_paths, "trash empty: git rm")?;
    crate::helpers::git_run(
        vault.root(),
        &["add", "manifest.enc"],
        "trash empty: git add manifest.enc",
    )?;
    crate::helpers::git_run(
        vault.root(),
        &["commit", "-m", &format!("trash empty: purged {} item(s)", purged_count)],
        "trash empty: git commit",
    )?;

    eprintln!("Emptied trash: {} item(s)", purged_count);
    Ok(())
}
