package com.cameleer.server.app.controller;

import com.cameleer.server.app.dto.ClickHousePerformanceResponse;
import com.cameleer.server.app.dto.ClickHouseQueryInfo;
import com.cameleer.server.app.dto.ClickHouseStatusResponse;
import com.cameleer.server.app.dto.ClickHouseTableInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@ConditionalOnProperty(
    name = "cameleer.server.security.infrastructureendpoints",
    havingValue = "true",
    matchIfMissing = true
)
@RestController
@RequestMapping("/api/v1/admin/clickhouse")
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "ClickHouse Admin", description = "ClickHouse monitoring and diagnostics (ADMIN only)")
public class ClickHouseAdminController {

    private final JdbcTemplate clickHouseJdbc;
    private final String clickHouseUrl;

    public ClickHouseAdminController(
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc,
            @Value("${cameleer.server.clickhouse.url:}") String clickHouseUrl) {
        this.clickHouseJdbc = clickHouseJdbc;
        this.clickHouseUrl = clickHouseUrl;
    }

    @GetMapping("/status")
    @Operation(summary = "ClickHouse cluster status")
    public ClickHouseStatusResponse getStatus() {
        try {
            var row = clickHouseJdbc.queryForMap(
                    "SELECT version() AS version, formatReadableTimeDelta(uptime()) AS uptime");
            return new ClickHouseStatusResponse(true,
                    (String) row.get("version"),
                    (String) row.get("uptime"),
                    clickHouseUrl);
        } catch (Exception e) {
            return new ClickHouseStatusResponse(false, null, null, clickHouseUrl);
        }
    }

    @GetMapping("/tables")
    @Operation(summary = "List ClickHouse tables with sizes")
    public List<ClickHouseTableInfo> getTables() {
        return clickHouseJdbc.query("""
                SELECT t.name, t.engine,
                       t.total_rows AS row_count,
                       formatReadableSize(t.total_bytes) AS data_size,
                       t.total_bytes AS data_size_bytes,
                       ifNull(p.partition_count, 0) AS partition_count
                FROM system.tables t
                LEFT JOIN (
                    SELECT table, countDistinct(partition) AS partition_count
                    FROM system.parts
                    WHERE database = currentDatabase() AND active
                    GROUP BY table
                ) p ON t.name = p.table
                WHERE t.database = currentDatabase()
                ORDER BY t.total_bytes DESC NULLS LAST
                """,
                (rs, rowNum) -> new ClickHouseTableInfo(
                        rs.getString("name"),
                        rs.getString("engine"),
                        rs.getLong("row_count"),
                        rs.getString("data_size"),
                        rs.getLong("data_size_bytes"),
                        rs.getInt("partition_count")));
    }

    @GetMapping("/performance")
    @Operation(summary = "ClickHouse storage and performance metrics")
    public ClickHousePerformanceResponse getPerformance() {
        try {
            var row = clickHouseJdbc.queryForMap("""
                    SELECT
                        formatReadableSize(sum(bytes_on_disk)) AS disk_size,
                        formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size,
                        if(sum(data_uncompressed_bytes) > 0,
                           round(sum(bytes_on_disk) / sum(data_uncompressed_bytes), 3), 0) AS compression_ratio,
                        sum(rows) AS total_rows,
                        count() AS part_count
                    FROM system.parts
                    WHERE database = currentDatabase() AND active
                    """);

            String memory = "N/A";
            try {
                memory = clickHouseJdbc.queryForObject(
                        "SELECT formatReadableSize(value) FROM system.metrics WHERE metric = 'MemoryTracking'",
                        String.class);
            } catch (Exception ignored) {}

            int currentQueries = 0;
            try {
                Integer q = clickHouseJdbc.queryForObject(
                        "SELECT toInt32(value) FROM system.metrics WHERE metric = 'Query'",
                        Integer.class);
                if (q != null) currentQueries = q;
            } catch (Exception ignored) {}

            return new ClickHousePerformanceResponse(
                    (String) row.get("disk_size"),
                    (String) row.get("uncompressed_size"),
                    ((Number) row.get("compression_ratio")).doubleValue(),
                    ((Number) row.get("total_rows")).longValue(),
                    ((Number) row.get("part_count")).intValue(),
                    memory != null ? memory : "N/A",
                    currentQueries);
        } catch (Exception e) {
            return new ClickHousePerformanceResponse("N/A", "N/A", 0, 0, 0, "N/A", 0);
        }
    }

    @GetMapping("/queries")
    @Operation(summary = "Active ClickHouse queries")
    public List<ClickHouseQueryInfo> getQueries() {
        try {
            return clickHouseJdbc.query("""
                    SELECT
                        query_id,
                        round(elapsed, 2) AS elapsed_seconds,
                        formatReadableSize(memory_usage) AS memory,
                        read_rows,
                        substring(query, 1, 200) AS query
                    FROM system.processes
                    WHERE is_initial_query = 1
                      AND query NOT LIKE '%system.processes%'
                    ORDER BY elapsed DESC
                    """,
                    (rs, rowNum) -> new ClickHouseQueryInfo(
                            rs.getString("query_id"),
                            rs.getDouble("elapsed_seconds"),
                            rs.getString("memory"),
                            rs.getLong("read_rows"),
                            rs.getString("query")));
        } catch (Exception e) {
            return List.of();
        }
    }

}
