import { asApiError, isApiError, type ApiError, type ApiErrorDetail } from '@/types/api';

type TranslateFn = (key: string) => string;

const STATUS_FALLBACK_KEYS: Record<number, string> = {
  401: 'AUTH_UNAUTHORIZED',
  403: 'PERMISSION_DENIED',
  404: 'NOT_FOUND',
  409: 'CONFLICT',
  500: 'INTERNAL_ERROR',
};

function translateKey(t: TranslateFn, key: string): string | null {
  const translated = t(key);
  return translated !== key ? translated : null;
}

function formatValidationDetails(details: ApiErrorDetail[], t: TranslateFn): string | null {
  const messages = details
    .map((detail) => translateKey(t, detail.code))
    .filter((message): message is string => Boolean(message));

  if (messages.length === 0) {
    return null;
  }

  return messages.join(' ');
}

function resolveFromStatus(statusCode: number, t: TranslateFn): string | null {
  const key = STATUS_FALLBACK_KEYS[statusCode] ?? (statusCode >= 500 ? 'INTERNAL_ERROR' : null);
  return key ? translateKey(t, key) : null;
}

function normalizeLegacyError(value: unknown): ApiError | null {
  if (isApiError(value)) {
    return value;
  }

  if (typeof value === 'object' && value !== null && 'statusCode' in value) {
    const statusCode =
      typeof (value as { statusCode?: unknown }).statusCode === 'number'
        ? (value as { statusCode: number }).statusCode
        : 500;

    return {
      statusCode,
      code:
        statusCode === 401
          ? 'AUTH_UNAUTHORIZED'
          : statusCode === 403
            ? 'PERMISSION_DENIED'
            : statusCode >= 500
              ? 'INTERNAL_ERROR'
              : 'BAD_REQUEST',
    };
  }

  if (value instanceof Error) {
    if (value.message === 'Network Error') {
      return { statusCode: 0, code: 'NETWORK_ERROR' };
    }
    if (value.message.includes('timeout')) {
      return { statusCode: 0, code: 'TIMEOUT' };
    }
  }

  return null;
}

/** Maps API error codes (and status fallbacks) to translated user-facing text. */
export function getUserFacingError(
  err: unknown,
  t: TranslateFn,
  featureFallback?: string,
): string {
  const apiError = asApiError(err) ?? normalizeLegacyError(err);

  if (apiError?.code === 'VALIDATION_FAILED' && apiError.details?.length) {
    const validationMessage = formatValidationDetails(apiError.details, t);
    if (validationMessage) {
      return validationMessage;
    }
  }

  if (apiError?.code) {
    const codeMessage = translateKey(t, apiError.code);
    if (codeMessage) {
      return codeMessage;
    }
  }

  if (apiError?.statusCode) {
    const statusMessage = resolveFromStatus(apiError.statusCode, t);
    if (statusMessage) {
      return statusMessage;
    }
  }

  if (featureFallback?.trim()) {
    return featureFallback;
  }

  return translateKey(t, 'GENERIC') ?? 'Something went wrong';
}

/** @deprecated Prefer getUserFacingError(err, tErrors, fallback). */
export function formatApiErrorMessage(
  err: unknown,
  fallback: string,
  t?: TranslateFn,
): string {
  if (t) {
    return getUserFacingError(err, t, fallback);
  }

  const apiError = asApiError(err);
  if (apiError?.code) {
    return apiError.code;
  }

  return fallback;
}
