use crate::doc::Doc;
use crate::error::{usage, Result};
use crate::project::Project;
use crate::refs;
use crate::Ctx;

pub fn run(ctx: &Ctx) -> Result<()> {
    let prj = ctx.open()?;
    // Drive the full sync (reconcile references + linkify + regenerate INDEX).
    crate::commands::sync::run(&prj)?;
    println!("synced {}", prj.base.join("INDEX.md").display());
    Ok(())
}

/// Regenerate the unified `INDEX.md` at the base, grouped by type. Errors if any
/// document fails to parse.
pub fn regenerate(prj: &Project) -> Result<()> {
    let (mut docs, errors) = prj.load_docs();
    if !errors.is_empty() {
        return Err(usage("fix parse errors first (run verify)"));
    }
    docs.sort_by(|a, b| {
        refs::id_number(a.id().unwrap_or("")).cmp(&refs::id_number(b.id().unwrap_or("")))
    });

    let pcfg = &prj.pcfg;
    let mut lines = vec![
        "# Index".to_string(),
        String::new(),
        "Generated by opys — do not edit.".to_string(),
        String::new(),
    ];
    for tname in pcfg.types.keys() {
        let group: Vec<&Doc> = docs
            .iter()
            .filter(|d| d.id().and_then(|id| pcfg.type_name_for_id(id)) == Some(tname.as_str()))
            .collect();
        if group.is_empty() {
            continue;
        }
        lines.push(format!("## {tname}"));
        lines.push(String::new());
        for d in group {
            lines.push(index_line(d));
        }
        lines.push(String::new());
    }
    std::fs::write(prj.base.join("INDEX.md"), lines.join("\n") + "\n")?;
    Ok(())
}

fn index_line(d: &Doc) -> String {
    let tags = d.frontmatter.tags().unwrap_or_default().join(", ");
    format!(
        "- {} [{}] ({}) {}",
        d.id().unwrap_or(""),
        d.status().unwrap_or(""),
        tags,
        d.title
    )
}
