// synops-context — Hent kontekst for en samtale (kommunikasjonsnode).
//
// Samler all relevant informasjon om en samtale i ett markdown-dokument:
// tittel, deltakere, spesifikasjon (discusses-edge), meldingshistorikk,
// og relaterte noder. Designet for å gi Claude (eller andre konsumenter)
// komplett kontekst for å delta i en samtale.
//
// Output: markdown til stdout.
// Logging: structured tracing til stderr.
//
// Miljøvariabler:
//   DATABASE_URL  — PostgreSQL-tilkobling (påkrevd)
//
// Ref: docs/retninger/unix_filosofi.md, docs/infra/claude_agent.md

use clap::Parser;
use std::process;
use uuid::Uuid;

/// Hent kontekst for en kommunikasjonsnode (samtale).
#[derive(Parser)]
#[command(name = "synops-context", about = "Hent kontekst for en samtale")]
struct Cli {
    /// Kommunikasjonsnode-ID
    #[arg(long)]
    communication_id: Uuid,

    /// Maks antall meldinger i historikken (default: 50)
    #[arg(long, default_value_t = 50)]
    max_messages: i64,
}

// --- Database-rader ---

#[derive(sqlx::FromRow)]
struct NodeRow {
    #[allow(dead_code)]
    id: Uuid,
    title: Option<String>,
    #[allow(dead_code)]
    content: Option<String>,
    node_kind: String,
    visibility: String,
    metadata: Option<serde_json::Value>,
    created_at: chrono::DateTime<chrono::Utc>,
    #[allow(dead_code)]
    created_by: Uuid,
}

#[derive(sqlx::FromRow)]
struct ParticipantRow {
    id: Uuid,
    title: Option<String>,
    node_kind: String,
    edge_type: String,
}

#[derive(sqlx::FromRow)]
struct MessageRow {
    #[allow(dead_code)]
    id: Uuid,
    content: Option<String>,
    created_by: Uuid,
    created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(sqlx::FromRow)]
struct RelatedNodeRow {
    #[allow(dead_code)]
    id: Uuid,
    title: Option<String>,
    node_kind: String,
    edge_type: String,
    direction: String,
}

#[tokio::main]
async fn main() {
    synops_common::logging::init("synops_context");

    let cli = Cli::parse();

    if let Err(e) = run(cli).await {
        eprintln!("Feil: {e}");
        process::exit(1);
    }
}

