// synops-feature-status — Sjekk feature-status.
//
// Samler informasjon om en feature fra fire kilder:
// 1. Spec-fil på disk (docs/features/ eller docs/concepts/)
// 2. Oppgavestatus fra tasks.md
// 3. Nylige git-commits som berører featuren
// 4. Ubesvart feedback fra databasen (kommunikasjonsnoder med discusses-edge)
//
// Output: markdown-formatert sammendrag til stdout.
//
// Miljøvariabler:
//   DATABASE_URL  — PostgreSQL-tilkobling (valgfri — feedback hoppes over uten)
//
// Ref: docs/retninger/unix_filosofi.md, docs/features/feature_feedback.md

use clap::Parser;
use regex::Regex;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

/// Sjekk feature-status: spec, oppgaver, commits og feedback.
#[derive(Parser)]
#[command(name = "synops-feature-status", about = "Sjekk feature-status")]
struct Cli {
    /// Feature-nøkkel (f.eks. "lydstudio", "chat", "publisering")
    feature_key: String,

    /// Antall commits å vise (default: 10)
    #[arg(long, default_value_t = 10)]
    commits: usize,

    /// Repo-rot (default: /home/vegard/synops)
    #[arg(long, default_value = "/home/vegard/synops")]
    repo: String,
}

fn main() {
    synops_common::logging::init("synops_feature_status");
    let cli = Cli::parse();
    let repo = PathBuf::from(&cli.repo);

    if !repo.join("tasks.md").exists() {
        eprintln!("Feil: Fant ikke tasks.md i {}", repo.display());
        process::exit(1);
    }

    let key = &cli.feature_key;

    println!("# Feature-status: {key}\n");

    // 1. Spec-sammendrag
    print_spec_summary(&repo, key);

    // 2. Oppgavestatus
    print_task_status(&repo, key);

    // 3. Nylige commits
    print_recent_commits(&repo, key, cli.commits);

    // 4. Ubesvart feedback (krever DATABASE_URL)
    run_feedback(key);
}

// --- Spec-sammendrag ---

fn find_spec_file(repo: &Path, key: &str) -> Option<PathBuf> {
    let key_lower = key.to_lowercase();
    let underscore = key_lower.replace('-', "_");
    let hyphen = key_lower.replace('_', "-");

    let candidates = [
        repo.join(format!("docs/features/{key_lower}.md")),
        repo.join(format!("docs/concepts/{key_lower}.md")),
        repo.join(format!("docs/features/{underscore}.md")),
        repo.join(format!("docs/concepts/{underscore}.md")),
        repo.join(format!("docs/features/{hyphen}.md")),
        repo.join(format!("docs/concepts/{hyphen}.md")),
    ];

    for path in &candidates {
        if path.exists() {
            return Some(path.clone());
        }
    }

    // Fuzzy: søk i begge mapper etter filer som inneholder nøkkelen
    for dir in &["docs/features", "docs/concepts"] {
        let dir_path = repo.join(dir);
        if let Ok(entries) = std::fs::read_dir(&dir_path) {
            for entry in entries.flatten() {
                let name = entry.file_name().to_string_lossy().to_lowercase();
                if name.ends_with(".md") && name.contains(&key_lower) {
                    return Some(entry.path());
                }
            }
        }
    }

    None
}

fn extract_summary(content: &str) -> String {
    let lines: Vec<&str> = content.lines().collect();
    let mut summary_parts: Vec<String> = Vec::new();

    // Hent tittelen (første # heading)
    for line in &lines {
        if line.starts_with("# ") {
            summary_parts.push(line.to_string());
            break;
        }
    }

    // Hent første avsnitt etter tittelen (eller etter frontmatter)
    let mut in_frontmatter = false;
    let mut past_title = false;
    let mut paragraph = Vec::new();

    for line in &lines {
        if line.starts_with("---") {
            in_frontmatter = !in_frontmatter;
            continue;
        }
        if in_frontmatter {
            continue;
        }
        if line.starts_with("# ") {
            past_title = true;
            continue;
        }
        if !past_title {
            continue;
        }

        let trimmed = line.trim();
        if trimmed.is_empty() {
            if !paragraph.is_empty() {
                break;
            }
            continue;
        }
        if trimmed.starts_with('#') {
            if !paragraph.is_empty() {
                break;
            }
            continue;
        }
        paragraph.push(trimmed.to_string());
    }

    if !paragraph.is_empty() {
        summary_parts.push(paragraph.join(" "));
    }

    // Hent ## headings som oversikt
    let headings: Vec<&str> = lines
        .iter()
        .filter(|l| l.starts_with("## "))
        .copied()
        .collect();

    if !headings.is_empty() {
        summary_parts.push(String::new());
        summary_parts.push("**Seksjoner:**".to_string());
        for h in &headings {
            summary_parts.push(format!("- {}", h.trim_start_matches("## ")));
        }
    }

    summary_parts.join("\n")
}

