package com.cameleer.server.app.alerting.eval;

import com.cameleer.server.core.alerting.AlertRule;
import com.cameleer.server.core.alerting.ConditionKind;
import com.cameleer.server.core.alerting.RouteMetricCondition;
import com.cameleer.server.core.runtime.EnvironmentRepository;
import com.cameleer.server.core.search.ExecutionStats;
import com.cameleer.server.core.storage.StatsStore;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.Map;

@Component
public class RouteMetricEvaluator implements ConditionEvaluator<RouteMetricCondition> {

    private final StatsStore statsStore;
    private final EnvironmentRepository envRepo;

    public RouteMetricEvaluator(StatsStore statsStore, EnvironmentRepository envRepo) {
        this.statsStore = statsStore;
        this.envRepo    = envRepo;
    }

    @Override
    public ConditionKind kind() { return ConditionKind.ROUTE_METRIC; }

    @Override
    public EvalResult evaluate(RouteMetricCondition c, AlertRule rule, EvalContext ctx) {
        Instant from = ctx.now().minusSeconds(c.windowSeconds());
        Instant to   = ctx.now();

        String envSlug = envRepo.findById(rule.environmentId())
                .map(e -> e.slug())
                .orElse(null);

        String appSlug  = c.scope() != null ? c.scope().appSlug()  : null;
        String routeId  = c.scope() != null ? c.scope().routeId()  : null;

        ExecutionStats stats;
        if (routeId != null) {
            stats = statsStore.statsForRoute(from, to, routeId, appSlug, envSlug);
        } else if (appSlug != null) {
            stats = statsStore.statsForApp(from, to, appSlug, envSlug);
        } else {
            stats = statsStore.stats(from, to, envSlug);
        }

        double actual = switch (c.metric()) {
            case ERROR_RATE     -> errorRate(stats);
            case AVG_DURATION_MS -> (double) stats.avgDurationMs();
            case P99_LATENCY_MS -> (double) stats.p99LatencyMs();
            case THROUGHPUT     -> (double) stats.totalCount();
            case ERROR_COUNT    -> (double) stats.failedCount();
        };

        boolean fire = switch (c.comparator()) {
            case GT  -> actual >  c.threshold();
            case GTE -> actual >= c.threshold();
            case LT  -> actual <  c.threshold();
            case LTE -> actual <= c.threshold();
            case EQ  -> actual == c.threshold();
        };

        if (!fire) return EvalResult.Clear.INSTANCE;

        return new EvalResult.Firing(actual, c.threshold(),
                Map.of(
                        "route", Map.of("id",   routeId  == null ? "" : routeId),
                        "app",   Map.of("slug",  appSlug  == null ? "" : appSlug)
                )
        );
    }

    private double errorRate(ExecutionStats s) {
        long total = s.totalCount();
        return total == 0 ? 0.0 : (double) s.failedCount() / total;
    }
}