async fn run(cli: Cli) -> Result<(), String> {
    let db = synops_common::db::connect().await?;

    let comm_id = cli.communication_id;

    // 1. Hent kommunikasjonsnoden
    let comm: NodeRow = sqlx::query_as(
        "SELECT id, title, content, node_kind::text, visibility::text, metadata, created_at, created_by \
         FROM nodes WHERE id = $1",
    )
    .bind(comm_id)
    .fetch_optional(&db)
    .await
    .map_err(|e| format!("DB-feil: {e}"))?
    .ok_or_else(|| format!("Kommunikasjonsnode {comm_id} finnes ikke"))?;

    if comm.node_kind != "communication" {
        return Err(format!(
            "Node {comm_id} er '{}', ikke 'communication'",
            comm.node_kind
        ));
    }

    // 2. Hent deltakere (owner, member_of, admin, reader)
    let participants = sqlx::query_as::<_, ParticipantRow>(
        "SELECT n.id, n.title, n.node_kind::text as node_kind, e.edge_type \
         FROM nodes n JOIN edges e ON e.source_id = n.id \
         WHERE e.target_id = $1 AND e.edge_type IN ('owner', 'member_of', 'admin', 'reader')",
    )
    .bind(comm_id)
    .fetch_all(&db)
    .await
    .map_err(|e| format!("DB-feil (deltakere): {e}"))?;

    // 3. Hent spesifikasjon (discusses-edge)
    let spec_content: Option<String> = sqlx::query_scalar::<_, Option<String>>(
        "SELECT n.content FROM nodes n \
         JOIN edges e ON e.source_id = $1 AND e.target_id = n.id \
         WHERE e.edge_type = 'discusses' LIMIT 1",
    )
    .bind(comm_id)
    .fetch_optional(&db)
    .await
    .map_err(|e| format!("DB-feil (spec): {e}"))?
    .flatten();

    // 4. Hent meldingshistorikk
    let mut messages = sqlx::query_as::<_, MessageRow>(
        "SELECT n.id, n.content, n.created_by, 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.node_kind = 'content' \
         ORDER BY n.created_at DESC LIMIT $2",
    )
    .bind(comm_id)
    .bind(cli.max_messages)
    .fetch_all(&db)
    .await
    .map_err(|e| format!("DB-feil (meldinger): {e}"))?;

    messages.reverse();

    // 5. Hent relaterte noder (andre edges enn belongs_to og deltaker-edges)
    let related = sqlx::query_as::<_, RelatedNodeRow>(
        "SELECT n.id, n.title, n.node_kind::text as node_kind, e.edge_type, 'ut' as direction \
         FROM nodes n JOIN edges e ON e.target_id = n.id \
         WHERE e.source_id = $1 AND e.edge_type NOT IN ('owner', 'member_of', 'admin', 'reader') \
         UNION ALL \
         SELECT n.id, n.title, n.node_kind::text as node_kind, e.edge_type, 'inn' as direction \
         FROM nodes n JOIN edges e ON e.source_id = n.id \
         WHERE e.target_id = $1 AND e.edge_type NOT IN ('belongs_to', 'owner', 'member_of', 'admin', 'reader')",
    )
    .bind(comm_id)
    .bind(comm_id)
    .fetch_all(&db)
    .await
    .map_err(|e| format!("DB-feil (relaterte): {e}"))?;

    // --- Bygg markdown ---
    let mut md = String::new();

    // Tittel og metadata
    let title = comm.title.as_deref().unwrap_or("Uten tittel");
    md.push_str(&format!("# {title}\n\n"));
    md.push_str(&format!("- **Type:** {}\n", comm.node_kind));
    md.push_str(&format!("- **Synlighet:** {}\n", comm.visibility));
    md.push_str(&format!(
        "- **Opprettet:** {}\n",
        comm.created_at.format("%Y-%m-%d %H:%M")
    ));

    if let Some(meta) = &comm.metadata {
        if let Some(started) = meta.get("started_at").and_then(|v| v.as_str()) {
            md.push_str(&format!("- **Startet:** {started}\n"));
        }
        if let Some(ended) = meta.get("ended_at").and_then(|v| v.as_str()) {
            md.push_str(&format!("- **Avsluttet:** {ended}\n"));
        }
    }

    // Deltakere
    md.push_str("\n## Deltakere\n\n");
    if participants.is_empty() {
        md.push_str("_Ingen deltakere registrert._\n");
    } else {
        // Bygg navn-map for meldingshistorikken
        let name_map: std::collections::HashMap<Uuid, String> = participants
            .iter()
            .map(|p| {
                (
                    p.id,
                    p.title.clone().unwrap_or_else(|| p.node_kind.clone()),
                )
            })
            .collect();

        for p in &participants {
            let name = p.title.as_deref().unwrap_or("Ukjent");
            let role = match p.edge_type.as_str() {
                "owner" => "eier",
                "admin" => "admin",
                "member_of" => "deltaker",
                "reader" => "leser",
                other => other,
            };
            md.push_str(&format!(
                "- **{name}** ({}, {role})\n",
                p.node_kind
            ));
        }

        // Spesifikasjon
        if let Some(ref spec) = spec_content {
            if !spec.is_empty() {
                md.push_str("\n## Spesifikasjon\n\n");
                md.push_str(spec);
                md.push('\n');
            }
        }

        // Meldingshistorikk
        md.push_str(&format!(
            "\n## Meldingshistorikk ({} meldinger)\n\n",
            messages.len()
        ));
        if messages.is_empty() {
            md.push_str("_Ingen meldinger._\n");
        } else {
            for m in &messages {
                let name = name_map
                    .get(&m.created_by)
                    .map(|s| s.as_str())
                    .unwrap_or("Ukjent");
                let ts = m.created_at.format("%H:%M");
                let content = m.content.as_deref().unwrap_or("");
                md.push_str(&format!("**{name}** ({ts}):\n{content}\n\n"));
            }
        }

        // Relaterte noder
        if !related.is_empty() {
            md.push_str("## Relaterte noder\n\n");
            for r in &related {
                let name = r.title.as_deref().unwrap_or("Uten tittel");
                let arrow = match r.direction.as_str() {
                    "ut" => "→",
                    "inn" => "←",
                    _ => "-",
                };
                md.push_str(&format!(
                    "- {arrow} **{name}** ({}, {})\n",
                    r.node_kind, r.edge_type
                ));
            }
        }
    }

    tracing::info!(
        communication_id = %comm_id,
        participants = participants.len(),
        messages = messages.len(),
        related = related.len(),
        "Kontekst generert"
    );

    print!("{md}");

    Ok(())
}
