//! Runtime schema self-heal for chat persistence tables.
//!
//! Ensures `athena.chat_*` tables exist per tenant before [`crate::ChatApp`] and outbox workers
//! touch the database. Invoked from [`crate::SqlxChatApp`] on first pool use per client.

use std::collections::HashSet;
use std::sync::Mutex;

use athena_schema_heal::{
    SchemaHealAuditConfig, SchemaHealContext, SchemaHealPlan, SchemaHealer, SchemaMutation,
};
use sqlx::PgPool;
use tracing::info;

use crate::error::{ChatError, ChatResult};

const CHAT_SCHEMA: &str = "athena";

/// Ensures durable chat runtime tables exist on the request-bound `athena` schema.
///
/// Returns `Ok(false)` when the schema was already marked ready for this client.
pub async fn ensure_chat_runtime_schema(
    pool: &PgPool,
    client_name: &str,
    ready_clients: &Mutex<HashSet<String>>,
) -> ChatResult<bool> {
    {
        let cache = ready_clients.lock().map_err(|err| {
            ChatError::Internal(format!("chat schema cache lock poisoned: {err}"))
        })?;
        if cache.contains(client_name) {
            return Ok(false);
        }
    }

    let report = SchemaHealer::new(pool)
        .with_audit_config(SchemaHealAuditConfig {
            enabled: true,
            actor: "athena_chat_runtime".to_string(),
        })
        .apply_plan(&chat_runtime_schema_heal_plan(client_name))
        .await
        .map_err(|err| {
            ChatError::Internal(format!("failed to self-heal chat runtime schema: {err}"))
        })?;

    if report.applied_count() > 0 {
        info!(
            client = %client_name,
            plan = %report.plan_name,
            applied = report.applied_count(),
            skipped = report.skipped_count(),
            "Applied chat runtime schema self-heal mutations"
        );
    }

    let mut cache = ready_clients
        .lock()
        .map_err(|err| ChatError::Internal(format!("chat schema cache lock poisoned: {err}")))?;
    cache.insert(client_name.to_string());
    Ok(report.applied_count() > 0)
}

