/**
 * Centralized Error Catalog
 *
 * Defines all error codes, HTTP status codes, and i18n messages in one place.
 * This provides a single source of truth for error handling across the application.
 */

import { Language } from '../i18n';
import { ErrorCategory, ErrorSeverity, ErrorEntry } from './types';
import { AUTH_ERRORS } from './auth';
import { VALIDATION_ERRORS } from './validation';
import { NOT_FOUND_ERRORS } from './notFound';
import { CONFLICT_ERRORS } from './conflict';
import { DATABASE_ERRORS } from './database';
import { EXTERNAL_ERRORS } from './external';
import { SYSTEM_ERRORS } from './system';

// Re-export ErrorCategory for use in error interfaces
export { ErrorCategory, ErrorSeverity };

// Error catalog - all error definitions
export const ERROR_CATALOG: Record<string, ErrorEntry> = {
	...AUTH_ERRORS,
	...VALIDATION_ERRORS,
	...NOT_FOUND_ERRORS,
	...CONFLICT_ERRORS,
	...DATABASE_ERRORS,
	...EXTERNAL_ERRORS,
	...SYSTEM_ERRORS,
};

// Type-safe error codes (extracted from ERROR_CATALOG keys)
export type ErrorCode = keyof typeof ERROR_CATALOG;

// Helper functions
export function getErrorEntry(code: string): ErrorEntry | undefined {
	return ERROR_CATALOG[code];
}

export function getHttpStatusFromCode(code: string): number {
	const entry = ERROR_CATALOG[code];
	return entry?.httpStatus ?? 500;
}

export function getErrorMessage(code: string, language: Language = Language.vi): string {
	const entry = ERROR_CATALOG[code];
	if (!entry) return language === Language.vi ? 'Lỗi không xác định' : 'Unknown error';
	return entry.message[language];
}

export function getErrorCategory(code: string): ErrorCategory {
	const entry = ERROR_CATALOG[code];
	return entry?.category ?? ErrorCategory.SYSTEM;
}

export function getErrorSeverity(code: string): ErrorSeverity {
	const entry = ERROR_CATALOG[code];
	return entry?.severity ?? 'medium';
}

/**
 * Convert PascalCase to SNAKE_CASE
 * @param str - PascalCase string
 * @returns SNAKE_CASE string
 */
function toSnakeCase(str: string): string {
	return str
		.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
		.replace(/ERROR$/i, '')
		.toUpperCase();
}

/**
 * Normalize legacy error type/class name to error catalog code
 * This is for backward compatibility with legacy error types
 * @param errorType - Error type or class name (e.g., 'InvalidCredentialsError')
 * @returns Normalized error code string (e.g., 'INVALID_CREDENTIALS')
 */
export function normalizeLegacyErrorType(errorType: string): string {
	// Map legacy class names to catalog codes
	const typeMap: Record<string, string> = {
		InvalidCredentialsError: 'INVALID_CREDENTIALS',
		AccountLockedError: 'ACCOUNT_LOCKED',
		AccountInactiveError: 'ACCOUNT_INACTIVE',
		UserExistsError: 'USER_EXISTS',
		ValidationError: 'VALIDATION_ERROR',
		InternalServerError: 'INTERNAL_SERVER_ERROR',
		OAUTH_ONLY_ACCOUNT: 'OAUTH_ONLY_ACCOUNT',
	};

	return typeMap[errorType] || toSnakeCase(errorType);
}

/**
 * Get error messages in both languages from error code
 * @param code - Error code string
 * @returns Object with vi and en messages
 */
export function getErrorMessages(code: string): { vi: string; en: string } {
	const entry = ERROR_CATALOG[code];
	if (!entry) return { vi: 'Lỗi không xác định', en: 'Unknown error' };
	return entry.message;
}

/**
 * Create a violation object from error code
 * @param code - Error code string
 * @param typeOrAdditionalData - Error type (e.g., class name) or additional data
 * @param additionalData - Optional additional data
 * @returns Violation object
 */
export function createViolation(
	code: string,
	typeOrAdditionalData?: string | unknown,
	additionalData?: unknown,
): { messages: { vi: string; en: string }; type: string; code: string; additionalData?: unknown } {
	// Detect if second param is type or additionalData
	let type: string;
	let data: unknown;

	if (typeof typeOrAdditionalData === 'string') {
		type = typeOrAdditionalData;
		data = additionalData;
	} else {
		type = code; // Default type to code
		data = typeOrAdditionalData;
	}

	return {
		messages: getErrorMessages(code),
		type,
		code,
		additionalData: data,
	};
}