fn print_spec_summary(repo: &Path, key: &str) {
    println!("## Spec\n");

    match find_spec_file(repo, key) {
        Some(path) => {
            let rel_path = path
                .strip_prefix(repo)
                .unwrap_or(&path)
                .display()
                .to_string();

            match std::fs::read_to_string(&path) {
                Ok(content) => {
                    println!("Kilde: `{rel_path}`\n");
                    let summary = extract_summary(&content);
                    if summary.is_empty() {
                        println!("_(Tom spec-fil)_\n");
                    } else {
                        println!("{summary}\n");
                    }
                }
                Err(e) => println!("Kunne ikke lese {rel_path}: {e}\n"),
            }
        }
        None => {
            println!("Ingen spec-fil funnet for «{key}».");
            println!("Søkte i: docs/features/, docs/concepts/\n");
        }
    }
}

// --- Oppgavestatus ---

struct TaskInfo {
    phase: u32,
    phase_title: String,
    id: String,
    title: String,
    status: String,
    details: Vec<String>,
}

fn print_task_status(repo: &Path, key: &str) {
    println!("## Oppgaver\n");

    let tasks_path = repo.join("tasks.md");
    let content = match std::fs::read_to_string(&tasks_path) {
        Ok(c) => c,
        Err(e) => {
            println!("Kunne ikke lese tasks.md: {e}\n");
            return;
        }
    };

    let key_lower = key.to_lowercase();
    let search_terms: Vec<String> = vec![
        key_lower.clone(),
        key_lower.replace('-', "_"),
        key_lower.replace('_', "-"),
        key_lower.replace('_', " "),
        key_lower.replace('-', " "),
    ];

    let phase_re = Regex::new(r"^## Fase (\d+):\s*(.+)$").unwrap();
    let task_re = Regex::new(r"^- \[(.)\] (\d+\.\d+)\s+(.+)$").unwrap();
    let detail_re = Regex::new(r"^  > (.+)$").unwrap();

    let mut all_tasks: Vec<TaskInfo> = Vec::new();
    let mut current_phase: Option<(u32, String)> = None;

    for line in content.lines() {
        if let Some(caps) = phase_re.captures(line) {
            let num: u32 = caps[1].parse().unwrap_or(0);
            let title = caps[2].trim().to_string();
            current_phase = Some((num, title));
            continue;
        }

        if let Some(caps) = task_re.captures(line) {
            let status_char = &caps[1];
            let id = caps[2].to_string();
            let title = caps[3].to_string();

            let status = match status_char {
                " " => "[ ]",
                "~" => "[~]",
                "x" => "[x]",
                "?" => "[?]",
                "!" => "[!]",
                _ => "[?]",
            }
            .to_string();

            let (phase, phase_title) = current_phase
                .clone()
                .unwrap_or((0, "Ukjent".to_string()));

            all_tasks.push(TaskInfo {
                phase,
                phase_title,
                id,
                title,
                status,
                details: Vec::new(),
            });
            continue;
        }

        if let Some(caps) = detail_re.captures(line) {
            if let Some(task) = all_tasks.last_mut() {
                task.details.push(caps[1].to_string());
            }
        }
    }

    // Finn oppgaver som matcher feature-nøkkelen
    let mut matching: Vec<&TaskInfo> = all_tasks
        .iter()
        .filter(|t| {
            let title_lower = t.title.to_lowercase();
            let phase_lower = t.phase_title.to_lowercase();
            search_terms
                .iter()
                .any(|term| title_lower.contains(term) || phase_lower.contains(term))
        })
        .collect();

    // Også prøv synops-<key> hvis ingenting funnet
    if matching.is_empty() {
        let tool_name = format!("synops-{}", key_lower);
        matching = all_tasks
            .iter()
            .filter(|t| t.title.to_lowercase().contains(&tool_name))
            .collect();
    }

    if matching.is_empty() {
        println!("Ingen oppgaver funnet for «{key}».\n");
        return;
    }

    let total = matching.len();
    let done = matching.iter().filter(|t| t.status == "[x]").count();

    println!("Funnet {total} relaterte oppgaver ({done}/{total} ferdig):\n");

    let mut current_ph: Option<u32> = None;
    for task in &matching {
        if current_ph != Some(task.phase) {
            current_ph = Some(task.phase);
            println!("### Fase {}: {}\n", task.phase, task.phase_title);
        }
        println!("- {} {} {}", task.status, task.id, task.title);
        for detail in &task.details {
            println!("  > {detail}");
        }
    }
    println!();
}

