//! `hephaes init` — cold-start build (plan §6). Autonomous by default;
//! `--dry-run` prints scope only, `--interactive` gates the build.

use super::{
    base_overrides, ensure_config, repo_root, run_metrics, verbosity, wiki_abs, with_spinner,
};
use crate::agent::{prompts, AgentTask};
use crate::cli::{GlobalArgs, InitArgs};
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, schema};
use crate::{providers, util};
use anyhow::Result;
use owo_colors::OwoColorize;

pub async fn run(global: &GlobalArgs, args: InitArgs) -> Result<()> {
    let root = repo_root(args.path.clone())?;
    let paths = Paths::new(&root);

    let mut ov = base_overrides(global);
    ov.include = args.include.clone();
    ov.exclude = args.exclude.clone();
    ov.max_file_bytes = args.max_file_bytes;

    // Scanning needs no provider, so do it before requiring config (dry-run is
    // fully offline).
    let cfg_probe = crate::config::resolve(&paths, &ov)?;
    let scan = {
        let cfg_for_scan = cfg_probe.clone().unwrap_or_else(|| default_scan_cfg(&ov));
        with_spinner("scanning repository", async {
            scanner::scan_root(&root, &cfg_for_scan)
        })
        .await?
    };

    if args.dry_run {
        print_plan(&root, &scan);
        return Ok(());
    }

    // Now require a configured provider (runs the selector if needed).
    let cfg = ensure_config(&paths, &ov, true).await?;
    let wiki = wiki_abs(&root, &cfg);
    let project = project_name(&root);

    println!(
        "Building wiki for {} → {}",
        project.bold(),
        cfg.wiki_dir.dimmed()
    );
    println!(
        "  {} files ({}), provider {}",
        scan.files.len(),
        category_line(&scan),
        cfg.provider.as_str().dimmed()
    );

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

    std::fs::create_dir_all(&wiki)?;
    util::write_text(&wiki.join("HEPHAES.md"), &schema::hephaes_md(&project))?;

    let repo_map = repomap::build(&scan, 400, 40);
    let provider = providers::build(&cfg, &paths, cfg.model_for(Op::Init))?;

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

    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();

    // hephaes owns index.md + log.md deterministically.
    index::rebuild(&wiki)?;
    let commit = diff::head_commit(&root);
    let manifest = Manifest::rebuild(&scan, &wiki, commit);
    manifest.save(&paths.manifest)?;

    let pages = index::list_pages(&wiki).len();
    log::append(
        &wiki,
        "ingest",
        &format!("init — {project}"),
        &format!(
            "Built {pages} page(s) from {} source file(s).",
            scan.files.len()
        ),
    )?;

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

    println!(
        "\n{} {} page(s) under {}/ · {}",
        "done".green().bold(),
        pages,
        cfg.wiki_dir,
        run_metrics(&outcome, elapsed).dimmed()
    );
    if global.verbose && !outcome.final_text.trim().is_empty() {
        println!(
            "\n{}",
            util::truncate_chars(outcome.final_text.trim(), 1200)
        );
    }
    Ok(())
}

fn default_scan_cfg(ov: &crate::config::Overrides) -> crate::config::Config {
    crate::config::Config {
        provider: crate::config::ProviderId::Claude, // unused for scanning
        model: None,
        models: Default::default(),
        wiki_dir: ov
            .wiki_dir
            .clone()
            .unwrap_or_else(|| crate::config::DEFAULT_WIKI_DIR.to_string()),
        ollama_url: crate::config::DEFAULT_OLLAMA_URL.to_string(),
        concurrency: 2,
        include: ov.include.clone(),
        exclude: ov.exclude.clone(),
        max_file_bytes: ov
            .max_file_bytes
            .unwrap_or(crate::config::DEFAULT_MAX_FILE_BYTES),
    }
}

fn project_name(root: &std::path::Path) -> String {
    root.file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("project")
        .to_string()
}

fn category_line(scan: &scanner::Scan) -> String {
    scan.count_by_category()
        .iter()
        .map(|(c, n)| format!("{n} {}", c.label()))
        .collect::<Vec<_>>()
        .join(", ")
}

fn print_plan(root: &std::path::Path, scan: &scanner::Scan) {
    println!("{}", "init plan (dry run)".bold());
    println!("repository: {}", root.display());
    println!("files:      {} ({})", scan.files.len(), category_line(scan));
    let langs = scan.languages();
    if !langs.is_empty() {
        let s = langs
            .iter()
            .take(8)
            .map(|(l, n)| format!("{l} {n}"))
            .collect::<Vec<_>>()
            .join(", ");
        println!("languages:  {s}");
    }
    println!(
        "\nWould build: overview, module pages (per major area), concept/flow pages, glossary,\n\
         plus index.md and log.md. Re-run without --dry-run to build."
    );
}
