"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.HotAPI = exports.EventExecutionType = void 0;
const HotServer_1 = require("./HotServer");
const HotRoute_1 = require("./HotRoute");
const HotRouteMethod_1 = require("./HotRouteMethod");
const Hot_1 = require("./Hot");
/**
 * The type of object to use during event executions.
 */
var EventExecutionType;
(function (EventExecutionType) {
    EventExecutionType[EventExecutionType["HotRoute"] = 0] = "HotRoute";
    EventExecutionType[EventExecutionType["HotMethod"] = 1] = "HotMethod";
    EventExecutionType[EventExecutionType["HotAPI"] = 2] = "HotAPI";
})(EventExecutionType || (exports.EventExecutionType = EventExecutionType = {}));
/**
 * The API to use.
 */
class HotAPI {
    constructor(baseUrl, connection, db = null) {
        if (connection == null)
            throw new Error(`No server attached to api with baseUrl: ${baseUrl}`);
        this.connection = connection;
        this.description = "";
        this.bearerToken = "";
        this.baseUrl = baseUrl;
        this.createFunctions = true;
        this.executeEventsUsing = EventExecutionType.HotRoute;
        this.db = db;
        this.openAPI = {};
        this.userAuth = null;
        this.routes = {};
        this.onPreRegister = null;
        this.onPostRegister = null;
    }
    /**
     * Set the database schema for use.
     */
    setDBSchema(schema) {
        if (this.connection.api == null)
            throw new Error(`No API has been set!`);
        if (this.connection.api.db == null)
            throw new Error(`No database has been set for API base url ${this.connection.api.baseUrl}`);
        this.connection.api.db.schema = schema;
    }
    /**
     * Get the database being used.
     */
    getDB() {
        if (this.connection.api.db == null)
            throw new Error(`No database has been set for API base url ${this.connection.api.baseUrl}`);
        return (this.connection.api.db);
    }
    /**
     * Get the database schema being used.
     */
    getDBSchema() {
        if (this.connection.api.db == null)
            throw new Error(`No database has been set for API base url ${this.connection.api.baseUrl}`);
        return (this.connection.api.db.schema);
    }
    /**
     * Add a route. If this.createFunctions is set to true (which is default), this will take the incoming
     * route and create an object in this HotAPI object using the name of the route. If there's
     * any HotRouteMethods inside of the incoming HotRoute, it will create the methods
     * and attach them to the newly created HotRoute object.
     *
     * @example
     * ```ts
     * import { HotAPI, HotRoute } from "hotstaq";
     *
     * class AppAPI extends HotAPI
     * {
     * 		constructor (baseUrl: string, connection: HotServer | HotClient = null, db: any = null)
     * 		{
     * 			super(baseUrl, connection, db);
     *
     * 			// Creates the route http://127.0.0.1:8080/v1/hello_world/
     * 			this.addRoute (new ExampleRoute (this));
     * 		}
     * }
     *
     * class ExampleRoute extends HotRoute
     * {
     * 		constructor (api: AppAPI)
     * 		{
     * 			super (api.connection, "hello_world");
     *
     * 			// Creates the HTTP GET endpoint: http://127.0.0.1:8080/v1/hello_world/hi
     * 			// When this endpoint is accessed, the JSON response will return "hello"
     * 			this.addMethod ("hi", async () =>
     * 			{
     * 				return ("hello");
     * 			});
     * 		}
     * }
     * ```
     *
     * In the client browser, this would be used like so:
     * ```
     * await Hot.API.hello_world.hi ();
     * ```
     *
     * @param route The route to add. Can be either a full HotRoute object, or just
     * the route's name. If a HotRoute object is supplied, the rest of the parameters
     * will be ignored.
     * @param routeMethod The route's method to add. If the route parameter is a string,
     * it will be interpreted as the route's name, and this will be the method added to
     * the new route.
     * @param executeFunction The function to execute when routeMethod is called by the API.
     */
    addRoute(route, routeMethod = null, executeFunction = null) {
        let routeName = "";
        if (typeof (route) === "string") {
            routeName = route;
            if (this.routes[routeName] == null)
                this.routes[routeName] = new HotRoute_1.HotRoute(this.connection, routeName);
            if (typeof (routeMethod) === "string") {
                this.routes[routeName].addMethod(HotRouteMethod_1.HotRouteMethod.create(this.routes[routeName], routeMethod, executeFunction));
            }
            else
                this.routes[routeName].addMethod(routeMethod);
        }
        else {
            routeName = route.route;
            this.routes[route.route] = route;
        }
        this.routes[routeName].connection = this.connection;
        // Create the route functions for the server/client. This must also be done after the route's onPreRegister has been called.
        if (this.createFunctions === true)
            this.createAPIRouteMethods(routeName);
    }
    /**
     * Create the API route methods for use in api.my_route.my_method ().
     */
    createAPIRouteMethods(routeName) {
        // @ts-ignore
        let newRoute = this[routeName];
        if (newRoute == null)
            newRoute = {};
        for (let iIdx = 0; iIdx < this.routes[routeName].methods.length; iIdx++) {
            let currentRoute = this.routes[routeName];
            let newRouteMethod = this.routes[routeName].methods[iIdx];
            /*
            /// @fixme Is this really necessary? A HTTP call is much more preferable,
            /// especially for accruate testing.
            if (this.connection instanceof HotServer)
            {
                if (newRouteMethod.onServerExecute != null)
                    newRoute[newRouteMethod.name] = newRouteMethod.onServerExecute;
            }
            else*/
            {
                /*
                /// @fixme Is onClientExecute necessary? I'm thinking the dev can just simply create
                /// their own function to call.
                if (newRouteMethod.onClientExecute != null)
                    newRoute[newRouteMethod.name] = newRouteMethod.onClientExecute;
                else
                {*/
                newRoute[newRouteMethod.name] = (data, files) => {
                    let httpMethod = newRouteMethod.type;
                    // Construct the url here. Base + route + route method
                    let routeStr = newRouteMethod.getRouteUrl();
                    let args = [routeStr, data, httpMethod, files, this.bearerToken];
                    return (this.makeCall.apply(this, args));
                };
                //}
            }
        }
        // @ts-ignore
        this[routeName] = newRoute;
    }
    /**
     * Register a route with the server.
     */
    registerRoute(route) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.connection instanceof HotServer_1.HotServer)
                yield this.connection.registerRoute(route);
        });
    }
    /**
     * Register all routes with the server.
     */
    registerRoutes() {
        return __awaiter(this, void 0, void 0, function* () {
            for (let key in this.routes) {
                let route = this.routes[key];
                yield this.registerRoute(route);
            }
        });
    }
    /**
     * Make a call to the API.
     */
    makeCall(route_1, data_1) {
        return __awaiter(this, arguments, void 0, function* (route, data, httpMethod = HotRouteMethod_1.HotEventMethod.POST, files = {}, bearerToken = "") {
            let url = this.baseUrl;
            const httpMethodStr = httpMethod.toUpperCase();
            if (url[(url.length - 1)] === "/")
                url = url.substr(0, (url.length - 1));
            if (route[0] !== "/")
                url += "/";
            url += route;
            return (Hot_1.Hot.httpRequest(url, data, httpMethod, files, bearerToken));
        });
    }
}
exports.HotAPI = HotAPI;
//# sourceMappingURL=HotAPI.js.map