package com.cameleer.server.app.storage;

import com.cameleer.server.core.runtime.Environment;
import com.cameleer.server.core.runtime.EnvironmentColor;
import com.cameleer.server.core.runtime.EnvironmentRepository;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

public class PostgresEnvironmentRepository implements EnvironmentRepository {

    private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {};
    private final JdbcTemplate jdbc;
    private final ObjectMapper objectMapper;

    public PostgresEnvironmentRepository(JdbcTemplate jdbc, ObjectMapper objectMapper) {
        this.jdbc = jdbc;
        this.objectMapper = objectMapper;
    }

    private static final String SELECT_COLS =
            "id, slug, display_name, production, enabled, default_container_config, jar_retention_count, color, created_at";

    @Override
    public List<Environment> findAll() {
        return jdbc.query(
                "SELECT " + SELECT_COLS + " FROM environments ORDER BY created_at",
                (rs, rowNum) -> mapRow(rs));
    }

    @Override
    public long count() {
        return jdbc.queryForObject("SELECT COUNT(*) FROM environments", Long.class);
    }

    @Override
    public Optional<Environment> findById(UUID id) {
        var results = jdbc.query(
                "SELECT " + SELECT_COLS + " FROM environments WHERE id = ?",
                (rs, rowNum) -> mapRow(rs), id);
        return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
    }

    @Override
    public Optional<Environment> findBySlug(String slug) {
        var results = jdbc.query(
                "SELECT " + SELECT_COLS + " FROM environments WHERE slug = ?",
                (rs, rowNum) -> mapRow(rs), slug);
        return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
    }

    @Override
    public UUID create(String slug, String displayName, boolean production) {
        UUID id = UUID.randomUUID();
        jdbc.update("INSERT INTO environments (id, slug, display_name, production) VALUES (?, ?, ?, ?)",
                id, slug, displayName, production);
        return id;
    }

    @Override
    public void update(UUID id, String displayName, boolean production, boolean enabled, String color) {
        jdbc.update("UPDATE environments SET display_name = ?, production = ?, enabled = ?, color = ?, updated_at = now() WHERE id = ?",
                displayName, production, enabled, color, id);
    }

    @Override
    public void updateJarRetentionCount(UUID id, Integer jarRetentionCount) {
        jdbc.update("UPDATE environments SET jar_retention_count = ? WHERE id = ?", jarRetentionCount, id);
    }

    @Override
    public void delete(UUID id) {
        jdbc.update("DELETE FROM environments WHERE id = ?", id);
    }

    @Override
    public void updateDefaultContainerConfig(UUID id, Map<String, Object> defaultContainerConfig) {
        try {
            String json = objectMapper.writeValueAsString(defaultContainerConfig);
            jdbc.update("UPDATE environments SET default_container_config = ?::jsonb WHERE id = ?", json, id);
        } catch (Exception e) {
            throw new RuntimeException("Failed to serialize default container config", e);
        }
    }

    private Environment mapRow(ResultSet rs) throws SQLException {
        Map<String, Object> config = Map.of();
        try {
            String json = rs.getString("default_container_config");
            if (json != null && !json.isBlank()) {
                config = objectMapper.readValue(json, MAP_TYPE);
            }
        } catch (Exception e) { /* use empty default */ }
        int retentionRaw = rs.getInt("jar_retention_count");
        Integer jarRetentionCount = rs.wasNull() ? null : retentionRaw;
        String color = rs.getString("color");
        if (color == null || color.isBlank()) {
            color = EnvironmentColor.DEFAULT;
        }
        return new Environment(
                UUID.fromString(rs.getString("id")),
                rs.getString("slug"),
                rs.getString("display_name"),
                rs.getBoolean("production"),
                rs.getBoolean("enabled"),
                config,
                jarRetentionCount,
                color,
                rs.getTimestamp("created_at").toInstant()
        );
    }
}
