package com.cameleer.server.app.storage;

import com.cameleer.server.core.runtime.App;
import com.cameleer.server.core.runtime.AppRepository;
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 PostgresAppRepository implements AppRepository {

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

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

    @Override
    public List<App> findByEnvironmentId(UUID environmentId) {
        return jdbc.query(
                "SELECT id, environment_id, slug, display_name, container_config, created_at, updated_at FROM apps WHERE environment_id = ? ORDER BY created_at",
                (rs, rowNum) -> mapRow(rs), environmentId);
    }

    @Override
    public Optional<App> findById(UUID id) {
        var results = jdbc.query(
                "SELECT id, environment_id, slug, display_name, container_config, created_at, updated_at FROM apps WHERE id = ?",
                (rs, rowNum) -> mapRow(rs), id);
        return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
    }

    @Override
    public Optional<App> findByEnvironmentIdAndSlug(UUID environmentId, String slug) {
        var results = jdbc.query(
                "SELECT id, environment_id, slug, display_name, container_config, created_at, updated_at FROM apps WHERE environment_id = ? AND slug = ?",
                (rs, rowNum) -> mapRow(rs), environmentId, slug);
        return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
    }

    @Override
    public Optional<App> findBySlug(String slug) {
        var results = jdbc.query(
                "SELECT id, environment_id, slug, display_name, container_config, created_at, updated_at FROM apps WHERE slug = ? LIMIT 1",
                (rs, rowNum) -> mapRow(rs), slug);
        return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
    }

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

    @Override
    public List<App> findAll() {
        return jdbc.query(
                "SELECT id, environment_id, slug, display_name, container_config, created_at, updated_at FROM apps ORDER BY created_at",
                (rs, rowNum) -> mapRow(rs));
    }

    @Override
    public long count() {
        Long n = jdbc.queryForObject("SELECT COUNT(*) FROM apps", Long.class);
        return n == null ? 0L : n;
    }

    @Override
    public void updateContainerConfig(UUID id, Map<String, Object> containerConfig) {
        try {
            String json = objectMapper.writeValueAsString(containerConfig);
            jdbc.update("UPDATE apps SET container_config = ?::jsonb, updated_at = now() WHERE id = ?", json, id);
        } catch (Exception e) {
            throw new RuntimeException("Failed to serialize container config", e);
        }
    }

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

    private App mapRow(ResultSet rs) throws SQLException {
        Map<String, Object> config = Map.of();
        try {
            String json = rs.getString("container_config");
            if (json != null && !json.isBlank()) {
                config = objectMapper.readValue(json, MAP_TYPE);
            }
        } catch (Exception e) { /* use empty default */ }
        return new App(
                UUID.fromString(rs.getString("id")),
                UUID.fromString(rs.getString("environment_id")),
                rs.getString("slug"),
                rs.getString("display_name"),
                config,
                rs.getTimestamp("created_at").toInstant(),
                rs.getTimestamp("updated_at").toInstant()
        );
    }
}
