package at.mocode.infrastructure.gateway.metrics

import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Timer
import io.micrometer.core.instrument.config.MeterFilter
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
import java.time.Duration

/**
 * Konfiguration für Gateway-spezifische Metriken mit Micrometer.
 *
 * Diese Konfiguration stellt folgende Metriken bereit:
 * - Request/Response Zeit Metriken (Timer)
 * - Fehlerrate Tracking (Counter)
 * - Custom Business Metrics
 *
 * Alle Metriken werden automatisch an Prometheus exportiert durch die
 * bestehende monitoring-client Integration.
 */
@Configuration
class GatewayMetricsConfig {

    companion object {
        // Metric Namen als Konstanten für bessere Wartbarkeit
        const val GATEWAY_REQUEST_TIMER = "gateway_request_duration"
        const val GATEWAY_ERROR_COUNTER = "gateway_errors_total"
        const val GATEWAY_REQUESTS_COUNTER = "gateway_requests_total"
        const val GATEWAY_CIRCUIT_BREAKER_COUNTER = "gateway_circuit_breaker_events_total"
        const val GATEWAY_DOWNSTREAM_HEALTH_GAUGE = "gateway_downstream_health_status"
    }

    /**
     * Konfiguriert globale Meter-Registry Einstellungen für das Gateway.
     */
    @Bean
    fun gatewayMeterRegistryCustomizer(): MeterRegistryCustomizer<MeterRegistry> {
        return MeterRegistryCustomizer { registry ->
            // Gemeinsame Tags für alle Gateway-Metriken
            registry.config()
                .commonTags("service", "gateway", "component", "infrastructure")
                // Filterung von zu detaillierten Metriken
                .meterFilter(MeterFilter.deny { id ->
                    val name = id.name
                    // Ausschluss von internen Spring/Netty Metriken, die zu viel Rauschen erzeugen
                    name.startsWith("reactor.netty.connection.provider") ||
                    name.startsWith("reactor.netty.bytebuf.allocator") ||
                    name.startsWith("jvm.gc.overhead")
                })
                // Histogram-Buckets für Request Duration optimieren
                .meterFilter(MeterFilter.accept())
        }
    }

    /**
     * WebFilter für automatische Request/Response Zeit und Error Rate Tracking.
     *
     * Dieser Filter misst:
     * - Gesamte Request-Verarbeitungszeit
     * - Anzahl der Requests nach Status-Code kategorisiert
     * - Error-Rate basierend auf HTTP Status Codes
     */
    @Bean
    fun gatewayMetricsWebFilter(meterRegistry: MeterRegistry): WebFilter {
        return GatewayMetricsWebFilter(meterRegistry)
    }

    /**
     * Bean für Request Duration Timer - ermöglicht manuelle Messungen.
     */
    @Bean
    fun requestTimer(meterRegistry: MeterRegistry): Timer {
        return Timer.builder(GATEWAY_REQUEST_TIMER)
            .description("Gateway request processing time")
            .tag("type", "http")
            .register(meterRegistry)
    }

    /**
     * Bean für Error Counter - ermöglicht manuelles Error Tracking.
     */
    @Bean
    fun errorCounter(meterRegistry: MeterRegistry): Counter {
        return Counter.builder(GATEWAY_ERROR_COUNTER)
            .description("Total number of gateway errors")
            .register(meterRegistry)
    }

    /**
     * Bean für Request Counter - ermöglicht Request-Volumen Tracking.
     */
    @Bean
    fun requestCounter(meterRegistry: MeterRegistry): Counter {
        return Counter.builder(GATEWAY_REQUESTS_COUNTER)
            .description("Total number of gateway requests")
            .register(meterRegistry)
    }

    /**
     * Bean für Circuit Breaker Events Counter.
     */
    @Bean
    fun circuitBreakerCounter(meterRegistry: MeterRegistry): Counter {
        return Counter.builder(GATEWAY_CIRCUIT_BREAKER_COUNTER)
            .description("Circuit breaker events in gateway")
            .register(meterRegistry)
    }
}

/**
 * WebFilter Implementation für automatische Metrics-Erfassung.
 */
class GatewayMetricsWebFilter(private val meterRegistry: MeterRegistry) : WebFilter {

    override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
        val startTime = System.nanoTime()
        val request = exchange.request
        val path = request.path.value()
        val method = request.method.toString()

        // Request Counter incrementer
        Counter.builder(GatewayMetricsConfig.GATEWAY_REQUESTS_COUNTER)
            .tag("method", method)
            .tag("path", normalizePath(path))
            .description("Total gateway requests")
            .register(meterRegistry)
            .increment()

        return chain.filter(exchange)
            .doFinally { _ ->
                val duration = Duration.ofNanos(System.nanoTime() - startTime)
                val response = exchange.response
                val statusCode = response.statusCode?.value()?.toString() ?: "unknown"
                val statusSeries = when {
                    statusCode.startsWith("2") -> "2xx"
                    statusCode.startsWith("3") -> "3xx"
                    statusCode.startsWith("4") -> "4xx"
                    statusCode.startsWith("5") -> "5xx"
                    else -> "unknown"
                }

                // Request Duration Timer
                Timer.builder(GatewayMetricsConfig.GATEWAY_REQUEST_TIMER)
                    .tag("method", method)
                    .tag("path", normalizePath(path))
                    .tag("status", statusCode)
                    .tag("status_series", statusSeries)
                    .description("Gateway request processing time")
                    .register(meterRegistry)
                    .record(duration)

                // Error Counter für 4xx und 5xx Responses
                if (statusCode.startsWith("4") || statusCode.startsWith("5")) {
                    Counter.builder(GatewayMetricsConfig.GATEWAY_ERROR_COUNTER)
                        .tag("method", method)
                        .tag("path", normalizePath(path))
                        .tag("status", statusCode)
                        .tag("status_series", statusSeries)
                        .tag("error_type", if (statusCode.startsWith("4")) "client_error" else "server_error")
                        .description("Gateway error count")
                        .register(meterRegistry)
                        .increment()
                }
            }
    }

    /**
     * Normalisiert Pfade für Metriken, um Kardinalität-Explosion zu vermeiden.
     * Beispiel: /api/horses/123 → /api/horses/{id}
     */
    private fun normalizePath(path: String): String {
        return path
            // UUID pattern ersetzen
            .replace(Regex("/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"), "/{uuid}")
            // Numerische IDs ersetzen
            .replace(Regex("/\\d+"), "/{id}")
            // Sehr lange Pfade kürzen
            .let { if (it.length > 100) "${it.substring(0, 97)}..." else it }
    }
}
