import { Type, type Static } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";

const uuidPattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$";

export const stableEngineeringErrors = {
  BROWSER_UNSUPPORTED: { httpStatus: 426, messageKey: "browser.unsupported" },
  MODEL_CONFIG_VERSION_CONFLICT: { httpStatus: 412, messageKey: "MODEL_CONFIG_VERSION_CONFLICT" },
  MODEL_DEFAULT_REPLACEMENT_REQUIRED: { httpStatus: 409, messageKey: "MODEL_DEFAULT_REPLACEMENT_REQUIRED" },
  MODEL_DEFAULT_REPLACEMENT_INVALID: { httpStatus: 409, messageKey: "MODEL_DEFAULT_REPLACEMENT_INVALID" },
  MODEL_RECOMMENDATION_PRIORITY_INVALID: { httpStatus: 400, messageKey: "MODEL_RECOMMENDATION_PRIORITY_INVALID" },
  MODEL_RECOMMENDATION_PRIORITY_CONFLICT: { httpStatus: 409, messageKey: "MODEL_RECOMMENDATION_PRIORITY_CONFLICT" },
  PRIVATE_CONTENT_NOTICE_ACK_REQUIRED: { httpStatus: 428, messageKey: "PRIVATE_CONTENT_NOTICE_ACK_REQUIRED" },
  ASSET_HISTORY_REFERENCE_CONFLICT: { httpStatus: 409, messageKey: "ASSET_HISTORY_REFERENCE_CONFLICT" },
  ASSET_CLEANUP_CANDIDATE_STALE: { httpStatus: 409, messageKey: "ASSET_CLEANUP_CANDIDATE_STALE" },
  STORAGE_CAPACITY_EXCEEDED: { httpStatus: 507, messageKey: "STORAGE_CAPACITY_EXCEEDED" },
  REGISTRATION_REJECTED: { httpStatus: 409, messageKey: "auth.registration.rejected" },
  REGISTRATION_REQUEST_INVALID: { httpStatus: 400, messageKey: "auth.registration.request_invalid" },
  IDEMPOTENCY_KEY_CONFLICT: { httpStatus: 409, messageKey: "request.idempotency_conflict" },
  AUTH_SESSION_INVALID: { httpStatus: 401, messageKey: "auth.session.invalid" },
  AUTH_SERVICE_UNAVAILABLE: { httpStatus: 503, messageKey: "auth.service.unavailable" },
  AUTH_ENTRY_REJECTED: { httpStatus: 409, messageKey: "auth.entry.rejected" },
  AUTH_RATE_LIMITED: { httpStatus: 429, messageKey: "auth.rate_limited" },
  AUTH_CSRF_INVALID: { httpStatus: 403, messageKey: "auth.csrf.invalid" },
} as const;

export type StableEngineeringErrorCode = keyof typeof stableEngineeringErrors;

