import { GhostError, GhostErrorOptions } from './GhostError';

const mergeOptions = (options: GhostErrorOptions, defaults: GhostErrorOptions) => {
    const result = { ...defaults };

    // Ignore undefined options - for example passing statusCode: undefined should not override the default
    (Object.keys(options) as (keyof GhostErrorOptions)[]).forEach((key) => {
        if (options[key] !== undefined) {
            result[key] = options[key];
        }
    });

    return result;
};

export class InternalServerError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 500,
                level: 'critical',
                errorType: 'InternalServerError',
                message: 'The server has encountered an error.',
            }),
        );
    }
}

export class IncorrectUsageError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 400,
                level: 'critical',
                errorType: 'IncorrectUsageError',
                message: 'We detected a misuse. Please read the stack trace.',
            }),
        );
    }
}

export class NotFoundError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 404,
                errorType: 'NotFoundError',
                message: 'Resource could not be found.',
                hideStack: true,
            }),
        );
    }
}

export class BadRequestError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 400,
                errorType: 'BadRequestError',
                message: 'The request could not be understood.',
            }),
        );
    }
}

export class UnauthorizedError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 401,
                errorType: 'UnauthorizedError',
                message: 'You are not authorised to make this request.',
            }),
        );
    }
}

export class NoPermissionError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 403,
                errorType: 'NoPermissionError',
                message: 'You do not have permission to perform this request.',
            }),
        );
    }
}

export class ValidationError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 422,
                errorType: 'ValidationError',
                message: 'The request failed validation.',
            }),
        );
    }
}

export class UnsupportedMediaTypeError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 415,
                errorType: 'UnsupportedMediaTypeError',
                message: 'The media in the request is not supported by the server.',
            }),
        );
    }
}

export class TooManyRequestsError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 429,
                errorType: 'TooManyRequestsError',
                message: 'Server has received too many similar requests in a short space of time.',
            }),
        );
    }
}

export class MaintenanceError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 503,
                errorType: 'MaintenanceError',
                message: 'The server is temporarily down for maintenance.',
            }),
        );
    }
}

export class MethodNotAllowedError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 405,
                errorType: 'MethodNotAllowedError',
                message: 'Method not allowed for resource.',
            }),
        );
    }
}

export class RequestNotAcceptableError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 406,
                errorType: 'RequestNotAcceptableError',
                message: 'Request not acceptable for provided Accept-Version header.',
                hideStack: true,
            }),
        );
    }
}

export class RangeNotSatisfiableError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 416,
                errorType: 'RangeNotSatisfiableError',
                message: 'Range not satisfiable for provided Range header.',
                hideStack: true,
            }),
        );
    }
}

export class RequestEntityTooLargeError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 413,
                errorType: 'RequestEntityTooLargeError',
                message: 'Request was too big for the server to handle.',
            }),
        );
    }
}

export class TokenRevocationError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 503,
                errorType: 'TokenRevocationError',
                message: 'Token is no longer available.',
            }),
        );
    }
}

export class VersionMismatchError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 400,
                errorType: 'VersionMismatchError',
                message: 'Requested version does not match server version.',
            }),
        );
    }
}

export class DataExportError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 500,
                errorType: 'DataExportError',
                message: 'The server encountered an error whilst exporting data.',
            }),
        );
    }
}

export class DataImportError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 500,
                errorType: 'DataImportError',
                message: 'The server encountered an error whilst importing data.',
            }),
        );
    }
}

export class EmailError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 500,
                errorType: 'EmailError',
                message: 'The server encountered an error whilst sending email.',
            }),
        );
    }
}

export class ThemeValidationError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 422,
                errorType: 'ThemeValidationError',
                message: 'The theme has a validation error.',
                errorDetails: {},
            }),
        );
    }
}

export class DisabledFeatureError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 409,
                errorType: 'DisabledFeatureError',
                message: 'Unable to complete the request, this feature is disabled.',
            }),
        );
    }
}

export class UpdateCollisionError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                statusCode: 409,
                errorType: 'UpdateCollisionError',
                message: 'Unable to complete the request, there was a conflict.',
            }),
        );
    }
}

export class HostLimitError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'HostLimitError',
                hideStack: true,
                statusCode: 403,
                message: 'Unable to complete the request, this resource is limited.',
            }),
        );
    }
}

export class HelperWarning extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'HelperWarning',
                hideStack: true,
                statusCode: 400,
                message: 'A theme helper has done something unexpected.',
            }),
        );
    }
}

export class PasswordResetRequiredError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'PasswordResetRequiredError',
                statusCode: 401,
                message:
                    'For security, you need to create a new password. An email has been sent to you with instructions!',
            }),
        );
    }
}

export class UnhandledJobError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'UnhandledJobError',
                message: 'Processed job threw an unhandled error',
                level: 'critical',
            }),
        );
    }
}

export class NoContentError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'NoContentError',
                statusCode: 204,
                hideStack: true,
            }),
        );
    }
}

export class ConflictError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'ConflictError',
                statusCode: 409,
                message: 'The server has encountered an conflict.',
            }),
        );
    }
}

export class MigrationError extends GhostError {
    constructor(options: GhostErrorOptions = {}) {
        super(
            mergeOptions(options, {
                errorType: 'MigrationError',
                message: 'An error has occurred applying a database migration.',
                level: 'critical',
            }),
        );
    }
}
