import { ErrorCode } from '#constants/errors';
import { GenericError } from './generic';
import { Errors } from './ISpecificErrors';
import { JWTPayload } from '#services/auth';

/**
 * Helper utilities for common error handling patterns
 * These utilities reduce boilerplate and centralize common error patterns
 */

/**
 * Wrap an async function with error handling
 * Converts non-GenericError errors to GenericError with specified code
 * @param fn - Async function to wrap
 * @param errorCode - Error code to use if an error occurs (default: INTERNAL_SERVER_ERROR)
 * @returns Promise with the function result or throws GenericError
 */
export async function withErrorHandling<T>(
	fn: () => Promise<T>,
	errorCode: ErrorCode = 'INTERNAL_SERVER_ERROR',
): Promise<T> {
	try {
		return await fn();
	} catch (error) {
		if (error instanceof GenericError) throw error;
		throw new GenericError(errorCode, { originalError: error });
	}
}

/**
 * Require a value to be found, throw NotFoundError if null/undefined
 * @param value - Value to check
 * @param resource - Resource name for error message (default: 'Resource')
 * @returns The value if not null/undefined
 * @throws NotFoundError if value is null or undefined
 */
export function requireFound<T>(value: T | null, resource: string = 'Resource'): T {
	if (!value) throw Errors.notFound(resource);
	return value;
}

/**
 * Require a user to have a specific permission
 * @param user - JWT payload with permissions
 * @param permission - Required permission
 * @throws AuthorizationError if permission is missing
 */
export function requirePermission(user: JWTPayload, permission: string): void {
	if (!user.permissions?.includes(permission)) {
		throw Errors.insufficientPermissions({ required: permission });
	}
}

/**
 * Require a value to be present (not null/undefined/empty)
 * @param value - Value to check
 * @param fieldName - Field name for error message
 * @throws ValidationError if value is null, undefined, or empty
 */
export function validateRequired(value: unknown, fieldName: string): void {
	if (value === null || value === undefined || value === '') {
		throw Errors.validation([
			{
				field: fieldName,
				code: 'VALIDATION_ERROR',
				message: { vi: `${fieldName} là bắt buộc`, en: `${fieldName} is required` },
			},
		]);
	}
}

/**
 * Validate that a string matches a minimum length
 * @param value - String value to validate
 * @param fieldName - Field name for error message
 * @param minLength - Minimum required length
 * @throws ValidationError if string is too short
 */
export function validateMinLength(value: string, fieldName: string, minLength: number): void {
	if (value.length < minLength) {
		throw Errors.validation([
			{
				field: fieldName,
				code: 'VALUE_TOO_SHORT',
				message: {
					vi: `${fieldName} phải có ít nhất ${minLength} ký tự`,
					en: `${fieldName} must have at least ${minLength} characters`,
				},
			},
		]);
	}
}

/**
 * Validate that a string does not exceed a maximum length
 * @param value - String value to validate
 * @param fieldName - Field name for error message
 * @param maxLength - Maximum allowed length
 * @throws ValidationError if string is too long
 */
export function validateMaxLength(value: string, fieldName: string, maxLength: number): void {
	if (value.length > maxLength) {
		throw Errors.validation([
			{
				field: fieldName,
				code: 'VALUE_TOO_LONG',
				message: {
					vi: `${fieldName} không được vượt quá ${maxLength} ký tự`,
					en: `${fieldName} must not exceed ${maxLength} characters`,
				},
			},
		]);
	}
}

/**
 * Validate email format
 * @param email - Email string to validate
 * @param fieldName - Field name (default: 'email')
 * @throws ValidationError if email format is invalid
 */
export function validateEmail(email: string, fieldName: string = 'email'): void {
	const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
	if (!emailRegex.test(email)) {
		throw Errors.invalidEmail(fieldName);
	}
}

/**
 * Validate UUID format
 * @param uuid - UUID string to validate
 * @param fieldName - Field name (default: 'id')
 * @throws ValidationError if UUID format is invalid
 */
export function validateUuid(uuid: string, fieldName: string = 'id'): void {
	const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
	if (!uuidRegex.test(uuid)) {
		throw Errors.invalidUuid(fieldName);
	}
}

/**
 * Check if user owns a resource
 * @param resourceOwnerId - Owner ID of the resource
 * @param userId - Current user ID
 * @throws ForbiddenError if user does not own the resource
 */
export function requireOwnership(resourceOwnerId: string, userId: string): void {
	if (resourceOwnerId !== userId) {
		throw Errors.forbidden({ resourceOwnerId, userId });
	}
}
