use std::sync::OnceLock;
use std::time::Duration;

use efdn_telem::metrics::__private;
use efdn_telem::metrics::MetricsHandle;
use metrics::SharedString;

use crate::GatewayError;
use crate::health::{HealthGateStatus, HealthHandle};

static REQUESTS: OnceLock<&'static str> = OnceLock::new();
static REQUEST_DURATION: OnceLock<&'static str> = OnceLock::new();
static SERVICE_LIVE: OnceLock<&'static str> = OnceLock::new();
static SERVICE_READY: OnceLock<&'static str> = OnceLock::new();
static SERVICE_STARTED: OnceLock<&'static str> = OnceLock::new();
static GATE_STATUS: OnceLock<&'static str> = OnceLock::new();

pub fn describe() {
    __private::describe_counter(
        requests_name(),
        "Telemetry gateway HTTP requests by route and status",
    );
    __private::describe_duration_histogram(
        request_duration_name(),
        "Telemetry gateway HTTP request duration",
    );
    __private::describe_gauge(service_live_name(), "Service liveness state");
    __private::describe_gauge(service_ready_name(), "Service readiness state");
    __private::describe_gauge(service_started_name(), "Service startup state");
    __private::describe_gauge(
        gate_status_name(),
        "Readiness gate state: 2 pass, 1 warn, 0 fail",
    );
}

pub fn record_request(route: &'static str, status: u16, elapsed: Duration) {
    let status = SharedString::from(status.to_string());
    let route = SharedString::from(route);
    __private::counter(
        requests_name(),
        [("route", route.clone()), ("status", status)],
    )
    .increment(1);
    __private::duration_histogram(request_duration_name(), [("route", route)]).record(elapsed);
}

pub fn record_health(health: &HealthHandle) {
    let report = health.report();
    gauge(service_live_name()).set(as_gauge(report.is_live()));
    gauge(service_ready_name()).set(as_gauge(report.is_ready()));
    gauge(service_started_name()).set(as_gauge(report.is_started()));
    for gate in report.gates() {
        record_gate(gate.name().as_str(), gate.status());
    }
}

pub fn render(handle: &MetricsHandle, health: &HealthHandle) -> Result<String, GatewayError> {
    record_health(health);
    handle.render().map_err(GatewayError::from)
}

fn record_gate(name: &str, status: &HealthGateStatus) {
    __private::gauge(
        gate_status_name(),
        [("gate", SharedString::from(name.to_owned()))],
    )
    .set(status.metric_value());
}

fn gauge(name: &'static str) -> metrics::Gauge {
    __private::gauge(name, [])
}

fn as_gauge(value: bool) -> f64 {
    if value { 1.0 } else { 0.0 }
}

fn requests_name() -> &'static str {
    REQUESTS.get_or_init(|| __private::metric_name("gateway", "requests_total"))
}

fn request_duration_name() -> &'static str {
    REQUEST_DURATION.get_or_init(|| __private::metric_name("gateway", "request_duration_seconds"))
}

fn service_live_name() -> &'static str {
    SERVICE_LIVE.get_or_init(|| __private::metric_name("gateway", "service_live"))
}

fn service_ready_name() -> &'static str {
    SERVICE_READY.get_or_init(|| __private::metric_name("gateway", "service_ready"))
}

fn service_started_name() -> &'static str {
    SERVICE_STARTED.get_or_init(|| __private::metric_name("gateway", "service_started"))
}

fn gate_status_name() -> &'static str {
    GATE_STATUS.get_or_init(|| __private::metric_name("gateway", "health_gate"))
}
