"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HotRouteMethod = exports.HotValidationType = exports.ServerRequest = exports.PassType = exports.HotEventMethod = void 0;
const Hot_1 = require("./Hot");
const HotRoute_1 = require("./HotRoute");
const HotServer_1 = require("./HotServer");
const HotProcessInput_1 = require("./HotProcessInput");
/**
 * The available event methods.
 */
var HotEventMethod;
(function (HotEventMethod) {
    /**
     * A HTTP GET request.
     */
    HotEventMethod["GET"] = "get";
    /**
     * A HTTP POST request.
     */
    HotEventMethod["POST"] = "post";
    /**
     * This will upload a file, then post the json request afterwards.
     */
    HotEventMethod["FILE_UPLOAD"] = "file_upload_then_post_json";
    /**
     * A SSE sub event. This will close the event whenever
     */
    HotEventMethod["SSE_SUB_EVENT"] = "sse_sub_event";
    /**
     * A websocket event.
     */
    HotEventMethod["WEBSOCKET_CLIENT_PUB_EVENT"] = "websocket_client_pub_event";
    /**
     * Handles both a HTTP POST websocket event requests.
     */
    HotEventMethod["POST_AND_WEBSOCKET_CLIENT_PUB_EVENT"] = "post_and_websocket_client_pub_event";
})(HotEventMethod || (exports.HotEventMethod = HotEventMethod = {}));
/**
 * How data should be passed through the server execute process.
 */
var PassType;
(function (PassType) {
    /**
     * Any data set in jsonObj will immediately be passed to the client when
     * this function returns.
     */
    PassType[PassType["ReturnToClient"] = 0] = "ReturnToClient";
    /**
     * Any data being set in jsonObj will be passed to the next step after
     * this function returns.
     */
    PassType[PassType["Update"] = 1] = "Update";
    /**
     * Ignore any data set in jsonObj.
     */
    PassType[PassType["Ignore"] = 2] = "Ignore";
})(PassType || (exports.PassType = PassType = {}));
/**
 * The request that came from a client.
 */
class ServerRequest {
    constructor(obj = null) {
        if (obj == null)
            obj = {};
        this.req = obj.req || null;
        this.res = obj.res || null;
        this.bearerToken = obj.bearerToken || "";
        this.wsSocket = obj.wsSocket || null;
        this.authorizedValue = obj.authorizedValue || null;
        this.jsonObj = obj.jsonObj || null;
        this.queryObj = obj.queryObj || null;
        this.files = obj.files || null;
        this.onClose = obj.onClose || null;
    }
    /**
     * Write to the HTTP response. This is mostly intended to be used
     * when using SSE. This requires JSON to be sent.
     */
    httpWrite(event, jsonObj) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.res == null)
                throw new Error(`Cannot close HTTP response because it is null!`);
            return (new Promise((resolve, reject) => {
                if (this.res.writable === true) {
                    let content = jsonObj;
                    if (typeof (jsonObj) !== "string")
                        content = JSON.stringify(jsonObj);
                    this.res.write(`event: ${event}\ndata: ${content}\n\n`, (err) => {
                        if (err != null)
                            throw err;
                        resolve();
                    });
                }
                else
                    resolve();
            }));
        });
    }
    /**
     * Write to the HTTP response. This is mostly intended to be used
     * when using SSE.
     */
    httpWriteRaw(data) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.res == null)
                throw new Error(`Cannot close HTTP response because it is null!`);
            return (new Promise((resolve, reject) => {
                if (this.res.writable === true) {
                    this.res.write(data, (err) => {
                        if (err != null)
                            throw err;
                        resolve();
                    });
                }
                else
                    resolve();
            }));
        });
    }
    /**
     * Close the HTTP response. This is mostly intended to be used when
     * using SSE.
     */
    httpClose() {
        if (this.res == null)
            throw new Error(`Cannot close HTTP response because it is null!`);
        this.res.end();
    }
}
exports.ServerRequest = ServerRequest;
/**
 * Backwards-compat alias for the validator-name string. Re-introduced
 * after 725eab8 removed it — `@hotstaq/userroute` and `@hotstaq/dataroute`
 * still ship .d.ts files that import this name, so removing it breaks
 * the TS build of every downstream package even when only the type
 * surface changed and the runtime kept working.
 *
 * The enum values mirror the keys registered in HotStaq.valids[…]; any
 * additional validator type can also be passed as a plain string.
 */
