use chrono::{DateTime, Utc};
use serde::Serialize;
use serde_json::Value;
use sqlx::Row;
use sqlx::postgres::{PgPool, PgRow};

#[derive(Debug, Clone, Serialize)]
pub struct AdmissionEventRecord {
    pub id: i64,
    pub created_at: DateTime<Utc>,
    pub event_id: String,
    pub method: String,
    pub route: String,
    pub client_name: Option<String>,
    pub request_bytes: Option<i64>,
    pub defer_requested: bool,
    pub is_query_route: bool,
    pub decision: String,
    pub reason: Option<String>,
    pub deferred_request_id: Option<String>,
    pub retry_after_seconds: Option<i32>,
    pub window_seconds: Option<i32>,
    pub global_limit_per_window: Option<i64>,
    pub per_client_limit_per_window: Option<i64>,
    pub meta: Value,
}

fn map_admission_event_row(row: &PgRow) -> Result<AdmissionEventRecord, sqlx::Error> {
    Ok(AdmissionEventRecord {
        id: row.try_get("id")?,
        created_at: row.try_get("created_at")?,
        event_id: row.try_get("event_id")?,
        method: row.try_get("method")?,
        route: row.try_get("route")?,
        client_name: row.try_get("client_name")?,
        request_bytes: row.try_get("request_bytes")?,
        defer_requested: row.try_get("defer_requested")?,
        is_query_route: row.try_get("is_query_route")?,
        decision: row.try_get("decision")?,
        reason: row.try_get("reason")?,
        deferred_request_id: row.try_get("deferred_request_id")?,
        retry_after_seconds: row.try_get("retry_after_seconds")?,
        window_seconds: row.try_get("window_seconds")?,
        global_limit_per_window: row.try_get("global_limit_per_window")?,
        per_client_limit_per_window: row.try_get("per_client_limit_per_window")?,
        meta: row.try_get("meta")?,
    })
}

pub async fn list_admission_events(
    pool: &PgPool,
    decision: Option<&str>,
    client_name: Option<&str>,
    limit: i64,
    offset: i64,
) -> Result<Vec<AdmissionEventRecord>, sqlx::Error> {
    let rows = sqlx::query(
        r#"
        SELECT
            id,
            created_at,
            event_id,
            method,
            route,
            client_name,
            request_bytes,
            defer_requested,
            is_query_route,
            decision,
            reason,
            deferred_request_id,
            retry_after_seconds,
            window_seconds,
            global_limit_per_window,
            per_client_limit_per_window,
            meta
        FROM public.gateway_admission_events
        WHERE ($1::text IS NULL OR decision = $1)
          AND ($2::text IS NULL OR client_name = $2)
        ORDER BY created_at DESC
        LIMIT $3
        OFFSET $4
        "#,
    )
    .bind(decision)
    .bind(client_name)
    .bind(limit)
    .bind(offset)
    .fetch_all(pool)
    .await?;

    rows.iter().map(map_admission_event_row).collect()
}