export const CorrelationIdSchema = Type.String({ pattern: uuidPattern, $id: "CorrelationId" });
export const GenerationErrorCategorySchema = Type.Union(
  [
    Type.Literal("upstream_timeout"),
    Type.Literal("upstream_failed"),
    Type.Literal("safety_rejected"),
    Type.Literal("model_disabled"),
    Type.Literal("gateway_balance_insufficient"),
    Type.Literal("gateway_contract_invalid"),
    Type.Literal("reference_invalid"),
    Type.Literal("unknown_retryable"),
    Type.Literal("unknown_non_retryable"),
  ],
  { $id: "GenerationErrorCategory" },
);
export const StableEngineeringErrorCodeSchema = Type.Union(
  [
    Type.Literal("BROWSER_UNSUPPORTED"),
    Type.Literal("MODEL_CONFIG_VERSION_CONFLICT"),
    Type.Literal("MODEL_DEFAULT_REPLACEMENT_REQUIRED"),
    Type.Literal("MODEL_DEFAULT_REPLACEMENT_INVALID"),
    Type.Literal("MODEL_RECOMMENDATION_PRIORITY_INVALID"),
    Type.Literal("MODEL_RECOMMENDATION_PRIORITY_CONFLICT"),
    Type.Literal("PRIVATE_CONTENT_NOTICE_ACK_REQUIRED"),
    Type.Literal("ASSET_HISTORY_REFERENCE_CONFLICT"),
    Type.Literal("ASSET_CLEANUP_CANDIDATE_STALE"),
    Type.Literal("STORAGE_CAPACITY_EXCEEDED"),
    Type.Literal("REGISTRATION_REJECTED"),
    Type.Literal("REGISTRATION_REQUEST_INVALID"),
    Type.Literal("IDEMPOTENCY_KEY_CONFLICT"),
    Type.Literal("AUTH_SESSION_INVALID"),
    Type.Literal("AUTH_SERVICE_UNAVAILABLE"),
    Type.Literal("AUTH_ENTRY_REJECTED"),
    Type.Literal("AUTH_RATE_LIMITED"),
    Type.Literal("AUTH_CSRF_INVALID"),
  ],
  { $id: "StableEngineeringErrorCode" },
);
export const ErrorDetailsSchema = Type.Object(
  {
    reason: Type.Optional(
      Type.Union([
        Type.Literal("platform_unsupported"),
        Type.Literal("brand_unsupported"),
        Type.Literal("version_unsupported"),
        Type.Literal("identity_unavailable"),
      ]),
    ),
    supported_browsers: Type.Optional(
      Type.Array(
        Type.Object(
          {
            brand: Type.Union([Type.Literal("Google Chrome"), Type.Literal("Microsoft Edge")]),
            major: Type.Integer({ minimum: 1 }),
          },
          { additionalProperties: false },
        ),
        { maxItems: 2 },
      ),
    ),
    capacity_status: Type.Optional(
      Type.Union([
        Type.Literal("normal"),
        Type.Literal("warning"),
        Type.Literal("critical"),
        Type.Literal("full"),
        Type.Literal("unavailable"),
      ]),
    ),
    current_task_ref: Type.Optional(Type.String({ maxLength: 160, pattern: "^[a-z][a-z0-9_]*:[A-Za-z0-9_-]+$" })),
    field_errors: Type.Optional(
      Type.Array(
        Type.Object(
          {
            field: Type.String({ maxLength: 120, pattern: "^[A-Za-z0-9_.\\[\\]-]+$" }),
            message_key: Type.String({ maxLength: 120, pattern: "^[A-Za-z0-9_.-]+$" }),
          },
          { additionalProperties: false },
        ),
        { maxItems: 32 },
      ),
    ),
    conflict_model_ids: Type.Optional(Type.Array(Type.String({ maxLength: 160, minLength: 1 }), { maxItems: 3 })),
    latest_version: Type.Optional(Type.Union([Type.Integer({ minimum: 0 }), Type.String({ maxLength: 120 })])),
    remaining_bytes: Type.Optional(Type.Integer({ minimum: 0 })),
  },
  { additionalProperties: false, $id: "ErrorDetails" },
);
export const ErrorEnvelopeSchema = Type.Object(
  {
    error: Type.Object(
      {
        code: StableEngineeringErrorCodeSchema,
        message_key: Type.String({ maxLength: 120, pattern: "^[A-Za-z0-9_.-]+$" }),
        correlation_id: CorrelationIdSchema,
        error_category: Type.Optional(GenerationErrorCategorySchema),
        details: ErrorDetailsSchema,
      },
      { additionalProperties: false },
    ),
  },
  { additionalProperties: false, $id: "ErrorEnvelope" },
);

export type ErrorDetails = Static<typeof ErrorDetailsSchema>;
export type ErrorEnvelope = Static<typeof ErrorEnvelopeSchema>;
export type GenerationErrorCategory = Static<typeof GenerationErrorCategorySchema>;

export function isCorrelationId(value: unknown): value is string {
  return Value.Check(CorrelationIdSchema, value);
}

export function isErrorEnvelope(value: unknown): value is ErrorEnvelope {
  if (!Value.Check(ErrorEnvelopeSchema, value)) return false;
  const definition = stableEngineeringErrors[value.error.code];
  return definition.messageKey === value.error.message_key;
}

export function createErrorEnvelope(input: {
  code: StableEngineeringErrorCode;
  correlationId: string;
  details?: ErrorDetails;
  errorCategory?: GenerationErrorCategory;
}): ErrorEnvelope {
  const definition = stableEngineeringErrors[input.code];
  const envelope: ErrorEnvelope = {
    error: {
      code: input.code,
      correlation_id: input.correlationId,
      details: input.details ?? {},
      message_key: definition.messageKey,
      ...(input.errorCategory ? { error_category: input.errorCategory } : {}),
    },
  };
  if (!isErrorEnvelope(envelope)) throw new Error("Error envelope does not match the frozen schema.");
  return envelope;
}
