/**
 * Unified Error Handling for Portal
 *
 * Clean, simple error handling that uses shared error codes from domain package.
 * Provides consistent error parsing and user-friendly messages.
 */

import { ApiError as ClientApiError, isApiError, parseDomainError } from "@/core/api";
import {
  ErrorCode,
  ErrorMessages,
  ErrorMetadata,
  mapHttpStatusToErrorCode,
  type ErrorCodeType,
} from "@customer-portal/domain/common";

// ============================================================================
// Types
// ============================================================================

export interface ParsedError {
  code: ErrorCodeType;
  message: string;
  shouldLogout: boolean;
  shouldRetry: boolean;
  fieldErrors?: Record<string, string> | undefined;
}

// ============================================================================
// Error Parsing
// ============================================================================

/**
 * Parse any error into a structured format with code and user-friendly message.
 * This is the main entry point for error handling.
 */
export function parseError(error: unknown): ParsedError {
  // Handle API client errors
  if (isApiError(error)) {
    return parseApiError(error);
  }

  // Handle network/fetch errors
  if (error instanceof Error) {
    return parseNativeError(error);
  }

  // Handle string errors
  if (typeof error === "string") {
    return {
      code: ErrorCode.UNKNOWN,
      message: error,
      shouldLogout: false,
      shouldRetry: true,
    };
  }

  // Unknown error type
  return {
    code: ErrorCode.UNKNOWN,
    message: ErrorMessages[ErrorCode.UNKNOWN],
    shouldLogout: false,
    shouldRetry: true,
  };
}

/**
 * Parse API client error
 */
function parseApiError(error: ClientApiError): ParsedError {
  const body = error.body;
  const status = error.response?.status;

  const domainError = parseDomainError(body);
  if (domainError) {
    const rawCode = domainError.error.code;
    const resolvedCode: ErrorCodeType = Object.prototype.hasOwnProperty.call(ErrorMetadata, rawCode)
      ? (rawCode as ErrorCodeType)
      : ErrorCode.UNKNOWN;

    const metadata = ErrorMetadata[resolvedCode] ?? ErrorMetadata[ErrorCode.UNKNOWN];
    const fieldErrors = extractFieldErrors(domainError.error.details);
    return {
      code: resolvedCode,
      message: domainError.error.message,
      shouldLogout: metadata.shouldLogout,
      shouldRetry: metadata.shouldRetry,
      ...(fieldErrors && { fieldErrors }),
    };
  }

  // Fall back to status code mapping
  const code = mapHttpStatusToErrorCode(status);
  const metadata = ErrorMetadata[code];
  return {
    code,
    message: error.message || ErrorMessages[code],
    shouldLogout: metadata.shouldLogout,
    shouldRetry: metadata.shouldRetry,
  };
}

/**
 * Extract fieldErrors from error details if present and valid.
 * Validates that the value is a Record<string, string> at runtime.
 */
function extractFieldErrors(details: unknown): Record<string, string> | undefined {
  if (!details || typeof details !== "object" || Array.isArray(details)) return undefined;

  const raw = (details as Record<string, unknown>)["fieldErrors"];
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;

  const result: Record<string, string> = {};
  for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
    if (typeof value === "string") {
      result[key] = value;
    }
  }

  return Object.keys(result).length > 0 ? result : undefined;
}

/**
 * Parse native JavaScript errors (network, timeout, etc.)
 */
function parseNativeError(error: Error): ParsedError {
  // Network errors
  if (error.name === "TypeError" && error.message.includes("fetch")) {
    return {
      code: ErrorCode.NETWORK_ERROR,
      message: ErrorMessages[ErrorCode.NETWORK_ERROR],
      shouldLogout: false,
      shouldRetry: true,
    };
  }

  // Timeout errors
  if (error.name === "AbortError") {
    return {
      code: ErrorCode.TIMEOUT,
      message: ErrorMessages[ErrorCode.TIMEOUT],
      shouldLogout: false,
      shouldRetry: true,
    };
  }

  return {
    code: ErrorCode.UNKNOWN,
    message: error.message || ErrorMessages[ErrorCode.UNKNOWN],
    shouldLogout: false,
    shouldRetry: true,
  };
}

// ============================================================================
// Convenience Functions
// ============================================================================

/**
 * Get user-friendly error message from any error
 */
export function getErrorMessage(error: unknown): string {
  return parseError(error).message;
}

/**
 * Check if error should trigger logout
 */
export function shouldLogout(error: unknown): boolean {
  return parseError(error).shouldLogout;
}

// ============================================================================
// Development Error Messages
// ============================================================================

/**
 * Return a detailed error message in development, or a user-friendly message in production.
 * Useful in catch blocks where you want visibility during development without
 * leaking internal details to end users.
 */
export function devErrorMessage(e: unknown, fallback: string, prodMessage: string): string {
  if (process.env.NODE_ENV === "development") {
    return e instanceof Error ? e.message : fallback;
  }
  return prodMessage;
}

// ============================================================================
// Re-exports from domain package for convenience
// ============================================================================

export {
  ErrorCode,
  ErrorMessages,
  ErrorMetadata,
  type ErrorCodeType,
} from "@customer-portal/domain/common";
