//! Template-suppression markers (§11 tail) — the `template_suppressions` table
//! (0058, made owner-polymorphic in 0060). A marker `(owner, target_kind,
//! reference)` records that an OWNER opts OUT of an inherited group template: a
//! handler script name it declines (`target_kind='trigger'`) or a path it
//! declines (`target_kind='route'`).
//!
//! Owner-polymorphic (like `extension_points`): an **app** declines for itself;
//! a **group** declines for its whole subtree. The functions take a
//! `ScriptOwner` and bind the matching owner column. Same tx-function style:
//! read over `&PgPool`, write over a `&mut Transaction`.

use picloud_shared::ScriptOwner;
use sqlx::{PgPool, Postgres, Transaction};

/// The two suppressible template kinds, as stored in `target_kind`.
pub const TARGET_TRIGGER: &str = "trigger";
pub const TARGET_ROUTE: &str = "route";

/// List the suppression markers declared at `owner`, as `(target_kind,
/// reference)` pairs, stably ordered. Backs `load_current` (the apply diff) and
/// the read-only `suppress ls`.
pub async fn list_for_owner(
    pool: &PgPool,
    owner: ScriptOwner,
) -> Result<Vec<(String, String)>, sqlx::Error> {
    let sql = match owner {
        ScriptOwner::App(_) => {
            "SELECT target_kind, reference FROM template_suppressions \
             WHERE app_id = $1 ORDER BY target_kind, LOWER(reference)"
        }
        ScriptOwner::Group(_) => {
            "SELECT target_kind, reference FROM template_suppressions \
             WHERE group_id = $1 ORDER BY target_kind, LOWER(reference)"
        }
    };
    sqlx::query_as(sql)
        .bind(owner_uuid(owner))
        .fetch_all(pool)
        .await
}

/// Insert a suppression marker in the apply transaction. Idempotent: re-apply
/// of an already-declared `(kind, reference)` is a no-op (`ON CONFLICT DO
/// NOTHING` against the per-owner partial unique index), so the marker survives
/// without a spurious version bump.
pub async fn insert_suppression_tx(
    tx: &mut Transaction<'_, Postgres>,
    owner: ScriptOwner,
    target_kind: &str,
    reference: &str,
) -> Result<(), sqlx::Error> {
    let (app_id, group_id) = owner_columns(owner);
    sqlx::query(
        "INSERT INTO template_suppressions (app_id, group_id, target_kind, reference) \
         VALUES ($1, $2, $3, $4) \
         ON CONFLICT DO NOTHING",
    )
    .bind(app_id)
    .bind(group_id)
    .bind(target_kind)
    .bind(reference)
    .execute(&mut **tx)
    .await?;
    Ok(())
}

/// Delete a suppression marker in the apply transaction. Used by `--prune` when
/// the manifest stops declaring the reference → the template re-inherits.
pub async fn delete_suppression_tx(
    tx: &mut Transaction<'_, Postgres>,
    owner: ScriptOwner,
    target_kind: &str,
    reference: &str,
) -> Result<(), sqlx::Error> {
    let sql = match owner {
        ScriptOwner::App(_) => {
            "DELETE FROM template_suppressions \
             WHERE app_id = $1 AND target_kind = $2 AND reference = $3"
        }
        ScriptOwner::Group(_) => {
            "DELETE FROM template_suppressions \
             WHERE group_id = $1 AND target_kind = $2 AND reference = $3"
        }
    };
    sqlx::query(sql)
        .bind(owner_uuid(owner))
        .bind(target_kind)
        .bind(reference)
        .execute(&mut **tx)
        .await?;
    Ok(())
}

/// `(app_id, group_id)` bind pair for the polymorphic owner — exactly one set.
fn owner_columns(owner: ScriptOwner) -> (Option<uuid::Uuid>, Option<uuid::Uuid>) {
    match owner {
        ScriptOwner::App(a) => (Some(a.into_inner()), None),
        ScriptOwner::Group(g) => (None, Some(g.into_inner())),
    }
}

/// The single owner UUID (app or group), for the WHERE-on-one-column queries.
fn owner_uuid(owner: ScriptOwner) -> uuid::Uuid {
    match owner {
        ScriptOwner::App(a) => a.into_inner(),
        ScriptOwner::Group(g) => g.into_inner(),
    }
}
