import Hapi from '@hapi/hapi'
import * as Boom from "@hapi/boom";
import { I18N } from './i18n';
import Joi, { StringSchema, NumberSchema, DateSchema, ObjectSchema, Schema, AnySchema } from 'joi';

export class ErrorHandler {
    static generateError = (
        request: Hapi.RequestQuery,
        type: number,
        message: string | null,
        err: any | null
    ): Boom.Boom<unknown> => {
        let error: Boom.Boom<unknown>;

        const getMessage = (key: string) => request.i18n.__(key);
        switch (type) {
            case 500:
                error = Boom.badImplementation(message || 'INTERNAL_SERVER_ERROR');
                break;
            case 400:
                error = Boom.badRequest(message || 'BAD_REQUEST');
                break;
            case 401:
                error = Boom.unauthorized(message || 'UNAUTHORIZED_REQUEST');
                break;
            case 403:
                error = Boom.forbidden(message || 'PERMISSION_DENIED');
                break;
            case 404:
                error = Boom.notFound(message || 'NOT_FOUND');
                break;
            default:
                error = Boom.badImplementation(message || 'UNKNOWN_ERROR_MESSAGE');
                break;
        }
        const key = message || error.output.payload.message;
        error.output.payload.error = getMessage(key);
        error.output.payload.message = getMessage(key);
        error.output.payload.errors = err;
        if (type === 500) {
            console.log(err); // optionally disable in prod
        }
        return error;
    }

    static routeError = (errors: Joi.ErrorReport[], message: string): Joi.ErrorReport[] => {
        return errors.map((err: Joi.ErrorReport) => {
            console.log("===> Joi Error Path:", JSON.stringify(err.path), "Code:", err.code);
            switch (err.code) {
                case 'any.required':
                case 'string.required':
                    err.message = message + '_REQUIRED';
                    break;
                case 'any.empty':
                case 'string.empty':
                    err.message = message + '_CANNOT_BE_EMPTY';
                    break;

                case 'string.email':
                    err.message = message + '_MUST_BE_VALID_EMAIL';
                    break;
                case 'string.base':
                case 'number.base':
                case 'boolean.base':
                case 'array.base':
                    err.message = 'DATA_TYPE_MISMATCH';
                    break;
                case 'string.max':
                    err.message = message + '_MAX_LENGTH_EXCEEDED';
                    break;
                case 'string.min':
                    err.message = message + '_MIN_LENGTH_MISS_MATCH';
                    break;
                case 'number.max':
                    err.message = message + '_MAX_VALUE_EXCEEDED';
                    break;
                case 'number.min':
                    err.message = message + '_MIN_VALUE_MISS_MATCH';
                    break;
                case 'any.only':
                case 'string.only':
                case 'only.only':
                    err.message = message + '_INVALID_VALUE';
                    break;
                case 'any.unknown':
                    err.message = message + '_FIELD_NOT_ALLOWED';
                    break;
                case 'string.pattern.base':
                case 'number.pattern.base':
                    err.message = message + '_INVALID_FORMAT';
                    break;
            }
            return err;
        });
    }
}

// export class AppError extends Error {
//     statusCode: number;
//     error?: string;
//     messageKey: string;
//     errors?: any;
//     constructor(statusCode: number, messageKey: string, details?: any, errorCode: string = '') {
//         super(messageKey);
//         this.statusCode = statusCode;
//         this.error = errorCode;
//         this.messageKey = messageKey;
//         this.errors = details;
//     }
//     toJSON() {
//         return {
//             statusCode: this.statusCode,
//             error: this.error,
//             message: I18N.t(this.messageKey),
//             errors: this.errors ?? null
//         };
//     }
// }

export class AppError extends Error {
    statusCode: number;
    error?: string;
    messageKey: string;
    errors?: any;
    attrbutes?: any;

    constructor(statusCode: number, messageKey: string, details?: any, errorCode: string = '') {
        super(messageKey);
        this.statusCode = statusCode;
        this.error = errorCode;
        this.messageKey = messageKey;
        this.errors = details;
    }
    toJSON() {
        return {
            statusCode: this.statusCode,
            error: this.error,
            message: I18N.t(this.messageKey),
            errors: this.sanitize(this.errors)
        };
    }

    private sanitize(input: any): any {
        if (!input) return null;

        if (typeof input === 'string') return input;

        // Handle plain Error instances
        if (input instanceof Error) {
            return {
                name: input.name,
                message: input.message,
                stack: process.env.NODE_ENV !== 'production' ? input.stack : undefined
            };
        }

        // Handle Sequelize-style validation errors
        if (Array.isArray(input?.errors) && input?.name?.includes('Sequelize')) {
            return input.errors.map((e: any) => ({
                message: e.message,
                path: e.path,
                type: e.type,
                value: e.value
            }));
        }

        // Fallback: remove circular references
        return this.removeCirculars(input);
    }

    private removeCirculars(obj: any): any {
        const seen = new WeakSet();
        return JSON.parse(JSON.stringify(obj, (_, value) => {
            if (typeof value === 'object' && value !== null) {
                if (seen.has(value)) return '[Circular]';
                seen.add(value);
            }
            return value;
        }));
    }
}

