import { AppErrorAction } from "./app-error-action.js"
import { AppErrorCode, AppErrorCodeKey } from "./app-error-code.js"
import { ErrorType } from "./error-type.js"

/**
 * Application error definition with all metadata
 */
export interface AppErrorDefinition {
  /** HTTP status code */
  httpErrorCode: number
  /** Unique application error code */
  appErrorCode: string
  /** Error message template (supports {0}, {1}, etc. placeholders) */
  message: string
  /** Action to take when error occurs */
  errorAction: AppErrorAction
  /** Short title for the error */
  title: string
  /** Error type category */
  errorType: ErrorType
  /** Reference documentation URL (optional) */
  referenceDoc: string | null
}

/**
 * Format a message template with arguments
 * Supports placeholders like {0}, {1}, etc.
 */
function formatMessage(template: string, args: unknown[]): string {
  return template.replace(/\{(\d+)\}/g, (match, index) => {
    const argIndex = parseInt(index, 10)
    return argIndex < args.length ? String(args[argIndex]) : match
  })
}

/**
 * Helper function to create an error definition
 */
function createError(
  httpErrorCode: number,
  errorCodeKey: AppErrorCodeKey,
  message: string,
  errorAction: AppErrorAction,
  title: string,
  errorType: ErrorType,
  referenceDoc: string | null = null,
): AppErrorDefinition {
  return {
    httpErrorCode,
    appErrorCode: AppErrorCode[errorCodeKey].code,
    message,
    errorAction,
    title,
    errorType,
    referenceDoc,
  }
}

/**
 * Application error definitions - similar to Java AppError enum
 * Each error has: HTTP code, app error code, message template, action, title, error type
 */
