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.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.util.List;
import java.util.UUID;

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

class PostgresAlertReadRepositoryIT extends AbstractPostgresIT {

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

    private PostgresAlertReadRepository repo;
    private UUID envId;
    private UUID instanceId1;
    private UUID instanceId2;
    private UUID instanceId3;
    private final String userId = "read-user-" + UUID.randomUUID();

    @BeforeEach
    void setup() {
        repo = new PostgresAlertReadRepository(jdbcTemplate);
        envId = UUID.randomUUID();
        instanceId1 = UUID.randomUUID();
        instanceId2 = UUID.randomUUID();
        instanceId3 = UUID.randomUUID();

        jdbcTemplate.update(
            "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?)",
            envId, "test-env-" + UUID.randomUUID(), "Test Env");
        jdbcTemplate.update(
            "INSERT INTO users (user_id, provider, email) VALUES ('sys-user', 'local', 'sys@example.com') ON CONFLICT (user_id) DO NOTHING");
        jdbcTemplate.update(
            "INSERT INTO users (user_id, provider, email) VALUES (?, 'local', ?) ON CONFLICT (user_id) DO NOTHING",
            userId, userId + "@example.com");

        // Each open alert_instance needs its own rule_id — the alert_instances_open_rule_uq
        // partial unique forbids multiple open instances sharing the same rule_id + exchange
        // discriminator (V13/V15). Three separate rules let all three instances coexist
        // in FIRING state so alert_reads tests can target each one independently.
        for (UUID instanceId : List.of(instanceId1, instanceId2, instanceId3)) {
            UUID 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 (?, ?, ?, 'WARNING', 'AGENT_STATE', '{}'::jsonb, 't', 'm', 'sys-user', 'sys-user')",
                ruleId, envId, "rule-" + instanceId);
            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, 'title', 'msg')",
                instanceId, ruleId, envId);
        }
    }

    @AfterEach
    void cleanup() {
        jdbcTemplate.update("DELETE FROM alert_reads WHERE user_id = ?", userId);
        jdbcTemplate.update("DELETE FROM alert_instances WHERE environment_id = ?", envId);
        jdbcTemplate.update("DELETE FROM alert_rules WHERE environment_id = ?", envId);
        jdbcTemplate.update("DELETE FROM environments WHERE id = ?", envId);
        jdbcTemplate.update("DELETE FROM users WHERE user_id = ?", userId);
    }

    @Test
    void markRead_insertsReadRecord() {
        repo.markRead(userId, instanceId1);

        int count = jdbcTemplate.queryForObject(
            "SELECT count(*) FROM alert_reads WHERE user_id = ? AND alert_instance_id = ?",
            Integer.class, userId, instanceId1);
        assertThat(count).isEqualTo(1);
    }

    @Test
    void markRead_isIdempotent() {
        repo.markRead(userId, instanceId1);
        // second call should not throw
        assertThatCode(() -> repo.markRead(userId, instanceId1)).doesNotThrowAnyException();

        int count = jdbcTemplate.queryForObject(
            "SELECT count(*) FROM alert_reads WHERE user_id = ? AND alert_instance_id = ?",
            Integer.class, userId, instanceId1);
        assertThat(count).isEqualTo(1);
    }

    @Test
    void bulkMarkRead_marksMultiple() {
        repo.bulkMarkRead(userId, List.of(instanceId1, instanceId2, instanceId3));

        int count = jdbcTemplate.queryForObject(
            "SELECT count(*) FROM alert_reads WHERE user_id = ?",
            Integer.class, userId);
        assertThat(count).isEqualTo(3);
    }

    @Test
    void bulkMarkRead_emptyListDoesNotThrow() {
        assertThatCode(() -> repo.bulkMarkRead(userId, List.of())).doesNotThrowAnyException();
    }

    @Test
    void bulkMarkRead_isIdempotent() {
        repo.bulkMarkRead(userId, List.of(instanceId1, instanceId2));
        assertThatCode(() -> repo.bulkMarkRead(userId, List.of(instanceId1, instanceId2)))
            .doesNotThrowAnyException();

        int count = jdbcTemplate.queryForObject(
            "SELECT count(*) FROM alert_reads WHERE user_id = ?",
            Integer.class, userId);
        assertThat(count).isEqualTo(2);
    }
}
