/**
 * Standardized Error Handler
 *
 * Provides consistent error handling patterns across the TimeSafari codebase
 * to improve debugging, user experience, and maintainability.
 *
 * @author Matthew Raymer
 * @since 2025-08-25
 */

import { AxiosError } from "axios";
import { logger } from "./logger";

/**
 * Standard error context for consistent logging
 */
export interface ErrorContext {
  component: string;
  operation: string;
  timestamp: string;
  [key: string]: unknown;
}

/**
 * Enhanced error information for better debugging
 */
export interface EnhancedErrorInfo {
  errorType: "AxiosError" | "NetworkError" | "ValidationError" | "UnknownError";
  status?: number;
  statusText?: string;
  errorData?: unknown;
  errorMessage: string;
  errorStack?: string;
  requestContext?: {
    url?: string;
    method?: string;
    headers?: Record<string, unknown>;
  };
}

/**
 * Standardized error handler for API operations
 *
 * @param error - The error that occurred
 * @param context - Context information about the operation
 * @param operation - Description of the operation being performed
 * @returns Enhanced error information for consistent handling
 */
export function handleApiError(
  error: unknown,
  context: ErrorContext,
  operation: string,
): EnhancedErrorInfo {
  const baseContext = {
    ...context,
    operation,
    timestamp: new Date().toISOString(),
  };

  if (error instanceof AxiosError) {
    const axiosError = error as AxiosError;
    const status = axiosError.response?.status;
    const statusText = axiosError.response?.statusText;
    const errorData = axiosError.response?.data;

    const enhancedError: EnhancedErrorInfo = {
      errorType: "AxiosError",
      status,
      statusText,
      errorData,
      errorMessage: axiosError.message,
      errorStack: axiosError.stack,
      requestContext: {
        url: axiosError.config?.url,
        method: axiosError.config?.method,
        headers: axiosError.config?.headers,
      },
    };

    // Log with consistent format
    logger.error(
      `[${context.component}] ❌ ${operation} failed (AxiosError):`,
      {
        ...baseContext,
        ...enhancedError,
      },
    );

    return enhancedError;
  }

  if (error instanceof Error) {
    const enhancedError: EnhancedErrorInfo = {
      errorType: "UnknownError",
      errorMessage: error.message,
      errorStack: error.stack,
    };

    logger.error(`[${context.component}] ❌ ${operation} failed (Error):`, {
      ...baseContext,
      ...enhancedError,
    });

    return enhancedError;
  }

  // Handle unknown error types
  const enhancedError: EnhancedErrorInfo = {
    errorType: "UnknownError",
    errorMessage: String(error),
  };

  logger.error(`[${context.component}] ❌ ${operation} failed (Unknown):`, {
    ...baseContext,
    ...enhancedError,
  });

  return enhancedError;
}

/**
 * Extract human-readable error message from various error response formats
 *
 * @param errorData - Error response data
 * @returns Human-readable error message
 */
export function extractErrorMessage(errorData: unknown): string {
  if (typeof errorData === "string") {
    return errorData;
  }

  if (typeof errorData === "object" && errorData !== null) {
    const obj = errorData as Record<string, unknown>;

    // Try common error message fields
    if (obj.message && typeof obj.message === "string") {
      return obj.message;
    }

    if (obj.error && typeof obj.error === "string") {
      return obj.error;
    }

    if (obj.detail && typeof obj.detail === "string") {
      return obj.detail;
    }

    if (obj.reason && typeof obj.reason === "string") {
      return obj.reason;
    }

    // Fallback to stringified object
    return JSON.stringify(errorData);
  }

  return String(errorData);
}

/**
 * Create user-friendly error message from enhanced error info
 *
 * @param errorInfo - Enhanced error information
 * @param fallbackMessage - Fallback message if error details are insufficient
 * @returns User-friendly error message
 */
export function createUserMessage(
  errorInfo: EnhancedErrorInfo,
  fallbackMessage: string,
): string {
  if (errorInfo.errorType === "AxiosError") {
    const status = errorInfo.status;
    const statusText = errorInfo.statusText;
    const errorMessage = extractErrorMessage(errorInfo.errorData);

    if (status && statusText) {
      if (errorMessage && errorMessage !== "{}") {
        return `${fallbackMessage}: ${status} ${statusText} - ${errorMessage}`;
      }
      return `${fallbackMessage}: ${status} ${statusText}`;
    }
  }

  if (
    errorInfo.errorMessage &&
    errorInfo.errorMessage !== "Request failed with status code 0"
  ) {
    return `${fallbackMessage}: ${errorInfo.errorMessage}`;
  }

  return fallbackMessage;
}

/**
 * Handle specific HTTP status codes with appropriate user messages
 *
 * @param status - HTTP status code
 * @param errorData - Error response data
 * @param operation - Description of the operation
 * @returns User-friendly error message
 */
export function handleHttpStatus(
  status: number,
  errorData: unknown,
  operation: string,
): string {
  const errorMessage = extractErrorMessage(errorData);

  switch (status) {
    case 400:
      return errorMessage || `${operation} failed: Bad request`;
    case 401:
      return `${operation} failed: Authentication required`;
    case 403:
      return `${operation} failed: Access denied`;
    case 404:
      return errorMessage || `${operation} failed: Resource not found`;
    case 409:
      return errorMessage || `${operation} failed: Conflict with existing data`;
    case 422:
      return errorMessage || `${operation} failed: Validation error`;
    case 429:
      return `${operation} failed: Too many requests. Please try again later.`;
    case 500:
      return `${operation} failed: Server error. Please try again later.`;
    case 502:
    case 503:
    case 504:
      return `${operation} failed: Service temporarily unavailable. Please try again later.`;
    default:
      return errorMessage || `${operation} failed: HTTP ${status}`;
  }
}

/**
 * Check if an error is a network-related error
 *
 * @param error - The error to check
 * @returns True if the error is network-related
 */
export function isNetworkError(error: unknown): boolean {
  if (error instanceof AxiosError) {
    return !error.response && !error.request;
  }

  if (error instanceof Error) {
    const message = error.message.toLowerCase();
    return (
      message.includes("network") ||
      message.includes("timeout") ||
      message.includes("connection") ||
      message.includes("fetch")
    );
  }

  return false;
}

/**
 * Check if an error is a timeout error
 *
 * @param error - The error to check
 * @returns True if the error is a timeout
 */
export function isTimeoutError(error: unknown): boolean {
  if (error instanceof AxiosError) {
    return (
      error.code === "ECONNABORTED" ||
      error.message.toLowerCase().includes("timeout")
    );
  }

  if (error instanceof Error) {
    return error.message.toLowerCase().includes("timeout");
  }

  return false;
}

/**
 * Create standardized error context for components
 *
 * @param component - Component name
 * @param operation - Operation being performed
 * @param additionalContext - Additional context information
 * @returns Standardized error context
 */
export function createErrorContext(
  component: string,
  operation: string,
  additionalContext: Record<string, unknown> = {},
): ErrorContext {
  return {
    component,
    operation,
    timestamp: new Date().toISOString(),
    ...additionalContext,
  };
}