export const AppError = {
  // Generic Errors
  INVALID_PARAMETER: createError(
    400,
    "INVALID_PARAMETER",
    "Please enter a valid parameter {0}.",
    AppErrorAction.DEFAULT,
    "Invalid Parameter",
    ErrorType.ARGUMENT_ERROR,
  ),

  UNAUTHORIZED_ACCESS: createError(
    403,
    "UNAUTHORIZED_ACCESS",
    "Unauthorized access",
    AppErrorAction.DEFAULT,
    "Unauthorized Access",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  GENERIC_BAD_REQUEST: createError(
    400,
    "GENERIC_BAD_REQUEST",
    "The request was invalid or cannot be processed: {0}",
    AppErrorAction.DEFAULT,
    "Bad Request",
    ErrorType.BAD_REQUEST,
  ),

  GENERIC_NOT_FOUND: createError(
    404,
    "GENERIC_NOT_FOUND",
    "The requested resource was not found: {0}",
    AppErrorAction.DEFAULT,
    "Not Found",
    ErrorType.NOT_FOUND,
  ),

  GENERIC_INTERNAL_SERVER_ERROR: createError(
    500,
    "GENERIC_INTERNAL_SERVER_ERROR",
    "An unexpected error occurred. Please try again later: {0}",
    AppErrorAction.LOG_EXTERNALLY,
    "Internal Server Error",
    ErrorType.INTERNAL_SERVER_ERROR,
  ),

  // Validation Errors
  VALIDATION_FAILURE: createError(
    400,
    "VALIDATION_FAILURE",
    "Validation Failure(s): {0}",
    AppErrorAction.DEFAULT,
    "Validation Failed",
    ErrorType.VALIDATION_ERROR,
  ),

  MISSING_REQUIRED_FIELDS: createError(
    400,
    "MISSING_REQUIRED_FIELDS",
    "Missing required fields: {0}",
    AppErrorAction.DEFAULT,
    "Missing Required Fields",
    ErrorType.VALIDATION_ERROR,
  ),

  PAYLOAD_TOO_LARGE: createError(
    413,
    "PAYLOAD_TOO_LARGE",
    "The request payload is too large. Max allowed size is {0} KB",
    AppErrorAction.DEFAULT,
    "Payload Exceeds Max Allowed Size",
    ErrorType.CONNECTIVITY_ERROR,
  ),

  FILE_SIZE_EXCEEDED: createError(
    400,
    "FILE_SIZE_EXCEEDED",
    "File size should be less than {0}",
    AppErrorAction.DEFAULT,
    "File Size Exceeded",
    ErrorType.VALIDATION_ERROR,
  ),

  INVALID_FILE_TYPE: createError(
    400,
    "INVALID_FILE_TYPE",
    "Invalid file type. Only {0} formats are allowed.",
    AppErrorAction.DEFAULT,
    "Invalid File Type",
    ErrorType.VALIDATION_ERROR,
  ),

  CONFLICT: createError(
    409,
    "CONFLICT",
    "Resource conflict: {0}",
    AppErrorAction.DEFAULT,
    "Conflict",
    ErrorType.BAD_REQUEST,
  ),

  UNPROCESSABLE_ENTITY: createError(
    422,
    "UNPROCESSABLE_ENTITY",
    "Unable to process request: {0}",
    AppErrorAction.DEFAULT,
    "Unprocessable Entity",
    ErrorType.VALIDATION_ERROR,
  ),

  // Authentication Errors
  JWT_AUTHENTICATION_FAILURE: createError(
    401,
    "JWT_AUTHENTICATION_FAILURE",
    "JWT is either invalid or expired",
    AppErrorAction.DEFAULT,
    "JWT Authentication Error",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  INVALID_REFRESH_TOKEN: createError(
    400,
    "INVALID_REFRESH_TOKEN",
    "Refresh token is either invalid or expired",
    AppErrorAction.DEFAULT,
    "Refresh Token Authentication Error",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  INVALID_API_KEY: createError(
    401,
    "INVALID_API_KEY",
    "The provided API key is invalid",
    AppErrorAction.DEFAULT,
    "Invalid API Key",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  API_KEY_EXPIRED: createError(
    401,
    "API_KEY_EXPIRED",
    "The provided API key has expired",
    AppErrorAction.DEFAULT,
    "API Key Expired",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  USER_DISABLED: createError(
    403,
    "USER_DISABLED",
    "Your account has been disabled. Please contact support for assistance.",
    AppErrorAction.DEFAULT,
    "User Disabled",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  MISSING_AUTHORIZATION_HEADER: createError(
    401,
    "MISSING_AUTHORIZATION_HEADER",
    "Authorization header is missing",
    AppErrorAction.DEFAULT,
    "Missing Authorization",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  MISSING_USER_ID: createError(
    401,
    "MISSING_USER_ID",
    "User ID is missing",
    AppErrorAction.DEFAULT,
    "Missing User ID",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  UNAUTHORIZED: createError(
    401,
    "UNAUTHORIZED",
    "Unauthorized: {0}",
    AppErrorAction.DEFAULT,
    "Unauthorized",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  FORBIDDEN: createError(
    403,
    "FORBIDDEN",
    "Access forbidden: {0}",
    AppErrorAction.DEFAULT,
    "Forbidden",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  // Rate Limiting
  TOO_MANY_REQUESTS: createError(
    429,
    "TOO_MANY_REQUESTS",
    "Too many requests. Please try again later.",
    AppErrorAction.DEFAULT,
    "Too Many Requests",
    ErrorType.CONNECTIVITY_ERROR,
  ),

  // GitHub Integration Errors
  GITHUB_API_ERROR: createError(
    502,
    "GITHUB_API_ERROR",
    "GitHub API error: {0}",
    AppErrorAction.LOG_EXTERNALLY,
    "GitHub API Error",
    ErrorType.CONNECTIVITY_ERROR,
  ),

  GITHUB_INSTALLATION_NOT_FOUND: createError(
    404,
    "GITHUB_INSTALLATION_NOT_FOUND",
    "GitHub installation not found for {0}",
    AppErrorAction.DEFAULT,
    "GitHub Installation Not Found",
    ErrorType.NOT_FOUND,
  ),

  GITHUB_REPOSITORY_NOT_FOUND: createError(
    404,
    "GITHUB_REPOSITORY_NOT_FOUND",
    "GitHub repository not found: {0}",
    AppErrorAction.DEFAULT,
    "GitHub Repository Not Found",
    ErrorType.NOT_FOUND,
  ),

  GITHUB_WEBHOOK_VERIFICATION_FAILED: createError(
    401,
    "GITHUB_WEBHOOK_VERIFICATION_FAILED",
    "GitHub webhook signature verification failed",
    AppErrorAction.LOG_EXTERNALLY,
    "Webhook Verification Failed",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  GITHUB_NOT_COLLABORATOR: createError(
    403,
    "GITHUB_NOT_COLLABORATOR",
    "You are not a collaborator on this repository: {0}",
    AppErrorAction.DEFAULT,
    "Not a Collaborator",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  GITHUB_BRANCH_NOT_FOUND: createError(
    404,
    "GITHUB_BRANCH_NOT_FOUND",
    "Branch not found: {0}",
    AppErrorAction.DEFAULT,
    "Branch Not Found",
    ErrorType.NOT_FOUND,
  ),

  GITHUB_PR_NOT_FOUND: createError(
    404,
    "GITHUB_PR_NOT_FOUND",
    "Pull request not found: {0}",
    AppErrorAction.DEFAULT,
    "PR Not Found",
    ErrorType.NOT_FOUND,
  ),

  GITHUB_INSTALLATION_ERROR: createError(
    401,
    "GITHUB_INSTALLATION_ERROR",
    "GitHub App installation error: {0}",
    AppErrorAction.DEFAULT,
    "GitHub Installation Error",
    ErrorType.AUTHENTICATION_ERROR,
  ),

  // Optimization Errors
  OPTIMIZATION_NOT_FOUND: createError(
    404,
    "OPTIMIZATION_NOT_FOUND",
    "Optimization not found: {0}",
    AppErrorAction.DEFAULT,
    "Optimization Not Found",
    ErrorType.NOT_FOUND,
  ),

  OPTIMIZATION_ALREADY_EXISTS: createError(
    400,
    "OPTIMIZATION_ALREADY_EXISTS",
    "Optimization already exists for: {0}",
    AppErrorAction.DEFAULT,
    "Optimization Already Exists",
    ErrorType.BAD_REQUEST,
  ),

  OPTIMIZATION_LIMIT_EXCEEDED: createError(
    403,
    "OPTIMIZATION_LIMIT_EXCEEDED",
    "Optimization limit exceeded. You have used {0} of {1} optimizations.",
    AppErrorAction.DEFAULT,
    "Optimization Limit Exceeded",
    ErrorType.BAD_REQUEST,
  ),

  OPTIMIZATION_REJECTED: createError(
    403,
    "OPTIMIZATION_REJECTED",
    "This optimization request was rejected: {0}",
    AppErrorAction.DEFAULT,
    "Optimization Rejected",
    ErrorType.BAD_REQUEST,
  ),

  OPTIMIZATION_APPROVAL_ERROR: createError(
    500,
    "OPTIMIZATION_APPROVAL_ERROR",
    "Error checking approval status: {0}",
    AppErrorAction.LOG_EXTERNALLY,
    "Approval Error",
    ErrorType.INTERNAL_SERVER_ERROR,
  ),

  // Subscription Errors
  SUBSCRIPTION_NOT_FOUND: createError(
    404,
    "SUBSCRIPTION_NOT_FOUND",
    "Subscription not found for user: {0}",
    AppErrorAction.DEFAULT,
    "Subscription Not Found",
    ErrorType.NOT_FOUND,
  ),

  SUBSCRIPTION_EXPIRED: createError(
    403,
    "SUBSCRIPTION_EXPIRED",
    "Your subscription has expired. Please renew to continue.",
    AppErrorAction.DEFAULT,
    "Subscription Expired",
    ErrorType.BAD_REQUEST,
  ),

  SUBSCRIPTION_LIMIT_REACHED: createError(
    403,
    "SUBSCRIPTION_LIMIT_REACHED",
    "You have reached your subscription limit of {0} optimizations.",
    AppErrorAction.DEFAULT,
    "Subscription Limit Reached",
    ErrorType.BAD_REQUEST,
  ),

  SUBSCRIPTION_INACTIVE: createError(
    403,
    "SUBSCRIPTION_INACTIVE",
    "Subscription is not active",
    AppErrorAction.DEFAULT,
    "Subscription Inactive",
    ErrorType.BAD_REQUEST,
  ),

  // Database Errors
  DATABASE_CONNECTION_ERROR: createError(
    500,
    "DATABASE_CONNECTION_ERROR",
    "Database connection error: {0}",
    AppErrorAction.LOG_EXTERNALLY,
    "Database Connection Error",
    ErrorType.INTERNAL_SERVER_ERROR,
  ),

  DATABASE_QUERY_ERROR: createError(
    500,
    "DATABASE_QUERY_ERROR",
    "Database query error: {0}",
    AppErrorAction.LOG_EXTERNALLY,
    "Database Query Error",
    ErrorType.INTERNAL_SERVER_ERROR,
  ),

  DUPLICATE_KEY_ERROR: createError(
    400,
    "DUPLICATE_KEY_ERROR",
    "The key already exists: {0}",
    AppErrorAction.DEFAULT,
    "Duplicate Key",
    ErrorType.BAD_REQUEST,
  ),

  // External Service Errors
  EXTERNAL_SERVICE_ERROR: createError(
    502,
    "EXTERNAL_SERVICE_ERROR",
    "External service error ({0}): {1}",
    AppErrorAction.LOG_EXTERNALLY,
    "External Service Error",
    ErrorType.CONNECTIVITY_ERROR,
  ),

  EXTERNAL_SERVICE_TIMEOUT: createError(
    504,
    "EXTERNAL_SERVICE_TIMEOUT",
    "External service timeout ({0}): Request timed out after {1}ms",
    AppErrorAction.LOG_EXTERNALLY,
    "External Service Timeout",
    ErrorType.CONNECTIVITY_ERROR,
  ),
} as const

export type AppErrorKey = keyof typeof AppError

/**
 * Get formatted message for an error with arguments
 */
export function getErrorMessage(error: AppErrorDefinition, ...args: unknown[]): string {
  return formatMessage(error.message, args)
}

/**
 * Get error definition by key
 */
export function getErrorByKey(key: AppErrorKey): AppErrorDefinition {
  return AppError[key]
}
