package connector_webhooks

import (
	"crypto/sha256"
	"encoding/hex"
	"time"

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

var connectorWebhookAdmissionDecisionsTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_webhook_admission_decisions_total",
		Help: "Connector webhook admission decisions by outcome and generic reason.",
	},
	[]string{"outcome", "reason"},
)

var connectorWebhookNoReceipt503Total = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_webhook_no_receipt_503_total",
		Help: "Connector webhook 503 responses before a durable verified ingress receipt exists.",
	},
	[]string{"reason"},
)

var connectorWebhookAckDropTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_webhook_ack_drop_total",
		Help: "Connector webhook controlled-unavailable responses acknowledged as contract drops.",
	},
	[]string{"verification", "reason"},
)

var connectorWebhookStageDurationSeconds = promauto.NewHistogramVec(
	prometheus.HistogramOpts{
		Name:    "nexuscrm_connector_webhook_stage_duration_seconds",
		Help:    "Connector webhook dispatch stage duration by generic stage and outcome.",
		Buckets: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30},
	},
	[]string{"stage", "outcome", "receipt_mode"},
)

var connectorWebhookIngressSnapshotCacheTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_webhook_ingress_snapshot_cache_total",
		Help: "Connector webhook receipt-mode ingress snapshot cache decisions by generic part.",
	},
	[]string{"part", "outcome"},
)

var connectorWebhookDispatchInflight = promauto.NewGauge(
	prometheus.GaugeOpts{
		Name: "nexuscrm_connector_webhook_dispatch_inflight",
		Help: "Connector webhook requests currently executing, including unverified requests.",
	},
)

var connectorWebhookVerifiedEndpointInflight = promauto.NewGaugeVec(
	prometheus.GaugeOpts{
		Name: "nexuscrm_connector_webhook_verified_endpoint_inflight",
		Help: "Authenticated connector webhook requests currently executing by opaque endpoint identity.",
	},
	[]string{"endpoint_hash", "receipt_mode"},
)

var connectorWebhookVerifiedEndpointDurationSeconds = promauto.NewHistogramVec(
	prometheus.HistogramOpts{
		Name:    "nexuscrm_connector_webhook_verified_endpoint_duration_seconds",
		Help:    "End-to-end connector webhook response duration for authenticated requests by opaque endpoint identity and outcome.",
		Buckets: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30},
	},
	[]string{"endpoint_hash", "outcome", "receipt_mode"},
)

var connectorWebhookVerifiedEndpointResponsesTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_webhook_verified_endpoint_responses_total",
		Help: "Connector webhook responses for authenticated requests by opaque endpoint identity and outcome.",
	},
	[]string{"endpoint_hash", "outcome", "receipt_mode"},
)

var connectorReceiptRouterStageDurationSeconds = promauto.NewHistogramVec(
	prometheus.HistogramOpts{
		Name:    "nexuscrm_connector_receipt_router_stage_duration_seconds",
		Help:    "Connector verified ingress receipt router stage duration by generic stage and outcome.",
		Buckets: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30},
	},
	[]string{"stage", "outcome"},
)

var connectorMaterializerStageDurationSeconds = promauto.NewHistogramVec(
	prometheus.HistogramOpts{
		Name:    "nexuscrm_connector_materializer_stage_duration_seconds",
		Help:    "Connector verified ingress materializer stage duration by generic stage and outcome.",
		Buckets: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30},
	},
	[]string{"stage", "outcome"},
)

var connectorMaterializerRouteCacheTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_materializer_route_cache_total",
		Help: "Connector verified ingress materializer route cache decisions.",
	},
	[]string{"outcome"},
)

var connectorMaterializerParserSingleflightTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_materializer_parser_cache_total",
		Help: "Connector verified ingress materializer parser singleflight decisions. Metric name is retained for compatibility.",
	},
	[]string{"outcome"},
)

var connectorMaterializerCompanionSingleflightTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_materializer_companion_singleflight_total",
		Help: "Connector verified ingress materializer companion resolver singleflight decisions.",
	},
	[]string{"outcome"},
)

var connectorMaterializerConsentSingleflightTotal = promauto.NewCounterVec(
	prometheus.CounterOpts{
		Name: "nexuscrm_connector_materializer_consent_singleflight_total",
		Help: "Connector verified ingress materializer consent checker singleflight decisions.",
	},
	[]string{"outcome"},
)

func recordConnectorWebhookAdmission(decision webhookAdmissionDecision) {
	outcome := "rejected"
	if decision.Allow {
		outcome = "accepted"
	}
	reason := decision.Reason
	if reason == "" {
		reason = "unknown"
	}
	connectorWebhookAdmissionDecisionsTotal.WithLabelValues(outcome, reason).Inc()
}

