// synops-search — Fulltekstsøk i grafen.
//
// Bruker PostgreSQL sin tsvector/tsquery med norsk tekstkonfigurasjon
// for å søke i noder (title vektet A, content vektet B).
// Migration 011 setter opp search_vector-kolonnen og GIN-indeks.
//
// Output: matchende noder med relevans-score og utdrag til stdout.
// Logging: structured tracing til stderr.
//
// Miljøvariabler:
//   DATABASE_URL  — PostgreSQL-tilkobling (påkrevd)
//
// Ref: docs/retninger/unix_filosofi.md, migrations/011_fulltext_search.sql

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

/// Fulltekstsøk i grafen (noder).
#[derive(Parser)]
#[command(name = "synops-search", about = "Fulltekstsøk i noder")]
struct Cli {
    /// Søkestreng (fritekst)
    query: String,

    /// Filtrer på node_kind (f.eks. content, communication, task)
    #[arg(long)]
    kind: Option<String>,

    /// Maks antall resultater (default: 20)
    #[arg(long, default_value_t = 20)]
    limit: i64,
}

#[derive(sqlx::FromRow)]
struct SearchResult {
    id: Uuid,
    node_kind: String,
    title: Option<String>,
    visibility: String,
    created_at: chrono::DateTime<chrono::Utc>,
    rank: f32,
    headline: Option<String>,
}

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

    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 query = &cli.query;

    // Bygg SQL dynamisk basert på om --kind er satt
    let results = if let Some(ref kind) = cli.kind {
        sqlx::query_as::<_, SearchResult>(
            "SELECT \
                n.id, \
                n.node_kind::text, \
                n.title, \
                n.visibility::text, \
                n.created_at, \
                ts_rank(n.search_vector, websearch_to_tsquery('norwegian', $1)) AS rank, \
                ts_headline('norwegian', COALESCE(n.content, ''), websearch_to_tsquery('norwegian', $1), \
                    'MaxWords=35, MinWords=15, MaxFragments=1, StartSel=«, StopSel=»') AS headline \
             FROM nodes n \
             WHERE n.search_vector @@ websearch_to_tsquery('norwegian', $1) \
               AND n.node_kind = $2 \
             ORDER BY rank DESC \
             LIMIT $3",
        )
        .bind(query)
        .bind(kind)
        .bind(cli.limit)
        .fetch_all(&db)
        .await
        .map_err(|e| format!("Søkefeil: {e}"))?
    } else {
        sqlx::query_as::<_, SearchResult>(
            "SELECT \
                n.id, \
                n.node_kind::text, \
                n.title, \
                n.visibility::text, \
                n.created_at, \
                ts_rank(n.search_vector, websearch_to_tsquery('norwegian', $1)) AS rank, \
                ts_headline('norwegian', COALESCE(n.content, ''), websearch_to_tsquery('norwegian', $1), \
                    'MaxWords=35, MinWords=15, MaxFragments=1, StartSel=«, StopSel=»') AS headline \
             FROM nodes n \
             WHERE n.search_vector @@ websearch_to_tsquery('norwegian', $1) \
             ORDER BY rank DESC \
             LIMIT $2",
        )
        .bind(query)
        .bind(cli.limit)
        .fetch_all(&db)
        .await
        .map_err(|e| format!("Søkefeil: {e}"))?
    };

    tracing::info!(
        query = %query,
        kind = cli.kind.as_deref().unwrap_or("alle"),
        results = results.len(),
        "Søk utført"
    );

    if results.is_empty() {
        println!("Ingen treff for «{query}».");
        return Ok(());
    }

    // Bygg markdown-output
    println!("# Søkeresultater for «{query}»\n");
    println!("{} treff:\n", results.len());

    for (i, r) in results.iter().enumerate() {
        let title = r.title.as_deref().unwrap_or("Uten tittel");
        let date = r.created_at.format("%Y-%m-%d %H:%M");

        println!(
            "{}. **{}** (`{}`)\n   ID: `{}`  |  {} | {}\n   Relevans: {:.3}",
            i + 1,
            title,
            r.node_kind,
            r.id,
            r.visibility,
            date,
            r.rank,
        );

        if let Some(ref headline) = r.headline {
            let hl = headline.trim();
            if !hl.is_empty() {
                println!("   > {hl}");
            }
        }
        println!();
    }

    Ok(())
}
