//! Embedding generation and persistence for indexed symbols.

use anyhow::Result;
use ckg_storage::Storage;

/// Embed `texts` and write `(id, vec)` rows into the per-repo `Embedding`
/// relation. Returns the number of rows written. Errors are bubbled — caller
/// decides whether to swallow (e.g. when offline / model not yet downloaded).
/// Embed `texts` and write `(id, vec)` rows. `batch` is the ONNX inference
/// chunk size (clamped to 1..=4096); surfaced via `CKG_EMBED_BATCH` env or
/// the `--embed-batch` CLI flag on `ckg index` (M9).
pub(super) fn embed_and_persist(
    storage: &Storage,
    targets: &[(String, String)],
    batch: usize,
) -> Result<usize> {
    let embedder = ckg_embed::Embedder::new();
    // M9: batch comes from clap arg (env = "CKG_EMBED_BATCH") threaded
    // through index_one; already clamped at the call site.
    let batch = batch.clamp(1, 4096);
    let mut written = 0usize;
    for chunk in targets.chunks(batch) {
        let texts: Vec<String> = chunk.iter().map(|(_, t)| t.clone()).collect();
        let vecs = embedder
            .embed(&texts)
            .map_err(|e| anyhow::anyhow!("embed batch: {e}"))?;
        let pairs: Vec<(String, Vec<f32>)> = chunk
            .iter()
            .zip(vecs)
            .map(|((id, _), v)| (id.clone(), v))
            .collect();
        let n = pairs.len();
        storage
            .put_embeddings(pairs)
            .map_err(|e| anyhow::anyhow!("put_embeddings: {e}"))?;
        written += n;
    }
    Ok(written)
}

/// Returns the current time as a Unix timestamp string.
///
/// CR-M5: refuse the "0" footgun. A clock-before-epoch is a system
/// misconfiguration the operator needs to see; downstream monotonicity
/// checks on `last_indexed_at` would silently degrade to a fixed timestamp
/// otherwise. Panicking surfaces the issue.
/// Returns the current time as a Unix timestamp string.
///
/// L10: returns `Result` so the clock-before-epoch failure propagates
/// cleanly instead of panicking. Callers use `?` to abort indexing with
/// a clear error message rather than an unwanted panic backtrace.
pub(super) fn now_unix() -> Result<String> {
    use std::time::{SystemTime, UNIX_EPOCH};
    let secs = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_err(|_| anyhow::anyhow!(
            "system clock is before UNIX_EPOCH; refusing to write 0 as \
             last_indexed_at — fix the system clock and retry"
        ))?
        .as_secs();
    Ok(secs.to_string())
}
