package metrics

import (
	"errors"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

type DaemonTransport struct {
	requests       *prometheus.CounterVec
	requestLatency *prometheus.HistogramVec
	routeDecisions *prometheus.CounterVec
	elasticEvents  *prometheus.CounterVec
	warmEvents     *prometheus.CounterVec
	elasticPool    *prometheus.GaugeVec
}

func RegisterDaemonTransport(reg prometheus.Registerer) (*DaemonTransport, error) {
	if reg == nil {
		return nil, errors.New("prometheus registerer is nil")
	}
	transport := &DaemonTransport{
		requests: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name: "agentteam_daemon_transport_requests_total",
				Help: "Total number of daemon Remote Endpoint requests.",
			},
			[]string{"endpoint", "link_mode", "outcome", "fallback"},
		),
		requestLatency: prometheus.NewHistogramVec(
			prometheus.HistogramOpts{
				Name:    "agentteam_daemon_transport_request_duration_seconds",
				Help:    "Daemon Remote Endpoint request duration in seconds.",
				Buckets: BucketsLatencySeconds,
			},
			[]string{"endpoint", "link_mode", "outcome"},
		),
		routeDecisions: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name: "agentteam_daemon_transport_route_decisions_total",
				Help: "Total number of daemon Remote Endpoint LinkPolicy route decisions.",
			},
			[]string{"endpoint", "link_policy", "mode", "reason"},
		),
		elasticEvents: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name: "agentteam_daemon_transport_elastic_events_total",
				Help: "Total number of Elastic Request Link lifecycle events.",
			},
			[]string{"event", "reason"},
		),
		warmEvents: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name: "agentteam_daemon_transport_warm_events_total",
				Help: "Total number of user-active Elastic Request Link warm-pool events.",
			},
			[]string{"origin", "profile", "warm_event", "reason"},
		),
		elasticPool: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Name: "agentteam_daemon_transport_elastic_pool",
				Help: "Current Elastic Request Link pool size by bounded state.",
			},
			[]string{"state"},
		),
	}
	for _, collector := range []prometheus.Collector{
		transport.requests,
		transport.requestLatency,
		transport.routeDecisions,
		transport.elasticEvents,
		transport.warmEvents,
		transport.elasticPool,
	} {
		if err := reg.Register(collector); err != nil {
			return nil, err
		}
	}
	for _, state := range []string{"grants", "links", "assignments"} {
		transport.SetElasticPool(state, 0)
	}
	return transport, nil
}

func (transport *DaemonTransport) RecordRemoteRequest(endpoint string, linkMode string, outcome string, fallback string, duration time.Duration) {
	if transport == nil {
		return
	}
	endpoint = daemonTransportEndpointLabel(endpoint)
	linkMode = daemonTransportEnumLabel(linkMode, daemonTransportLinkModes)
	outcome = daemonTransportEnumLabel(outcome, daemonTransportOutcomes)
	fallback = daemonTransportEnumLabel(fallback, daemonTransportFallbacks)
	transport.requests.WithLabelValues(endpoint, linkMode, outcome, fallback).Inc()
	if duration < 0 {
		duration = 0
	}
	transport.requestLatency.WithLabelValues(endpoint, linkMode, outcome).Observe(duration.Seconds())
}

func (transport *DaemonTransport) RecordRouteDecision(endpoint string, linkPolicy string, mode string, reason string) {
	if transport == nil {
		return
	}
	transport.routeDecisions.WithLabelValues(
		daemonTransportEndpointLabel(endpoint),
		daemonTransportEnumLabel(linkPolicy, daemonTransportLinkPolicies),
		daemonTransportEnumLabel(mode, daemonTransportRouteModes),
		daemonTransportEnumLabel(reason, daemonTransportReasons),
	).Inc()
}

func (transport *DaemonTransport) RecordElasticEvent(event string, reason string) {
	if transport == nil {
		return
	}
	transport.elasticEvents.WithLabelValues(
		daemonTransportEnumLabel(event, daemonTransportElasticEvents),
		daemonTransportEnumLabel(reason, daemonTransportReasons),
	).Inc()
}

func (transport *DaemonTransport) RecordWarmEvent(origin string, profile string, event string, reason string) {
	if transport == nil {
		return
	}
	// Warm-pool labels are intentionally closed enums: origin/profile/event/
	// reason are enough to tune the policy without exposing workspace, machine,
	// user, link, request, token, URL, or filesystem identifiers.
	transport.warmEvents.WithLabelValues(
		daemonTransportWarmEnumLabel(origin, daemonTransportWarmOrigins),
		daemonTransportWarmEnumLabel(profile, daemonTransportWarmProfiles),
		daemonTransportWarmEnumLabel(event, daemonTransportWarmEvents),
		daemonTransportWarmEnumLabel(reason, daemonTransportWarmReasons),
	).Inc()
}

func (transport *DaemonTransport) SetElasticPool(state string, value float64) {
	if transport == nil {
		return
	}
	if value < 0 {
		value = 0
	}
	transport.elasticPool.WithLabelValues(daemonTransportEnumLabel(state, daemonTransportPoolStates)).Set(value)
}

