//! Webhook triggers for Athena **management** schema mutations (`/management/*` DDL).

use actix_web::HttpRequest;
use actix_web::web::Data;
use serde_json::{Value, json};

use crate::AppState;
use crate::api::management::types::TableEditOperation;

use super::dispatch::{gateway_webhook_trigger_from_http, spawn_gateway_webhook_dispatch};

pub const ROUTE_SCHEMA_TABLE_CREATED: &str = "schema_table_created";
pub const ROUTE_SCHEMA_TABLE_DROPPED: &str = "schema_table_dropped";
pub const ROUTE_SCHEMA_COLUMN_ADDED: &str = "schema_column_added";
pub const ROUTE_SCHEMA_COLUMN_DROPPED: &str = "schema_column_dropped";
pub const ROUTE_SCHEMA_COLUMN_RENAMED: &str = "schema_column_renamed";
pub const ROUTE_SCHEMA_COLUMN_ALTERED: &str = "schema_column_altered";
pub const ROUTE_SCHEMA_INDEX_CREATED: &str = "schema_index_created";
pub const ROUTE_SCHEMA_INDEX_DROPPED: &str = "schema_index_dropped";
pub const ROUTE_SCHEMA_EXTENSION_INSTALLED: &str = "schema_extension_installed";

/// Fire a webhook after a successful management mutation on `client_name`'s database.
pub fn spawn_management_schema_webhook(
    app_state: Data<AppState>,
    req: &HttpRequest,
    client_name: &str,
    request_id: Option<String>,
    route_key: &str,
    table_name: Option<String>,
    payload: Value,
    response: Value,
) {
    spawn_gateway_webhook_dispatch(
        app_state,
        gateway_webhook_trigger_from_http(
            req,
            client_name,
            route_key,
            table_name,
            request_id,
            Some(payload),
            Some(response),
        ),
    );
}

fn push_column_altered(
    out: &mut Vec<(&'static str, Value)>,
    schema_name: &str,
    table_name: &str,
    resource_suffix: &str,
    op: &TableEditOperation,
) {
    let rid: String = format!("{}.{}{}", schema_name, table_name, resource_suffix);
    out.push((
        ROUTE_SCHEMA_COLUMN_ALTERED,
        json!({
            "schema_name": schema_name,
            "table_name": table_name,
            "resource_id": rid,
            "operation": serde_json::to_value(op).unwrap_or(json!({})),
        }),
    ));
}

/// Map each `TableEditOperation` to one `(route_key, response_summary)` pair for outbound hooks.
pub fn schema_webhooks_for_edit_operations(
    schema_name: &str,
    table_name: &str,
    operations: &[TableEditOperation],
) -> Vec<(&'static str, Value)> {
    let mut out: Vec<(&'static str, Value)> = Vec::with_capacity(operations.len());
    for op in operations {
        match op {
            TableEditOperation::AddColumn { column } => {
                let rid: String = format!("{}.{}.column:{}", schema_name, table_name, column.name);
                out.push((
                    ROUTE_SCHEMA_COLUMN_ADDED,
                    json!({
                        "schema_name": schema_name,
                        "table_name": table_name,
                        "resource_id": rid,
                        "column": column,
                    }),
                ));
            }
            TableEditOperation::RenameColumn { from, to } => {
                let rid: String = format!("{}.{}.rename:{}->{}", schema_name, table_name, from, to);
                out.push((
                    ROUTE_SCHEMA_COLUMN_RENAMED,
                    json!({
                        "schema_name": schema_name,
                        "table_name": table_name,
                        "resource_id": rid,
                        "from": from,
                        "to": to,
                    }),
                ));
            }
            TableEditOperation::SetDefault { column_name, .. } => {
                push_column_altered(
                    &mut out,
                    schema_name,
                    table_name,
                    &format!(".set_default:{column_name}"),
                    op,
                );
            }
            TableEditOperation::DropDefault { column_name } => {
                push_column_altered(
                    &mut out,
                    schema_name,
                    table_name,
                    &format!(".drop_default:{column_name}"),
                    op,
                );
            }
            TableEditOperation::SetNotNull { column_name } => {
                push_column_altered(
                    &mut out,
                    schema_name,
                    table_name,
                    &format!(".set_not_null:{column_name}"),
                    op,
                );
            }
            TableEditOperation::DropNotNull { column_name } => {
                push_column_altered(
                    &mut out,
                    schema_name,
                    table_name,
                    &format!(".drop_not_null:{column_name}"),
                    op,
                );
            }
        }
    }
    out
}
