import { IViolations } from '../violations/IViolations';
import { IErrorHandler } from '../index';
import { BaseError, ForeignKeyConstraintError, UniqueConstraintError, ValidationError } from 'sequelize';
import { createViolation, getErrorMessages } from '#constants/errors';

export interface DatabaseErrorAdditionalData {
	formFields?: unknown[];
	errorFields?: string[];
}

/** Sequelize Error Handler */
export class DatabaseErrorHandler implements IErrorHandler {
	canHandle(error: unknown): boolean {
		return error instanceof ValidationError || error instanceof ForeignKeyConstraintError || error instanceof BaseError;
	}

	handle(error: unknown, additionalData?: DatabaseErrorAdditionalData): IViolations {
		if (error instanceof UniqueConstraintError) {
			return [
				createViolation('UNIQUE_CONSTRAINT', 'SequelizeUniqueConstraintError', {
					errors: error.errors.map((err) => ({
						field: err.path,
						code: 'UNIQUE_CONSTRAINT',
						message: getErrorMessages('UNIQUE_CONSTRAINT'),
					})),
					...additionalData,
				}),
			];
		}

		if (error instanceof ValidationError) {
			return [
				createViolation('VALIDATION_ERROR', 'SequelizeValidationError', {
					errors: error.errors.map((e) => ({
						field: e.path,
						code: 'VALIDATION_ERROR',
						message: { en: e.message, vi: e.message },
					})),
					...additionalData,
				}),
			];
		}

		if (error instanceof ForeignKeyConstraintError) {
			const fields = error?.fields ? Object.keys(error?.fields) : [];
			return [
				createViolation('FOREIGN_KEY_CONSTRAINT', 'SequelizeForeignKeyConstraintError', {
					errors: fields.map((key) => ({
						field: key,
						code: 'FOREIGN_KEY_CONSTRAINT',
						message: getErrorMessages('FOREIGN_KEY_CONSTRAINT'),
					})),
					...additionalData,
				}),
			];
		}

		if (error instanceof BaseError) {
			return [
				createViolation('DATABASE_ERROR', 'SequelizeBaseError', {
					errors: [
						{
							field: 'internal',
							code: 'DATABASE_ERROR',
							message: { en: error.message, vi: error.message },
						},
					],
					message: error.message,
					stack: error.stack,
					...additionalData,
				}),
			];
		}

		throw new Error('Unhandled database error type');
	}
}