// --- Git-commits ---

fn print_recent_commits(repo: &Path, key: &str, max: usize) {
    println!("## Nylige commits\n");

    let key_lower = key.to_lowercase();
    let search_patterns: Vec<String> = vec![
        key_lower.clone(),
        key_lower.replace('-', "_"),
        key_lower.replace('_', "-"),
        format!("synops-{}", key_lower),
    ];

    let file_patterns: Vec<String> = vec![
        format!("docs/features/{}*", key_lower),
        format!("docs/features/{}*", key_lower.replace('-', "_")),
        format!("docs/concepts/{}*", key_lower),
        format!("docs/concepts/{}*", key_lower.replace('-', "_")),
        format!("tools/synops-{}*", key_lower),
    ];

    let mut commits: Vec<(String, String, String)> = Vec::new();
    let mut seen_hashes: std::collections::HashSet<String> =
        std::collections::HashSet::new();

    // Commits fra filer
    for pattern in &file_patterns {
        if let Ok(output) = Command::new("git")
            .args([
                "log",
                "--oneline",
                "--format=%h|%ai|%s",
                &format!("-{max}"),
                "--",
                pattern,
            ])
            .current_dir(repo)
            .output()
        {
            let stdout = String::from_utf8_lossy(&output.stdout);
            parse_commit_lines(&stdout, &mut commits, &mut seen_hashes);
        }
    }

    // Commits fra meldinger (--grep)
    for term in &search_patterns {
        if let Ok(output) = Command::new("git")
            .args([
                "log",
                "--oneline",
                "--format=%h|%ai|%s",
                &format!("-{max}"),
                "-i",
                "--grep",
                term,
            ])
            .current_dir(repo)
            .output()
        {
            let stdout = String::from_utf8_lossy(&output.stdout);
            parse_commit_lines(&stdout, &mut commits, &mut seen_hashes);
        }
    }

    // Sorter etter dato (nyeste først)
    commits.sort_by(|a, b| b.1.cmp(&a.1));
    commits.truncate(max);

    if commits.is_empty() {
        println!("Ingen commits funnet for «{key}».\n");
        return;
    }

    println!("{} relevante commits:\n", commits.len());
    for (hash, date, msg) in &commits {
        let short_date = if date.len() >= 10 { &date[..10] } else { date };
        println!("- `{hash}` ({short_date}) {msg}");
    }
    println!();
}

fn parse_commit_lines(
    stdout: &str,
    commits: &mut Vec<(String, String, String)>,
    seen: &mut std::collections::HashSet<String>,
) {
    for line in stdout.lines() {
        let parts: Vec<&str> = line.splitn(3, '|').collect();
        if parts.len() == 3 && seen.insert(parts[0].to_string()) {
            commits.push((
                parts[0].to_string(),
                parts[1].to_string(),
                parts[2].to_string(),
            ));
        }
    }
}

// --- Feedback fra database ---

fn run_feedback(key: &str) {
    // Sikre at DATABASE_URL er satt — prøv maskinrommet.env som fallback
    if std::env::var("DATABASE_URL").is_err() {
        if let Ok(content) = std::fs::read_to_string("/tmp/maskinrommet.env") {
            for line in content.lines() {
                if let Some(val) = line.strip_prefix("DATABASE_URL=") {
                    // SAFETY: kalles tidlig i main, før noen tråder er startet.
                    unsafe { std::env::set_var("DATABASE_URL", val); }
                    break;
                }
            }
        }
    }

    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(print_feedback(key));
}

