package com.cameleer.server.app.config;

import com.cameleer.server.app.metrics.ServerMetrics;
import com.cameleer.server.app.search.ClickHouseLogStore;
import com.cameleer.server.app.storage.ClickHouseAgentEventRepository;
import com.cameleer.server.app.storage.ClickHouseUsageTracker;
import com.cameleer.server.app.storage.ClickHouseDiagramStore;
import com.cameleer.server.app.storage.ClickHouseRouteCatalogStore;
import com.cameleer.server.core.storage.RouteCatalogStore;
import com.cameleer.server.app.storage.ClickHouseMetricsQueryStore;
import com.cameleer.server.app.storage.ClickHouseMetricsStore;
import com.cameleer.server.app.storage.ClickHouseServerMetricsQueryStore;
import com.cameleer.server.app.storage.ClickHouseServerMetricsStore;
import com.cameleer.server.app.storage.ClickHouseStatsStore;
import com.cameleer.server.core.admin.AuditRepository;
import com.cameleer.server.core.admin.AuditService;
import com.cameleer.server.core.agent.AgentEventRepository;
import com.cameleer.server.core.agent.AgentInfo;
import com.cameleer.server.core.agent.AgentRegistryService;
import com.cameleer.server.core.detail.DetailService;
import com.cameleer.server.app.ingestion.ExecutionFlushScheduler;
import com.cameleer.server.app.search.ClickHouseSearchIndex;
import com.cameleer.server.app.storage.ClickHouseExecutionStore;
import com.cameleer.server.core.ingestion.BufferedLogEntry;
import com.cameleer.server.core.ingestion.ChunkAccumulator;
import com.cameleer.server.core.ingestion.IngestionService;
import com.cameleer.server.core.ingestion.MergedExecution;
import com.cameleer.server.core.ingestion.WriteBuffer;
import com.cameleer.server.core.storage.*;
import com.cameleer.server.core.storage.LogIndex;
import com.cameleer.server.core.storage.StatsStore;
import com.cameleer.server.core.storage.model.MetricsSnapshot;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class StorageBeanConfig {

    @Bean
    public DetailService detailService(ExecutionStore executionStore) {
        return new DetailService(executionStore);
    }

    @Bean
    public AuditService auditService(AuditRepository auditRepository) {
        return new AuditService(auditRepository);
    }

    @Bean
    public IngestionService ingestionService(DiagramStore diagramStore,
                                              WriteBuffer<MetricsSnapshot> metricsBuffer) {
        return new IngestionService(diagramStore, metricsBuffer);
    }

    @Bean
    public MetricsStore clickHouseMetricsStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseMetricsStore(tenantProperties.getId(), clickHouseJdbc);
    }

    @Bean
    public MetricsQueryStore clickHouseMetricsQueryStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseMetricsQueryStore(tenantProperties.getId(), clickHouseJdbc);
    }

    @Bean
    public ServerMetricsStore clickHouseServerMetricsStore(
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseServerMetricsStore(clickHouseJdbc);
    }

    @Bean
    public ServerMetricsQueryStore clickHouseServerMetricsQueryStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseServerMetricsQueryStore(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── Execution Store ──────────────────────────────────────────────────

    @Bean
    public ClickHouseExecutionStore clickHouseExecutionStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseExecutionStore(tenantProperties.getId(), clickHouseJdbc);
    }

    @Bean
    public ChunkAccumulator chunkAccumulator(
            TenantProperties tenantProperties,
            WriteBuffer<MergedExecution> executionBuffer,
            WriteBuffer<ChunkAccumulator.ProcessorBatch> processorBatchBuffer,
            DiagramStore diagramStore,
            AgentRegistryService registryService) {
        return new ChunkAccumulator(
                tenantProperties.getId(),
                executionBuffer::offerOrWarn,
                processorBatchBuffer::offerOrWarn,
                diagramStore,
                java.time.Duration.ofMinutes(5),
                instanceId -> {
                    AgentInfo agent = registryService.findById(instanceId);
                    return agent != null && agent.environmentId() != null
                            ? agent.environmentId() : "default";
                });
    }

    @Bean
    public ExecutionFlushScheduler executionFlushScheduler(
            WriteBuffer<MergedExecution> executionBuffer,
            WriteBuffer<ChunkAccumulator.ProcessorBatch> processorBatchBuffer,
            WriteBuffer<BufferedLogEntry> logBuffer,
            ClickHouseExecutionStore executionStore,
            ClickHouseLogStore logStore,
            ChunkAccumulator accumulator,
            IngestionConfig config,
            ServerMetrics serverMetrics) {
        return new ExecutionFlushScheduler(executionBuffer, processorBatchBuffer,
                logBuffer, executionStore, logStore, accumulator, config, serverMetrics);
    }

    @Bean
    public ClickHouseSearchIndex clickHouseSearchIndex(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        // Return type is the concrete class so Spring exposes the bean under both
        // SearchIndex (for SearchIndexer) AND ClickHouseSearchIndex (for ExchangeMatchEvaluator,
        // which calls countExecutionsForAlerting — a method that exists only on the concrete type).
        // Declaring the return as the interface hides the concrete methods from autowire
        // matching and crashloops the app on startup.
        return new ClickHouseSearchIndex(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── ClickHouse Stats Store ─────────────────────────────────────────

    @Bean
    public StatsStore clickHouseStatsStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseStatsStore(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── ClickHouse Diagram Store ──────────────────────────────────────

    @Bean
    public DiagramStore clickHouseDiagramStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseDiagramStore(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── ClickHouse Route Catalog Store ───────────────────────────────

    @Bean
    public RouteCatalogStore clickHouseRouteCatalogStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseRouteCatalogStore(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── ClickHouse Agent Event Repository ─────────────────────────────

    @Bean
    public AgentEventRepository clickHouseAgentEventRepository(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseAgentEventRepository(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── ClickHouse Log Store ──────────────────────────────────────────

    @Bean
    public ClickHouseLogStore clickHouseLogStore(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseLogStore(tenantProperties.getId(), clickHouseJdbc);
    }

    // ── Usage Analytics ──────────────────────────────────────────────

    @Bean
    public ClickHouseUsageTracker clickHouseUsageTracker(
            TenantProperties tenantProperties,
            @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) {
        return new ClickHouseUsageTracker(tenantProperties.getId(), clickHouseJdbc,
                new com.cameleer.server.core.ingestion.WriteBuffer<>(5000));
    }

    @Bean
    public com.cameleer.server.app.analytics.UsageTrackingInterceptor usageTrackingInterceptor(
            ClickHouseUsageTracker usageTracker) {
        return new com.cameleer.server.app.analytics.UsageTrackingInterceptor(usageTracker);
    }

    @Bean
    public com.cameleer.server.app.analytics.UsageFlushScheduler usageFlushScheduler(
            ClickHouseUsageTracker usageTracker) {
        return new com.cameleer.server.app.analytics.UsageFlushScheduler(usageTracker);
    }

    // ── License Repository ───────────────────────────────────────────

    @Bean
    public com.cameleer.server.app.license.LicenseRepository licenseRepository(
            JdbcTemplate jdbcTemplate) {
        return new com.cameleer.server.app.license.PostgresLicenseRepository(jdbcTemplate);
    }
}
