package io.cameleer.server.app.license;

import io.cameleer.server.core.license.LicenseGate;
import io.cameleer.license.LicenseState;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Prometheus gauges that track the live license posture.
 *
 * <ul>
 *   <li>{@code cameleer_license_state{state=...}} — one-hot per {@link LicenseState}, exactly
 *       one tag value carries 1.0 at any time.</li>
 *   <li>{@code cameleer_license_days_remaining} — days until {@code expiresAt}; negative
 *       (-1.0) when ABSENT/INVALID (no license loaded).</li>
 *   <li>{@code cameleer_license_last_validated_age_seconds} — seconds since the persisted
 *       {@code last_validated_at}; 0 when there is no DB row.</li>
 * </ul>
 *
 * <p>Refreshed eagerly on {@link LicenseChangedEvent} and lazily every 60 seconds so values
 * stay current even without explicit state changes (e.g. days_remaining ticks down across
 * the day, validated_age grows monotonically).</p>
 */
@Component
public class LicenseMetrics {

    private final LicenseGate gate;
    private final LicenseRepository repo;
    private final String tenantId;

    private final Map<LicenseState, AtomicReference<Double>> stateGauges = new EnumMap<>(LicenseState.class);
    private final AtomicReference<Double> daysRemaining = new AtomicReference<>(0.0);
    private final AtomicReference<Double> validatedAge = new AtomicReference<>(0.0);

    public LicenseMetrics(LicenseGate gate, LicenseRepository repo, MeterRegistry meters,
                          @Value("${cameleer.server.tenant.id:default}") String tenantId) {
        this.gate = gate;
        this.repo = repo;
        this.tenantId = tenantId;
        for (var s : LicenseState.values()) {
            var ref = new AtomicReference<>(0.0);
            stateGauges.put(s, ref);
            Gauge.builder("cameleer_license_state", ref, AtomicReference::get)
                    .tag("state", s.name())
                    .register(meters);
        }
        Gauge.builder("cameleer_license_days_remaining", daysRemaining, AtomicReference::get)
                .register(meters);
        Gauge.builder("cameleer_license_last_validated_age_seconds", validatedAge, AtomicReference::get)
                .register(meters);
    }

    @EventListener(LicenseChangedEvent.class)
    @Scheduled(fixedDelay = 60_000)
    public void refresh() {
        var state = gate.getState();
        for (var s : LicenseState.values()) {
            stateGauges.get(s).set(s == state ? 1.0 : 0.0);
        }
        var info = gate.getCurrent();
        daysRemaining.set(info == null
                ? -1.0
                : (double) Duration.between(Instant.now(), info.expiresAt()).toDays());
        repo.findByTenantId(tenantId).ifPresent(rec ->
                validatedAge.set((double) Duration.between(rec.lastValidatedAt(), Instant.now()).toSeconds()));
    }
}
