"""Stable licensing API and domain failures."""

from __future__ import annotations

from enum import Enum
from typing import Optional


class ServerErrorCode(str, Enum):
    INVALID_REQUEST = "invalid_request"
    AUTHENTICATION_REQUIRED = "authentication_required"
    AUTHORIZATION_DENIED = "authorization_denied"
    REAUTHENTICATION_REQUIRED = "reauthentication_required"
    IDEMPOTENCY_CONFLICT = "idempotency_conflict"
    SERIAL_INVALID = "serial_invalid"
    SERIAL_NOT_FOUND = "serial_not_found"
    SERIAL_BATCH_NOT_FOUND = "serial_batch_not_found"
    SERIAL_ALREADY_REDEEMED = "serial_already_redeemed"
    SERIAL_RESERVED = "serial_reserved"
    SERIAL_EXPIRED = "serial_expired"
    SKU_NOT_AVAILABLE = "sku_not_available"
    ACCOUNT_REQUIRED = "account_required"
    TRIAL_NOT_ELIGIBLE = "trial_not_eligible"
    DEVICE_LIMIT_REACHED = "device_limit_reached"
    SEAT_LIMIT_REACHED = "seat_limit_reached"
    SEAT_NOT_ASSIGNED = "seat_not_assigned"
    SEAT_REASSIGNMENT_REVIEW = "seat_reassignment_review"
    DEVICE_CHURN_REVIEW = "device_churn_review"
    FORCED_RELEASE_ALLOWANCE_EXHAUSTED = "forced_release_allowance_exhausted"
    DEVICE_NOT_FOUND = "device_not_found"
    DEVICE_RECOVERY_REQUIRED = "device_recovery_required"
    DEVICE_IDENTITY_REPLACEMENT_REQUIRED = "device_identity_replacement_required"
    LICENSE_NOT_FOUND = "license_not_found"
    SURFACE_PROFILE_NOT_FOUND = "surface_profile_not_found"
    GRANT_NOT_ACTIVE = "grant_not_active"
    CATALOG_UNAVAILABLE = "catalog_unavailable"
    SIGNING_UNAVAILABLE = "signing_unavailable"
    IDENTITY_UNAVAILABLE = "identity_unavailable"
    SUBSCRIPTION_INACTIVE = "subscription_inactive"
    BILLING_UNAVAILABLE = "billing_unavailable"
    RATE_LIMITED = "rate_limited"
    RATE_LIMIT_UNAVAILABLE = "rate_limit_unavailable"
    OFFLINE_REQUEST_NOT_FOUND = "offline_request_not_found"
    OFFLINE_REQUEST_EXPIRED = "offline_request_expired"
    CONFLICT = "conflict"
    INTERNAL_ERROR = "internal_error"


class ServerLicensingError(RuntimeError):
    def __init__(
        self,
        code: ServerErrorCode,
        message: str,
        *,
        status_code: int = 400,
        retryable: bool = False,
        existing_resource_id: Optional[str] = None,
        retry_after_seconds: Optional[int] = None,
    ) -> None:
        if not isinstance(code, ServerErrorCode):
            raise TypeError("code must be a ServerErrorCode")
        if not isinstance(message, str) or not message.strip():
            raise ValueError("message must be a non-empty string")
        if isinstance(status_code, bool) or not isinstance(status_code, int):
            raise TypeError("status_code must be an integer")
        if status_code < 400 or status_code > 599:
            raise ValueError("status_code must be an HTTP error status")
        if not isinstance(retryable, bool):
            raise TypeError("retryable must be a Boolean")
        if existing_resource_id is not None and not isinstance(existing_resource_id, str):
            raise TypeError("existing_resource_id must be a string or None")
        if retry_after_seconds is not None:
            if isinstance(retry_after_seconds, bool) or not isinstance(
                retry_after_seconds,
                int,
            ):
                raise TypeError("retry_after_seconds must be an integer or None")
            if retry_after_seconds < 1:
                raise ValueError("retry_after_seconds must be at least one")
        super().__init__(message.strip())
        self.code = code
        self.status_code = status_code
        self.retryable = retryable
        self.existing_resource_id = existing_resource_id
        self.retry_after_seconds = retry_after_seconds


__all__ = ["ServerErrorCode", "ServerLicensingError"]