fn chat_runtime_schema_heal_plan(client_name: &str) -> SchemaHealPlan {
    let context = SchemaHealContext::new(client_name, CHAT_SCHEMA)
        .with_object_type("chat_runtime_schema_self_heal")
        .with_table_name("chat_outbox");

    let mut plan = SchemaHealPlan::new("chat_runtime_storage", context);

    plan.push_mutation(SchemaMutation::statement(
        "ensure_pgcrypto_extension",
        "CREATE EXTENSION IF NOT EXISTS pgcrypto",
    ));
    plan.push_mutation(SchemaMutation::statement(
        "ensure_athena_schema",
        "CREATE SCHEMA IF NOT EXISTS athena",
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_files_stub_table",
        CHAT_SCHEMA,
        "files",
        r#"
        CREATE TABLE IF NOT EXISTS athena.files (
            id text PRIMARY KEY
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_rooms_table",
        CHAT_SCHEMA,
        "chat_rooms",
        r#"
        CREATE TABLE IF NOT EXISTS athena.chat_rooms (
            id uuid PRIMARY KEY,
            organization_id text NOT NULL,
            kind text NOT NULL CHECK (kind IN ('dm', 'group', 'channel')),
            title text,
            created_by text NOT NULL,
            created_at timestamptz NOT NULL DEFAULT now(),
            updated_at timestamptz NOT NULL DEFAULT now(),
            archived_at timestamptz,
            last_message_id uuid,
            last_message_seq bigint NOT NULL DEFAULT 0,
            last_message_at timestamptz,
            version bigint NOT NULL DEFAULT 1
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_rooms_org_recent_active",
        CHAT_SCHEMA,
        "chat_rooms",
        "idx_chat_rooms_org_recent_active",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_rooms_org_recent_active
            ON athena.chat_rooms (organization_id, updated_at DESC)
            WHERE archived_at IS NULL
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_room_members_table",
        CHAT_SCHEMA,
        "chat_room_members",
        r#"
        CREATE TABLE IF NOT EXISTS athena.chat_room_members (
            room_id uuid NOT NULL REFERENCES athena.chat_rooms(id) ON DELETE CASCADE,
            user_id text NOT NULL,
            role text NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
            joined_at timestamptz NOT NULL DEFAULT now(),
            last_read_seq bigint NOT NULL DEFAULT 0,
            last_read_message_id uuid,
            muted boolean NOT NULL DEFAULT false,
            notification_mode text,
            hidden_at timestamptz,
            PRIMARY KEY (room_id, user_id)
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_room_members_user_room",
        CHAT_SCHEMA,
        "chat_room_members",
        "idx_chat_room_members_user_room",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_room_members_user_room
            ON athena.chat_room_members (user_id, room_id)
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_messages_table",
        CHAT_SCHEMA,
        "chat_messages",
        r#"
        CREATE TABLE IF NOT EXISTS athena.chat_messages (
            id uuid PRIMARY KEY,
            room_id uuid NOT NULL REFERENCES athena.chat_rooms(id) ON DELETE CASCADE,
            room_seq bigint NOT NULL,
            sender_id text NOT NULL,
            client_message_id text,
            body_text text,
            body_json jsonb,
            reply_to_message_id uuid,
            created_at timestamptz NOT NULL DEFAULT now(),
            edited_at timestamptz,
            deleted_at timestamptz,
            metadata_json jsonb,
            CONSTRAINT chat_messages_room_seq_unique UNIQUE (room_id, room_seq),
            CONSTRAINT chat_messages_idempotency_unique UNIQUE (room_id, sender_id, client_message_id)
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_messages_room_seq_desc",
        CHAT_SCHEMA,
        "chat_messages",
        "idx_chat_messages_room_seq_desc",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_messages_room_seq_desc
            ON athena.chat_messages (room_id, room_seq DESC)
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_messages_room_created_desc",
        CHAT_SCHEMA,
        "chat_messages",
        "idx_chat_messages_room_created_desc",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_messages_room_created_desc
            ON athena.chat_messages (room_id, created_at DESC)
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_message_attachments_table",
        CHAT_SCHEMA,
        "chat_message_attachments",
        r#"
        CREATE TABLE IF NOT EXISTS athena.chat_message_attachments (
            message_id uuid NOT NULL REFERENCES athena.chat_messages(id) ON DELETE CASCADE,
            file_id text NOT NULL REFERENCES athena.files(id) ON DELETE RESTRICT,
            ordinal integer NOT NULL,
            PRIMARY KEY (message_id, ordinal)
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_message_attachments_file",
        CHAT_SCHEMA,
        "chat_message_attachments",
        "idx_chat_message_attachments_file",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_message_attachments_file
            ON athena.chat_message_attachments (file_id)
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_message_reactions_table",
        CHAT_SCHEMA,
        "chat_message_reactions",
        r#"
        CREATE TABLE IF NOT EXISTS athena.chat_message_reactions (
            message_id uuid NOT NULL REFERENCES athena.chat_messages(id) ON DELETE CASCADE,
            user_id text NOT NULL,
            emoji text NOT NULL,
            created_at timestamptz NOT NULL DEFAULT now(),
            PRIMARY KEY (message_id, user_id, emoji)
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_message_reactions_lookup",
        CHAT_SCHEMA,
        "chat_message_reactions",
        "idx_chat_message_reactions_lookup",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_message_reactions_lookup
            ON athena.chat_message_reactions (message_id, emoji)
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_table(
        "ensure_chat_outbox_table",
        CHAT_SCHEMA,
        "chat_outbox",
        r#"
        CREATE TABLE IF NOT EXISTS athena.chat_outbox (
            id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
            aggregate_type text NOT NULL CHECK (aggregate_type IN ('chat_message', 'chat_room', 'read_cursor')),
            aggregate_id text NOT NULL,
            room_id uuid NOT NULL REFERENCES athena.chat_rooms(id) ON DELETE CASCADE,
            room_seq bigint,
            event_type text NOT NULL,
            payload_json jsonb NOT NULL,
            created_at timestamptz NOT NULL DEFAULT now(),
            published_at timestamptz,
            publish_attempts integer NOT NULL DEFAULT 0
        )
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_outbox_unpublished",
        CHAT_SCHEMA,
        "chat_outbox",
        "idx_chat_outbox_unpublished",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_outbox_unpublished
            ON athena.chat_outbox (published_at, created_at)
            WHERE published_at IS NULL
        "#,
    ));
    plan.push_mutation(SchemaMutation::ensure_index(
        "ensure_idx_chat_outbox_room_seq",
        CHAT_SCHEMA,
        "chat_outbox",
        "idx_chat_outbox_room_seq",
        r#"
        CREATE INDEX IF NOT EXISTS idx_chat_outbox_room_seq
            ON athena.chat_outbox (room_id, room_seq)
        "#,
    ));

    plan
}
