package com.cameleer.server.app.alerting.storage;

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.search.ClickHouseLogStore;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Invariants of the consolidated V1 bootstrap schema. Replaces the per-migration
 * ITs (V12/V17/V18) that existed while the schema evolved across 18 files.
 */
class SchemaBootstrapIT extends AbstractPostgresIT {

    @MockBean(name = "clickHouseLogStore")
    ClickHouseLogStore clickHouseLogStore;

    private UUID testEnvId;
    private String testUserId;

    @AfterEach
    void cleanup() {
        if (testEnvId != null) jdbcTemplate.update("DELETE FROM environments WHERE id = ?", testEnvId);
        if (testUserId != null) jdbcTemplate.update("DELETE FROM users WHERE user_id = ?", testUserId);
    }

    @Test
    void all_alerting_tables_exist() {
        var tables = jdbcTemplate.queryForList("""
            SELECT table_name FROM information_schema.tables
             WHERE table_schema = 'public'
               AND table_name IN ('alert_rules','alert_rule_targets','alert_instances',
                                  'alert_silences','alert_notifications')
            """, String.class);
        assertThat(tables).containsExactlyInAnyOrder(
            "alert_rules", "alert_rule_targets", "alert_instances",
            "alert_silences", "alert_notifications");
    }

    @Test
    void alert_reads_table_absent() {
        Integer count = jdbcTemplate.queryForObject("""
            SELECT COUNT(*)::int FROM information_schema.tables
             WHERE table_name = 'alert_reads'
            """, Integer.class);
        assertThat(count).isZero();
    }

    @Test
    void alerting_enums_exist() {
        // Scope to current schema's namespace — Testcontainers reuse can otherwise
        // expose enums from a previous run's tenant_default schema alongside public.
        var enums = jdbcTemplate.queryForList("""
            SELECT t.typname FROM pg_type t
              JOIN pg_namespace n ON n.oid = t.typnamespace
             WHERE t.typname IN ('severity_enum','condition_kind_enum','alert_state_enum',
                                 'target_kind_enum','notification_status_enum')
               AND n.nspname = current_schema()
            """, String.class);
        assertThat(enums).containsExactlyInAnyOrder(
            "severity_enum", "condition_kind_enum", "alert_state_enum",
            "target_kind_enum", "notification_status_enum");
    }

    @Test
    void alert_state_enum_values() {
        var values = jdbcTemplate.queryForList("""
            SELECT unnest(enum_range(NULL::alert_state_enum))::text
            """, String.class);
        assertThat(values).containsExactlyInAnyOrder("PENDING", "FIRING", "RESOLVED");
    }

    @Test
    void condition_kind_enum_values() {
        var values = jdbcTemplate.queryForList("""
            SELECT unnest(enum_range(NULL::condition_kind_enum))::text
            """, String.class);
        assertThat(values).containsExactlyInAnyOrder(
            "ROUTE_METRIC", "EXCHANGE_MATCH", "AGENT_STATE", "AGENT_LIFECYCLE",
            "DEPLOYMENT_STATE", "LOG_PATTERN", "JVM_METRIC");
    }

    @Test
    void alert_instances_has_read_at_and_deleted_at() {
        var cols = jdbcTemplate.queryForList("""
            SELECT column_name FROM information_schema.columns
             WHERE table_name = 'alert_instances'
               AND column_name IN ('read_at','deleted_at')
               AND table_schema = current_schema()
            """, String.class);
        assertThat(cols).containsExactlyInAnyOrder("read_at", "deleted_at");
    }

    @Test
    void open_rule_unique_index_exists() {
        Integer count = jdbcTemplate.queryForObject("""
            SELECT COUNT(*)::int FROM pg_indexes
             WHERE indexname = 'alert_instances_open_rule_uq'
               AND tablename = 'alert_instances'
               AND schemaname = current_schema()
            """, Integer.class);
        assertThat(count).isEqualTo(1);

        Boolean isUnique = jdbcTemplate.queryForObject("""
            SELECT indisunique FROM pg_index
              JOIN pg_class c ON c.oid = pg_index.indexrelid
              JOIN pg_namespace n ON n.oid = c.relnamespace
             WHERE c.relname = 'alert_instances_open_rule_uq'
               AND n.nspname = current_schema()
            """, Boolean.class);
        assertThat(isUnique).isTrue();
    }

    @Test
    void deleting_environment_cascades_alerting_rows() {
        testEnvId = UUID.randomUUID();
        testUserId = UUID.randomUUID().toString();

        jdbcTemplate.update(
            "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?)",
            testEnvId, "test-cascade-env-" + testEnvId, "Test Cascade Env");
        jdbcTemplate.update(
            "INSERT INTO users (user_id, provider, email) VALUES (?, ?, ?)",
            testUserId, "local", "cascade@example.com");

        var ruleId = UUID.randomUUID();
        jdbcTemplate.update(
            "INSERT INTO alert_rules (id, environment_id, name, severity, condition_kind, condition, " +
            "notification_title_tmpl, notification_message_tmpl, created_by, updated_by) " +
            "VALUES (?, ?, 'r', 'WARNING', 'AGENT_STATE', '{}'::jsonb, 't', 'm', ?, ?)",
            ruleId, testEnvId, testUserId, testUserId);

        var instanceId = UUID.randomUUID();
        jdbcTemplate.update(
            "INSERT INTO alert_instances (id, rule_id, rule_snapshot, environment_id, state, severity, " +
            "fired_at, context, title, message) VALUES (?, ?, '{}'::jsonb, ?, 'FIRING', 'WARNING', " +
            "now(), '{}'::jsonb, 't', 'm')",
            instanceId, ruleId, testEnvId);

        jdbcTemplate.update("DELETE FROM environments WHERE id = ?", testEnvId);

        assertThat(jdbcTemplate.queryForObject(
            "SELECT count(*) FROM alert_rules WHERE environment_id = ?",
            Integer.class, testEnvId)).isZero();
        assertThat(jdbcTemplate.queryForObject(
            "SELECT count(*) FROM alert_instances WHERE environment_id = ?",
            Integer.class, testEnvId)).isZero();

        testEnvId = null; // already deleted; skip @AfterEach
    }
}