func recordConnectorWebhookNoReceipt503(reason string) {
	if reason == "" {
		reason = "unknown"
	}
	connectorWebhookNoReceipt503Total.WithLabelValues(reason).Inc()
}

func recordConnectorWebhookAckDrop(verification, reason string) {
	connectorWebhookAckDropTotal.WithLabelValues(verification, reason).Inc()
}

func recordConnectorWebhookStageDuration(stage string, outcome string, receiptMode bool, seconds float64) {
	if stage == "" {
		stage = "unknown"
	}
	if outcome == "" {
		outcome = "unknown"
	}
	mode := "false"
	if receiptMode {
		mode = "true"
	}
	connectorWebhookStageDurationSeconds.WithLabelValues(stage, outcome, mode).Observe(seconds)
}

func recordConnectorWebhookIngressSnapshotCache(part string, outcome string) {
	if part == "" {
		part = "unknown"
	}
	if outcome == "" {
		outcome = "unknown"
	}
	connectorWebhookIngressSnapshotCacheTotal.WithLabelValues(part, outcome).Inc()
}

type verifiedWebhookEndpointTelemetry struct {
	endpointHash string
	receiptMode  string
	startedAt    time.Time
}

func startVerifiedWebhookEndpointTelemetry(namespace, secretName string, receiptMode bool, startedAt time.Time) *verifiedWebhookEndpointTelemetry {
	endpointHash := webhookEndpointMetricHash(namespace, secretName)
	if endpointHash == "" {
		return nil
	}
	mode := webhookReceiptModeLabel(receiptMode)
	connectorWebhookVerifiedEndpointInflight.WithLabelValues(endpointHash, mode).Inc()
	return &verifiedWebhookEndpointTelemetry{
		endpointHash: endpointHash,
		receiptMode:  mode,
		startedAt:    startedAt,
	}
}

func (t *verifiedWebhookEndpointTelemetry) finish(status int) {
	if t == nil {
		return
	}
	outcome := webhookDispatchOutcome(status)
	connectorWebhookVerifiedEndpointInflight.WithLabelValues(t.endpointHash, t.receiptMode).Dec()
	connectorWebhookVerifiedEndpointDurationSeconds.WithLabelValues(t.endpointHash, outcome, t.receiptMode).Observe(time.Since(t.startedAt).Seconds())
	connectorWebhookVerifiedEndpointResponsesTotal.WithLabelValues(t.endpointHash, outcome, t.receiptMode).Inc()
}

func webhookEndpointMetricHash(namespace, secretName string) string {
	endpoint := canonicalWebhookEndpoint(namespace, secretName)
	if endpoint == "" {
		return ""
	}
	sum := sha256.Sum256([]byte(endpoint))
	return hex.EncodeToString(sum[:8])
}

func webhookReceiptModeLabel(receiptMode bool) string {
	if receiptMode {
		return "true"
	}
	return "false"
}

func recordConnectorReceiptRouterStageDuration(stage string, outcome string, seconds float64) {
	if stage == "" {
		stage = "unknown"
	}
	if outcome == "" {
		outcome = "unknown"
	}
	connectorReceiptRouterStageDurationSeconds.WithLabelValues(stage, outcome).Observe(seconds)
}

func recordConnectorMaterializerStageDuration(stage string, outcome string, seconds float64) {
	if stage == "" {
		stage = "unknown"
	}
	if outcome == "" {
		outcome = "unknown"
	}
	connectorMaterializerStageDurationSeconds.WithLabelValues(stage, outcome).Observe(seconds)
}

func recordConnectorMaterializerRouteCache(outcome string) {
	if outcome == "" {
		outcome = "unknown"
	}
	connectorMaterializerRouteCacheTotal.WithLabelValues(outcome).Inc()
}

func recordConnectorMaterializerParserSingleflight(outcome string) {
	if outcome == "" {
		outcome = "unknown"
	}
	connectorMaterializerParserSingleflightTotal.WithLabelValues(outcome).Inc()
}

func recordConnectorMaterializerCompanionSingleflight(outcome string) {
	if outcome == "" {
		outcome = "unknown"
	}
	connectorMaterializerCompanionSingleflightTotal.WithLabelValues(outcome).Inc()
}

func recordConnectorMaterializerConsentSingleflight(outcome string) {
	if outcome == "" {
		outcome = "unknown"
	}
	connectorMaterializerConsentSingleflightTotal.WithLabelValues(outcome).Inc()
}
