import http from "node:http";
import https from "node:https";
import querystring from "querystring";
import serializeQuery from "../../utils/serialize-query";
import _ from "lodash";
import { HttpFunctionResponse, HttpRequestParams } from "../../types";

/**
 * # Generate a http Request
 */
export default function httpRequest<
    ReqObj extends { [k: string]: any } = { [k: string]: any },
    ResObj extends { [k: string]: any } = { [k: string]: any }
>(params: HttpRequestParams<ReqObj>): Promise<HttpFunctionResponse<ResObj>> {
    return new Promise((resolve, reject) => {
        const isUrlEncodedFormBody = params.urlEncodedFormBody;

        const reqPayloadString = params.body
            ? isUrlEncodedFormBody
                ? querystring.stringify(params.body)
                : JSON.stringify(params.body).replace(/\n|\r|\n\r/gm, "")
            : undefined;

        const reqQueryString = params.query
            ? serializeQuery(params.query)
            : undefined;

        const paramScheme = params.scheme;
        const finalScheme = paramScheme == "http" ? http : https;

        const finalPath = params.path
            ? params.path + (reqQueryString ? reqQueryString : "")
            : undefined;

        delete params.body;
        delete params.scheme;
        delete params.query;
        delete params.urlEncodedFormBody;

        let finalHeaders: http.OutgoingHttpHeaders = {
            "Content-Type": isUrlEncodedFormBody
                ? "application/x-www-form-urlencoded"
                : "application/json",
        };

        if (reqPayloadString) {
            finalHeaders["Content-Length"] =
                Buffer.from(reqPayloadString).length;
        }

        finalHeaders = { ...finalHeaders, ...params.headers };

        /** @type {import("node:https").RequestOptions} */
        const requestOptions: import("node:https").RequestOptions = {
            ...params,
            headers: finalHeaders,
            port: paramScheme == "https" ? 443 : params.port,
            path: finalPath,
        };

        const httpsRequest = finalScheme.request(
            requestOptions,
            /**
             * Callback Function
             *
             * @description https request callback
             */
            (response) => {
                var str = "";

                response.on("data", function (chunk) {
                    str += chunk;
                });

                response.on("end", function () {
                    const data = (() => {
                        try {
                            const jsonObj: { [k: string]: any } =
                                JSON.parse(str);
                            return jsonObj;
                        } catch (error) {
                            return undefined;
                        }
                    })() as any;

                    resolve({
                        status: response.statusCode || 404,
                        data,
                        str,
                        requestedPath: finalPath,
                    });
                });

                response.on("error", (err) => {
                    resolve({
                        status: response.statusCode || 404,
                        str,
                        error: err.message,
                        requestedPath: finalPath,
                    });
                });
            }
        );

        if (reqPayloadString) {
            httpsRequest.write(reqPayloadString);
        }

        httpsRequest.on("error", (error) => {
            console.log("HTTPS request ERROR =>", error);
        });

        httpsRequest.end();
    });
}
