const path = require("path");
const joi = require("@hapi/joi");
const deref = require("json-schema-deref-sync");
const {sprintf} = require("sprintf-js");
import jsonSchemaValidator from "../../jsonSchema/jsonSchemaValidationService";
const MSG_SCHEMA_FAILED = "Failed json schema on %s, errors: %s";

export interface Schema {
    query?: any;
    body?: any;
    params?: any;
}

export default class RouteRequestJsonSchemaValidator {
    callback(req, res, next) {
        const {config: {route}} = req;

        if (!route.hasOwnProperty("requestJsonSchema")) {
            return next();
        }

        try {
            joi.assert(
                route,
                joi.object().keys({
                    requestJsonSchema: joi.object().keys({
                        query: joi.string(),
                        body: joi.string(),
                        params: joi.string(),
                    }).or("query", "body", "params"),
                }).unknown(true),
            );

            const {query, body, params} = this.buildRequestJsonSchema(
                route.requestJsonSchema,
            );

            if (params) {
                const paramsError = this.validateParams(params, req);

                if (paramsError) {
                    return next(sprintf(MSG_SCHEMA_FAILED, "params", paramsError).trim("\n"));
                }
            }

            if (query) {
                const queryError = this.validateQuery(query, req);

                if (queryError) {
                    return next(sprintf(MSG_SCHEMA_FAILED, "query", queryError).trim("\n"));
                }
            }

            if (body) {
                const bodyError = this.validateBody(body, req);

                if (bodyError) {
                    return next(sprintf(MSG_SCHEMA_FAILED, "body", bodyError).trim("\n"));
                }
            }
        } catch (error) {
            if (error.hasOwnProperty("details")) {
                console.debug(
                    `Skipping route "${route.id}" due to ${error.details[0].message}`);
                return next();
            }

            return next(error.message);
        }

        return next();
    }

    private validateParams(schema, req) {
        debugger;
        return jsonSchemaValidator(
            schema,
            req.params,
        );
    }

    private validateQuery(schema, req) {
        return jsonSchemaValidator(
            schema,
            req.query,
        );
    }

    private validateBody(schema, req) {
        return jsonSchemaValidator(
            schema,
            req.body,
        );
    }

    private buildRequestJsonSchema(requestJsonSchema) {
        const schema: Schema = {};

        if (requestJsonSchema.hasOwnProperty("params")) {
            const paramsSchema = path.join(
                process.cwd(),
                requestJsonSchema.params,
            );

            const paramsSchemaFromFile = require(paramsSchema);
            schema.params = deref(paramsSchemaFromFile);
        }

        if (requestJsonSchema.hasOwnProperty("query")) {
            const querySchema = path.join(
                process.cwd(),
                requestJsonSchema.query,
            );

            const querySchemaFromFile = require(querySchema);
            schema.query = deref(querySchemaFromFile);
        }

        if (requestJsonSchema.hasOwnProperty("body")) {
            const bodySchema = path.join(
                process.cwd(),
                requestJsonSchema.body,
            );

            const bodySchemaFromFile = require(bodySchema);

            schema.body = deref(bodySchemaFromFile);
        }

        return schema;
    }
}
