// Package telemetry provides shared edge observability helpers used by the
// gateway transports and internal event consumers.
package telemetry

import (
	"net/http"
	"strings"

	"google.golang.org/grpc/codes"
)

// EdgeOutcome is the stable low-cardinality outcome vocabulary shared by REST,
// gRPC, push shutdown, and observability backends.
type EdgeOutcome string

const (
	EdgeOutcomeSuccess               EdgeOutcome = "success"
	EdgeOutcomeMalformedRequest      EdgeOutcome = "malformed_request"
	EdgeOutcomeRequestTooLarge       EdgeOutcome = "request_too_large"
	EdgeOutcomeUnsupportedProtocol   EdgeOutcome = "unsupported_protocol"
	EdgeOutcomeUnknownSession        EdgeOutcome = "unknown_session"
	EdgeOutcomeRevokedSession        EdgeOutcome = "revoked_session"
	EdgeOutcomeInvalidSignature      EdgeOutcome = "invalid_signature"
	EdgeOutcomeStaleRequest          EdgeOutcome = "stale_request"
	EdgeOutcomeReplayDetected        EdgeOutcome = "replay_detected"
	EdgeOutcomeRateLimited           EdgeOutcome = "rate_limited"
	EdgeOutcomePolicyDenied          EdgeOutcome = "policy_denied"
	EdgeOutcomeDownstreamUnavailable EdgeOutcome = "downstream_unavailable"
	EdgeOutcomeBackendUnavailable    EdgeOutcome = "backend_unavailable"
	EdgeOutcomeInternalError         EdgeOutcome = "internal_error"
	EdgeOutcomeGatewayShuttingDown   EdgeOutcome = "gateway_shutting_down"
)

// RejectReason returns the stable reject reason for outcome. Success does not
// produce a reject reason.
func RejectReason(outcome EdgeOutcome) string {
	if outcome == EdgeOutcomeSuccess {
		return ""
	}

	return string(outcome)
}

// OutcomeFromPublicErrorCode maps the stable public REST error envelope into
// the shared edge-outcome vocabulary.
func OutcomeFromPublicErrorCode(statusCode int, code string) EdgeOutcome {
	switch strings.TrimSpace(code) {
	case "":
		if statusCode < http.StatusBadRequest {
			return EdgeOutcomeSuccess
		}
		return EdgeOutcomeInternalError
	case "invalid_request", "method_not_allowed", "not_found":
		return EdgeOutcomeMalformedRequest
	case "request_too_large":
		return EdgeOutcomeRequestTooLarge
	case "rate_limited":
		return EdgeOutcomeRateLimited
	case "service_unavailable":
		return EdgeOutcomeBackendUnavailable
	default:
		if statusCode >= http.StatusInternalServerError {
			return EdgeOutcomeInternalError
		}
		return EdgeOutcomeMalformedRequest
	}
}

// OutcomeFromGRPCStatus maps the stable authenticated gRPC reject contract
// into the shared edge-outcome vocabulary.
func OutcomeFromGRPCStatus(code codes.Code, message string) EdgeOutcome {
	switch {
	case code == codes.OK:
		return EdgeOutcomeSuccess
	case code == codes.InvalidArgument:
		return EdgeOutcomeMalformedRequest
	case code == codes.FailedPrecondition && strings.Contains(message, "unsupported protocol_version"):
		return EdgeOutcomeUnsupportedProtocol
	case code == codes.Unauthenticated && message == "unknown device session":
		return EdgeOutcomeUnknownSession
	case code == codes.FailedPrecondition && message == "device session is revoked":
		return EdgeOutcomeRevokedSession
	case code == codes.Unauthenticated && message == "invalid request signature":
		return EdgeOutcomeInvalidSignature
	case code == codes.FailedPrecondition && message == "request timestamp is outside the freshness window":
		return EdgeOutcomeStaleRequest
	case code == codes.FailedPrecondition && message == "request replay detected":
		return EdgeOutcomeReplayDetected
	case code == codes.ResourceExhausted && message == "authenticated request rate limit exceeded":
		return EdgeOutcomeRateLimited
	case code == codes.PermissionDenied && message == "authenticated request rejected by edge policy":
		return EdgeOutcomePolicyDenied
	case code == codes.Unavailable && message == "downstream service is unavailable":
		return EdgeOutcomeDownstreamUnavailable
	case code == codes.Unavailable && message == "gateway is shutting down":
		return EdgeOutcomeGatewayShuttingDown
	case code == codes.Unavailable:
		return EdgeOutcomeBackendUnavailable
	default:
		return EdgeOutcomeInternalError
	}
}