var HotValidationType;
(function (HotValidationType) {
    HotValidationType["UUID"] = "UUID";
    HotValidationType["Boolean"] = "boolean";
    HotValidationType["Number"] = "number";
    HotValidationType["Integer"] = "Integer";
    HotValidationType["Float"] = "Float";
    HotValidationType["Text"] = "Text";
    HotValidationType["Email"] = "Email";
    HotValidationType["Phone"] = "Phone";
    HotValidationType["URL"] = "URL";
    HotValidationType["IPv4"] = "IPv4";
    HotValidationType["IP"] = "IP";
    HotValidationType["IPv6"] = "IPv6";
    HotValidationType["Date"] = "Date";
    HotValidationType["Object"] = "Object";
    HotValidationType["Array"] = "Array";
    HotValidationType["Map"] = "Map";
    HotValidationType["Enum"] = "Enum";
    HotValidationType["JSON"] = "JSON";
    HotValidationType["JS"] = "JS";
    HotValidationType["Undefined"] = "undefined";
    HotValidationType["Null"] = "null";
})(HotValidationType || (exports.HotValidationType = HotValidationType = {}));
/**
 * An API method to make.
 */
class HotRouteMethod {
    constructor() {
        this.route = null;
        this.name = "";
        this.description = "";
        this.openAPI = null;
        this.tags = [];
        this.returns = null;
        this.validateQueryInput = new HotProcessInput_1.ValidationOptions();
        this.validateJSONInput = new HotProcessInput_1.ValidationOptions();
        this.parametersRefName = "";
        this.parameters = {};
        this.queryParameters = {};
        this.type = HotEventMethod.POST;
        this.isRegistered = false;
        this.executeSetup = true;
        this.testCases = {};
        this.onPreRegister = null;
        this.onRegister = null;
        this.onPostRegister = null;
        this.onServerAuthorize = null;
        this.onServerExecute = null;
        this.onClientExecute = null;
        this.onValidateQueryInput = null;
        this.onValidateJSONInput = null;
    }
    /**
     * Create a new route method.
     */
    static create(route, name = "", onExecute = null, type = HotEventMethod.POST, onServerAuthorize = null, onRegister = null, testCases = null) {
        let newMethod = new HotRouteMethod();
        let newRoute = null;
        if (route instanceof HotRoute_1.HotRoute)
            newRoute = route;
        else {
            newRoute = route.route;
            if (route.type != null)
                type = route.type;
            if (route.name != null)
                name = route.name;
            if (route.name != null)
                newMethod.tags = route.tags;
            if (route.description != null)
                newMethod.description = route.description;
            if (route.openAPI != null)
                newMethod.openAPI = route.openAPI;
            if (route.validateQueryInput != null)
                newMethod.validateQueryInput = route.validateQueryInput;
            if (route.validateJSONInput != null)
                newMethod.validateJSONInput = route.validateJSONInput;
            if (route.parametersRefName != null)
                newMethod.parametersRefName = route.parametersRefName;
            if (route.queryParameters != null)
                newMethod.queryParameters = route.queryParameters;
            if (route.returns != null) {
                if (typeof (route.returns) === "string") {
                    newMethod.returns = {
                        "type": "string",
                        "required": true,
                        "description": route.returns
                    };
                }
                else if (typeof (route.returns) === "function") {
                    /// @fixme Can't run await here for many reasons. await is required 
                    /// to execute HotStaq.convertInterfaceToRouteParameters. Fix later...
                    newMethod.returns = route.returns;
                }
                else
                    newMethod.returns = route.returns;
            }
            if (route.parameters != null) {
                if (route.parameters instanceof Function) {
                    // @ts-ignore
                    newMethod.parameters = route.parameters;
                }
                else {
                    for (let key in route.parameters) {
                        let param = route.parameters[key];
                        if (typeof (param) === "string") {
                            newMethod.parameters[key] = {
                                "type": "string",
                                "required": false,
                                "description": param
                            };
                        }
                        else if (typeof (param) === "function") {
                            /// @fixme Can't run await here for many reasons. await is required 
                            /// to execute HotStaq.convertInterfaceToRouteParameters. Fix later...
                            newMethod.parameters[key] = param;
                        }
                        else {
                            if (param.type == null)
                                param.type = "string";
                            newMethod.parameters[key] = param;
                        }
                    }
                }
            }
            if (route.onServerExecute != null)
                onExecute = route.onServerExecute;
            if (route.onServerAuthorize != null)
                onServerAuthorize = route.onServerAuthorize;
            if (route.onRegister != null)
                onRegister = route.onRegister;
            if (route.onPostRegister != null)
                newMethod.onPostRegister = route.onPostRegister;
            if (route.onServerExecute != null)
                newMethod.onServerExecute = route.onServerExecute;
            if (route.onClientExecute != null)
                newMethod.onClientExecute = route.onClientExecute;
            if (route.onValidateQueryInput != null)
                newMethod.onValidateQueryInput = route.onValidateQueryInput;
            if (route.onValidateJSONInput != null)
                newMethod.onValidateJSONInput = route.onValidateJSONInput;
            if (route.testCases != null)
                testCases = route.testCases;
        }
        if (name === "")
            throw new Error(`All route methods must have a name!`);
        newMethod.route = newRoute;
        newMethod.name = name;
        newMethod.type = type;
        newMethod.isRegistered = false;
        newMethod.executeSetup = false;
        newMethod.onServerAuthorize = onServerAuthorize;
        newMethod.onRegister = onRegister;
        newMethod.testCases = {};
        if (newMethod.route.connection.processor.mode === Hot_1.DeveloperMode.Development) {
            if (testCases != null) {
                if (testCases instanceof Array) {
                    for (let iIdx = 0; iIdx < testCases.length; iIdx++) {
                        let obj = testCases[iIdx];
                        if (typeof (obj) === "string") {
                            const testCaseName = obj;
                            const func = testCases[iIdx + 1];
                            newMethod.addTestCase(testCaseName, func);
                            iIdx++;
                        }
                        else
                            newMethod.addTestCase(obj);
                    }
                }
                else {
                    for (let key in testCases) {
                        let obj = testCases[key];
                        newMethod.addTestCase(obj);
                    }
                }
            }
        }
        if (newMethod.route.connection instanceof HotServer_1.HotServer)
            newMethod.onServerExecute = onExecute;
        //else
        //newMethod.onClientExecute = onExecute;
        return (newMethod);
    }
    /**
     * Add a new test case.
     */
    addTestCase(newTestCase, testCaseFunction = null) {
        if (typeof (newTestCase) === "string") {
            const name = newTestCase;
            const func = testCaseFunction;
            this.testCases[name] = {
                name: name,
                func: func
            };
            return;
        }
        if (typeof (newTestCase) === "function") {
            const testCaseId = Object.keys(this.testCases).length;
            const name = `${this.route.route}/${this.name} test case ${testCaseId}`;
            const func = newTestCase;
            this.testCases[name] = {
                name: name,
                func: func
            };
            return;
        }
        const testCase = newTestCase;
        this.testCases[testCase.name] = testCase;
    }
    /**
     * Get the relative url from this method and its route.
     */
    getRouteUrl() {
        if (this.route == null)
            throw new Error(`Route method ${this.name} does not have a parent route!`);
        let methodName = "/";
        if (this.route.version !== "")
            methodName += `${this.route.version}/`;
        if (this.route.prefix !== "")
            methodName += `${this.route.prefix}/`;
        if (this.route.route !== "")
            methodName += `${this.route.route}/`;
        methodName += this.name;
        return (methodName);
    }
}
exports.HotRouteMethod = HotRouteMethod;
//# sourceMappingURL=HotRouteMethod.js.map