func daemonTransportEndpointLabel(endpoint string) string {
	endpoint = strings.TrimSpace(endpoint)
	if endpoint == "" || len(endpoint) > 96 {
		return labelUnknown
	}
	for _, r := range endpoint {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '.' {
			continue
		}
		return labelUnknown
	}
	return endpoint
}

func daemonTransportEnumLabel(value string, allowed map[string]struct{}) string {
	value = strings.TrimSpace(strings.ToLower(value))
	if value == "" {
		return labelUnknown
	}
	if _, ok := allowed[value]; ok {
		return value
	}
	return labelUnknown
}

func daemonTransportWarmEnumLabel(value string, allowed map[string]struct{}) string {
	value = strings.TrimSpace(strings.ToLower(value))
	if value == "" {
		return "unknown"
	}
	if _, ok := allowed[value]; ok {
		return value
	}
	return "unknown"
}

func daemonTransportSet(values ...string) map[string]struct{} {
	set := make(map[string]struct{}, len(values)+1)
	set[labelUnknown] = struct{}{}
	for _, value := range values {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			set[trimmed] = struct{}{}
		}
	}
	return set
}

func daemonTransportWarmSet(values ...string) map[string]struct{} {
	set := make(map[string]struct{}, len(values)+1)
	set["unknown"] = struct{}{}
	for _, value := range values {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			set[trimmed] = struct{}{}
		}
	}
	return set
}

var (
	daemonTransportLinkModes = daemonTransportSet(
		"session_link",
		"elastic_request_link",
		"legacy_session_link",
		"none",
	)
	daemonTransportRouteModes = daemonTransportSet(
		"session_link",
		"elastic_request_link",
		"open_elastic_request_link",
		"legacy_session_link_fallback",
		"stable_error",
	)
	daemonTransportLinkPolicies = daemonTransportSet(
		"session_only",
		"session_preferred",
		"elastic_preferred",
		"elastic_required",
	)
	daemonTransportOutcomes = daemonTransportSet(
		"success",
		"failure",
		"timeout",
		"canceled",
		"busy",
		"unavailable",
	)
	daemonTransportFallbacks = daemonTransportSet(
		"none",
		"route_fallback",
		"safe_retry",
		"open_failed",
		"connect_timeout",
		"send_open_failed",
	)
	daemonTransportElasticEvents = daemonTransportSet(
		"grant_created",
		"grant_cleaned",
		"link_accepted",
		"link_rejected",
		"link_closed",
		"link_drained",
		"assignment_created",
		"assignment_completed",
		"assignment_cleaned",
		"cleanup_expired_grant",
		"cleanup_closed_link",
		"cleanup_canceled_assignment",
	)
	daemonTransportWarmOrigins = daemonTransportWarmSet(
		"background",
		"user_interactive",
		"system_internal",
	)
	daemonTransportWarmProfiles = daemonTransportWarmSet(
		"user_interactive_read_review_v1",
	)
	daemonTransportWarmEvents = daemonTransportWarmSet(
		"lease_refreshed",
		"exists",
		"grant_created",
		"connected",
		"cooldown",
		"skipped_capacity",
		"skipped_background",
		"skipped_ineligible",
		"drained",
		"closed",
	)
	daemonTransportPoolStates = daemonTransportSet(
		"grants",
		"links",
		"assignments",
	)
	daemonTransportReasons = daemonTransportSet(
		"ok",
		"unknown",
		"invalid_endpoint_timeout",
		"session_only",
		"session_link_within_policy",
		"session_link_unavailable",
		"missing_transport_capability",
		"endpoint_registry_mismatch",
		"elastic_request_disabled",
		"idle_elastic_request_link",
		"elastic_request_link_capacity",
		"open_elastic_request_link",
		"unknown_link",
		"missing_token",
		"malformed_token",
		"invalid_token",
		"expired",
		"replayed",
		"link_mismatch",
		"workspace_mismatch",
		"machine_mismatch",
		"generation_mismatch",
		"node_mismatch",
		"registry_mismatch",
		"protocol_mismatch",
		"capacity",
		"transport",
		"session",
		"profile",
		"background",
		"user_interactive",
		"system_internal",
		"web_api",
		"task_scope",
		"service_agentteam",
		"lease_expired",
		"backoff",
		"completed",
		"send_failed",
		"deadline_exceeded",
		"assignment_timeout",
		"connect_timeout",
		"canceled",
		"session_generation_changed",
		"session_disconnected",
		"link_disconnected",
		"idle_timeout",
		"max_age",
		"drain",
		"drain_after_assignment",
		"manual",
	)
	daemonTransportWarmReasons = daemonTransportWarmSet(
		"ok",
		"transport",
		"session",
		"profile",
		"capacity",
		"backoff",
		"lease_expired",
		"web_api",
		"task_scope",
		"service_agentteam",
		"background",
		"user_interactive",
		"system_internal",
		"connect_timeout",
		"token_reject",
		"send_open_failed",
		"dial_failed",
		"protocol_reject",
		"expired",
		"replayed",
		"node_mismatch",
		"registry_mismatch",
		"protocol_mismatch",
		"invalid_token",
		"malformed_token",
		"drain",
		"drain_after_assignment",
		"max_age",
		"idle_timeout",
	)
)
