//! `hephaes update` — incremental reconciliation (plan §7). Change detection is
//! by content hash against the manifest, so it works with or without git.

use super::{base_overrides, ensure_config, repo_root, run_metrics, verbosity, wiki_abs};
use crate::agent::{prompts, AgentTask};
use crate::cli::{GlobalArgs, UpdateArgs};
use crate::config::{Op, Paths};
use crate::progress_term::TerminalProgress;
use crate::repo::{diff, repomap, scanner};
use crate::state::Manifest;
use crate::wiki::{index, log};
use crate::{providers, util};
use anyhow::{bail, Result};
use owo_colors::OwoColorize;

pub async fn run(global: &GlobalArgs, args: UpdateArgs) -> Result<()> {
    let root = repo_root(args.path.clone())?;
    let paths = Paths::new(&root);
    let ov = base_overrides(global);
    let cfg = ensure_config(&paths, &ov, true).await?;
    let wiki = wiki_abs(&root, &cfg);

    if !wiki.exists() {
        bail!(
            "no wiki found at {} — run `hephaes init` first",
            cfg.wiki_dir
        );
    }

    let scan = scanner::scan_root(&root, &cfg)?;
    let manifest = Manifest::load(&paths.manifest);
    if manifest.sources.is_empty() {
        bail!("no manifest found — run `hephaes init` to build the wiki first");
    }

    let cs = manifest.diff_against(&scan);
    if cs.is_empty() {
        println!("{} wiki is up to date.", "✓".green());
        return Ok(());
    }

    let lookup: Vec<String> = cs
        .added
        .iter()
        .chain(cs.modified.iter())
        .chain(cs.deleted.iter())
        .cloned()
        .collect();
    let affected = manifest.affected_pages(&lookup);

    println!(
        "Changes since last build: {} added, {} modified, {} deleted; {} page(s) likely affected.",
        cs.added.len(),
        cs.modified.len(),
        cs.deleted.len(),
        affected.len()
    );

    if args.interactive && util::is_interactive() {
        let proceed = inquire::Confirm::new("Apply updates?")
            .with_default(true)
            .prompt()
            .unwrap_or(false);
        if !proceed {
            println!("Aborted.");
            return Ok(());
        }
    }

    let repo_map = repomap::build(&scan, 400, 40);
    let provider = providers::build(&cfg, &paths, cfg.model_for(Op::Update))?;
    let project = root
        .file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("project")
        .to_string();

    let task = AgentTask {
        system: prompts::system_prompt(&project),
        instruction: prompts::update_instruction(&cs, &affected),
        context: String::new(),
        repo_map,
        repo_root: root.clone(),
        wiki_dir: wiki.clone(),
        model: cfg.model_for(Op::Update),
        read_only: false,
        max_iters: 60,
    };

    let reporter = TerminalProgress::new(verbosity(global), util::is_interactive());
    let started = std::time::Instant::now();
    let outcome = provider.run_agent(&task, &reporter).await?;
    reporter.finish();
    let elapsed = started.elapsed();

    index::rebuild(&wiki)?;
    let commit = diff::head_commit(&root);
    let manifest = Manifest::rebuild(&scan, &wiki, commit);
    manifest.save(&paths.manifest)?;

    log::append(
        &wiki,
        "update",
        &format!("update — {project}"),
        &format!(
            "{} added, {} modified, {} deleted.",
            cs.added.len(),
            cs.modified.len(),
            cs.deleted.len()
        ),
    )?;

    if args.commit {
        let _ = diff::commit_wiki(&root, &cfg.wiki_dir, "hephaes: update wiki");
    }

    println!(
        "\n{} wiki updated · {}",
        "done".green().bold(),
        run_metrics(&outcome, elapsed).dimmed()
    );
    if global.verbose && !outcome.final_text.trim().is_empty() {
        println!(
            "\n{}",
            util::truncate_chars(outcome.final_text.trim(), 1000)
        );
    }
    Ok(())
}
