package com.cameleer.server.app.storage;

import com.cameleer.common.model.ApplicationConfig;
import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.core.runtime.Deployment;
import com.cameleer.server.core.runtime.DeploymentConfigSnapshot;
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.util.Map;
import java.util.UUID;

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

class PostgresDeploymentRepositoryIT extends AbstractPostgresIT {

    @Autowired PostgresDeploymentRepository repository;

    private UUID envId;
    private UUID appId;
    private UUID appVersionId;

    @BeforeEach
    void setup() {
        envId = UUID.randomUUID();
        jdbcTemplate.update(
                "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?)",
                envId, "test-env-" + envId, "Test Env");

        appId = UUID.randomUUID();
        jdbcTemplate.update(
                "INSERT INTO apps (id, environment_id, slug, display_name) VALUES (?, ?, ?, ?)",
                appId, envId, "app-it-" + appId, "App IT");

        appVersionId = UUID.randomUUID();
        jdbcTemplate.update(
                "INSERT INTO app_versions (id, app_id, version, jar_path, jar_checksum) VALUES (?, ?, ?, ?, ?)",
                appVersionId, appId, 1, "/tmp/app.jar", "deadbeef");
    }

    @AfterEach
    void cleanup() {
        jdbcTemplate.update("DELETE FROM deployments WHERE app_id = ?", appId);
        jdbcTemplate.update("DELETE FROM app_versions WHERE app_id = ?", appId);
        jdbcTemplate.update("DELETE FROM apps WHERE id = ?", appId);
        jdbcTemplate.update("DELETE FROM environments WHERE id = ?", envId);
    }

    @Test
    void deployedConfigSnapshot_roundtrips() {
        // given — create a deployment then store a snapshot
        ApplicationConfig agentConfig = new ApplicationConfig();
        agentConfig.setApplication("app-it");
        agentConfig.setEnvironment("staging");
        agentConfig.setVersion(3);
        agentConfig.setSamplingRate(0.5);

        UUID jarVersionId = UUID.randomUUID();
        DeploymentConfigSnapshot snapshot = new DeploymentConfigSnapshot(
                jarVersionId,
                agentConfig,
                Map.of("memoryLimitMb", 1024, "replicas", 2),
                null
        );

        // pre-V4 rows: no creator (createdBy is nullable)
        UUID deploymentId = repository.create(appId, appVersionId, envId, "test-container", null);
        repository.saveDeployedConfigSnapshot(deploymentId, snapshot);

        // when — load it back
        Deployment loaded = repository.findById(deploymentId).orElseThrow();

        // then
        assertThat(loaded.deployedConfigSnapshot().jarVersionId()).isEqualTo(jarVersionId);
        assertThat(loaded.deployedConfigSnapshot().agentConfig().getSamplingRate()).isEqualTo(0.5);
        assertThat(loaded.deployedConfigSnapshot().containerConfig()).containsEntry("memoryLimitMb", 1024);
    }

    @Test
    void deployedConfigSnapshot_nullByDefault() {
        // deployments created without a snapshot must return null (not throw)
        UUID deploymentId = repository.create(appId, appVersionId, envId, "test-container-null", null);

        Deployment loaded = repository.findById(deploymentId).orElseThrow();

        assertThat(loaded.deployedConfigSnapshot()).isNull();
    }

    @Test
    void deleteFailedByAppAndEnvironment_keepsStoppedAndActive() {
        // given: one STOPPED (checkpoint), one FAILED, one RUNNING
        UUID stoppedId = repository.create(appId, appVersionId, envId, "stopped", null);
        repository.updateStatus(stoppedId, com.cameleer.server.core.runtime.DeploymentStatus.STOPPED, null, null);

        UUID failedId = repository.create(appId, appVersionId, envId, "failed", null);
        repository.updateStatus(failedId, com.cameleer.server.core.runtime.DeploymentStatus.FAILED, null, "boom");

        UUID runningId = repository.create(appId, appVersionId, envId, "running", null);
        repository.updateStatus(runningId, com.cameleer.server.core.runtime.DeploymentStatus.RUNNING, "c1", null);

        // when
        repository.deleteFailedByAppAndEnvironment(appId, envId);

        // then: STOPPED and RUNNING survive; FAILED is gone
        assertThat(repository.findById(stoppedId)).isPresent();
        assertThat(repository.findById(runningId)).isPresent();
        assertThat(repository.findById(failedId)).isEmpty();
    }

    @Test
    void deployedConfigSnapshot_canBeClearedToNull() {
        UUID jarVersionId = UUID.randomUUID();
        DeploymentConfigSnapshot snapshot = new DeploymentConfigSnapshot(
                jarVersionId,
                new ApplicationConfig(),
                Map.of(),
                null
        );

        UUID deploymentId = repository.create(appId, appVersionId, envId, "test-container-clear", null);
        repository.saveDeployedConfigSnapshot(deploymentId, snapshot);
        repository.saveDeployedConfigSnapshot(deploymentId, null);

        Deployment loaded = repository.findById(deploymentId).orElseThrow();
        assertThat(loaded.deployedConfigSnapshot()).isNull();
    }
}