async fn print_feedback(key: &str) {
    println!("## Feedback\n");

    let db = match synops_common::db::connect().await {
        Ok(pool) => pool,
        Err(e) => {
            eprintln!("{e}");
            println!("_(DATABASE_URL ikke satt eller databasefeil — feedback hoppes over)_\n");
            return;
        }
    };

    // Finn spec-noder med matching feature_key
    let spec_nodes: Vec<(uuid::Uuid, Option<String>)> = sqlx::query_as(
        "SELECT id, title FROM nodes \
         WHERE metadata->>'feature_spec' = 'true' \
           AND LOWER(metadata->>'feature_key') = LOWER($1) \
         LIMIT 5",
    )
    .bind(key)
    .fetch_all(&db)
    .await
    .unwrap_or_default();

    if spec_nodes.is_empty() {
        println!("Ingen spec-node funnet i databasen for «{key}».");
        println!("Feature feedback-systemet er kanskje ikke satt opp for denne featuren.\n");
        return;
    }

    for (node_id, title) in &spec_nodes {
        let t = title.as_deref().unwrap_or("Uten tittel");
        println!("Spec-node: `{node_id}` — {t}");
        print_feedback_for_spec(&db, *node_id).await;
    }

    println!();
}

async fn print_feedback_for_spec(db: &sqlx::PgPool, spec_node_id: uuid::Uuid) {
    // Finn feedback-chatter (kommunikasjonsnoder med discusses-edge til spec)
    let feedback_chats: Vec<(uuid::Uuid, Option<String>)> = sqlx::query_as(
        "SELECT n.id, n.title \
         FROM nodes n \
         JOIN edges e ON e.source_id = n.id \
         WHERE e.target_id = $1 \
           AND e.edge_type = 'discusses' \
           AND n.node_kind = 'communication' \
         LIMIT 10",
    )
    .bind(spec_node_id)
    .fetch_all(db)
    .await
    .unwrap_or_default();

    if feedback_chats.is_empty() {
        println!("  Ingen feedback-chatter koblet til denne spec-noden.\n");
        return;
    }

    for (chat_id, chat_title) in &feedback_chats {
        let ct = chat_title.as_deref().unwrap_or("Feedback");
        println!("\n  Feedback-chat: `{chat_id}` — {ct}");

        // Finn ubesvarte meldinger: meldinger nyere enn siste Claude-svar
        let unanswered: Vec<(uuid::Uuid, Option<String>, chrono::DateTime<chrono::Utc>)> =
            sqlx::query_as(
                "SELECT n.id, \
                        LEFT(n.content, 200) AS excerpt, \
                        n.created_at \
                 FROM nodes n \
                 JOIN edges e ON e.source_id = n.id \
                 WHERE e.target_id = $1 \
                   AND e.edge_type = 'belongs_to' \
                   AND n.created_at > COALESCE( \
                       (SELECT MAX(n2.created_at) \
                        FROM nodes n2 \
                        JOIN edges e2 ON e2.source_id = n2.id \
                        WHERE e2.target_id = $1 \
                          AND e2.edge_type = 'belongs_to' \
                          AND n2.created_by = 'd3eebc99-9c0b-4ef8-bb6d-6bb9bd380a44'::uuid), \
                       '1970-01-01'::timestamptz) \
                   AND n.created_by != 'd3eebc99-9c0b-4ef8-bb6d-6bb9bd380a44'::uuid \
                 ORDER BY n.created_at DESC \
                 LIMIT 20",
            )
            .bind(chat_id)
            .fetch_all(db)
            .await
            .unwrap_or_default();

        if unanswered.is_empty() {
            println!("  Ingen ubesvart feedback.");
        } else {
            println!("  **{} ubesvarte meldinger:**", unanswered.len());
            for (msg_id, excerpt, created) in &unanswered {
                let date = created.format("%Y-%m-%d %H:%M");
                let text = excerpt
                    .as_deref()
                    .unwrap_or("(tomt)")
                    .replace('\n', " ");
                println!("  - `{msg_id}` ({date}): {text}");
            }
        }
    }
}
