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

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.core.http.TrustMode;
import com.cameleer.server.core.outbound.OutboundAuth;
import com.cameleer.server.core.outbound.OutboundConnection;
import com.cameleer.server.core.outbound.OutboundConnectionRepository;
import com.cameleer.server.core.outbound.OutboundMethod;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;

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

class PostgresOutboundConnectionRepositoryIT extends AbstractPostgresIT {

    @Autowired
    OutboundConnectionRepository repo;

    private static final String TENANT = "default";
    private static final String USER = "test-alice";

    @BeforeEach
    void seedUser() {
        jdbcTemplate.update(
            "INSERT INTO users (user_id, provider, email, display_name) VALUES (?, ?, ?, ?) ON CONFLICT (user_id) DO NOTHING",
            USER, "test", "alice@example.com", "Alice");
        jdbcTemplate.update("DELETE FROM outbound_connections WHERE tenant_id = ?", TENANT);
    }

    @AfterEach
    void cleanup() {
        jdbcTemplate.update("DELETE FROM outbound_connections WHERE created_by = ? OR updated_by = ?", USER, USER);
        jdbcTemplate.update("DELETE FROM users WHERE user_id = ?", USER);
    }

    private OutboundConnection draft(String name) {
        return new OutboundConnection(
                UUID.randomUUID(), TENANT, name, "desc",
                "https://hooks.slack.com/services/T/B/X", OutboundMethod.POST,
                Map.of("Content-Type", "application/json"), null,
                TrustMode.SYSTEM_DEFAULT, List.of(),
                null, new OutboundAuth.None(), List.of(),
                Instant.now(), USER, Instant.now(), USER);
    }

    @Test
    void saveAndRead() {
        OutboundConnection c = draft("slack-ops");
        repo.save(c);
        OutboundConnection loaded = repo.findById(TENANT, c.id()).orElseThrow();
        assertThat(loaded.name()).isEqualTo("slack-ops");
        assertThat(loaded.defaultHeaders()).containsEntry("Content-Type", "application/json");
        assertThat(loaded.method()).isEqualTo(OutboundMethod.POST);
        assertThat(loaded.tlsTrustMode()).isEqualTo(TrustMode.SYSTEM_DEFAULT);
        assertThat(loaded.auth()).isInstanceOf(OutboundAuth.None.class);
    }

    @Test
    void uniqueNamePerTenant() {
        OutboundConnection a = draft("slack-ops");
        repo.save(a);
        OutboundConnection b = draft("slack-ops");
        assertThatThrownBy(() -> repo.save(b))
                .isInstanceOf(org.springframework.dao.DuplicateKeyException.class);
    }

    @Test
    void allowedEnvironmentIdsRoundTrip() {
        UUID env1 = UUID.randomUUID();
        UUID env2 = UUID.randomUUID();
        OutboundConnection c = new OutboundConnection(
                UUID.randomUUID(), TENANT, "multi-env", null,
                "https://example.com", OutboundMethod.POST,
                Map.of(), null, TrustMode.SYSTEM_DEFAULT, List.of(),
                null, new OutboundAuth.None(), List.of(env1, env2),
                Instant.now(), USER, Instant.now(), USER);
        repo.save(c);
        OutboundConnection loaded = repo.findById(TENANT, c.id()).orElseThrow();
        assertThat(loaded.allowedEnvironmentIds()).containsExactly(env1, env2);
    }

    @Test
    void listByTenantOnlyReturnsCurrentTenant() {
        repo.save(draft("in-tenant"));
        // The outbound_connections.tenant_id is a plain varchar, so we can insert for a different tenant
        // without schema changes. Insert another tenant via the repo directly:
        UUID otherId = UUID.randomUUID();
        OutboundConnection other = new OutboundConnection(
                otherId, "other-tenant", "other-tenant-conn", null,
                "https://example.com", OutboundMethod.POST,
                Map.of(), null, TrustMode.SYSTEM_DEFAULT, List.of(),
                null, new OutboundAuth.None(), List.of(),
                Instant.now(), USER, Instant.now(), USER);
        repo.save(other);
        try {
            List<OutboundConnection> list = repo.listByTenant(TENANT);
            assertThat(list).extracting(OutboundConnection::name).containsExactly("in-tenant");
        } finally {
            repo.delete("other-tenant", otherId);
        }
    }

    @Test
    void deleteRemovesRow() {
        OutboundConnection c = draft("to-delete");
        repo.save(c);
        repo.delete(TENANT, c.id());
        assertThat(repo.findById(TENANT, c.id())).isEmpty();
    }
}
