//! Persistent conversation summaries generated by the context compactor.
//!
//! Each row covers all `chat_history` messages up to and including
//! `covers_up_to_message_id`. At most one active summary exists per stack;
//! a new compaction creates a new row that supersedes the previous one.

use sqlx::SqlitePool;

#[derive(Debug, Clone)]
pub struct ChatSummary {
    pub id:                      i64,
    pub stack_id:                i64,
    pub content:                 String,
    /// All chat_history rows with `id <= covers_up_to_message_id` are covered
    /// by this summary. `build_openai_messages` loads only rows *after* this id.
    pub covers_up_to_message_id: i64,
    pub created_at:              String,
}

/// Persist a new summary for the given stack, returning the new row id.
pub async fn save(
    pool:                    &SqlitePool,
    stack_id:                i64,
    content:                 &str,
    covers_up_to_message_id: i64,
) -> anyhow::Result<i64> {
    let id = sqlx::query_scalar::<_, i64>(
        "INSERT INTO chat_summaries
             (stack_id, content, covers_up_to_message_id)
         VALUES (?, ?, ?)
         RETURNING id",
    )
    .bind(stack_id)
    .bind(content)
    .bind(covers_up_to_message_id)
    .fetch_one(pool)
    .await?;
    Ok(id)
}

/// Returns the most recent summary for a stack, or `None` if none exists.
pub async fn latest_for_stack(
    pool:     &SqlitePool,
    stack_id: i64,
) -> anyhow::Result<Option<ChatSummary>> {
    let row = sqlx::query_as::<_, (i64, i64, String, i64, String)>(
        "SELECT id, stack_id, content, covers_up_to_message_id, created_at
         FROM   chat_summaries
         WHERE  stack_id = ?
         ORDER  BY id DESC
         LIMIT  1",
    )
    .bind(stack_id)
    .fetch_optional(pool)
    .await?;

    Ok(row.map(|(id, stack_id, content, covers_up_to_message_id, created_at)| ChatSummary {
        id,
        stack_id,
        content,
        covers_up_to_message_id,
        created_at,
    }))
}
