import http from "http";
import encrypt from "../../../functions/dsql/encrypt";
import grabHostNames from "../../../utils/grab-host-names";
import apiGoogleLogin from "../../../functions/api/users/social/api-google-login";
import getAuthCookieNames from "../../../functions/backend/cookies/get-auth-cookie-names";
import { writeAuthFile } from "../../../functions/backend/auth/write-auth-files";
import { APILoginFunctionReturn } from "../../../types";

type Param = {
    key?: string;
    token: string;
    database?: string;
    response?: http.ServerResponse;
    encryptionKey?: string;
    encryptionSalt?: string;
    additionalFields?: string[];
    additionalData?: { [s: string]: string | number };
    apiUserID?: string | number;
    debug?: boolean;
};

/**
 * # SERVER FUNCTION: Login with google Function
 */
export default async function googleAuth({
    key,
    token,
    database,
    response,
    encryptionKey,
    encryptionSalt,
    additionalFields,
    additionalData,
    apiUserID,
    debug,
}: Param): Promise<APILoginFunctionReturn> {
    const grabedHostNames = grabHostNames({
        userId: apiUserID || process.env.DSQL_API_USER_ID,
    });
    const { host, port, scheme, user_id } = grabedHostNames;

    const finalEncryptionKey =
        encryptionKey || process.env.DSQL_ENCRYPTION_PASSWORD;
    const finalEncryptionSalt =
        encryptionSalt || process.env.DSQL_ENCRYPTION_SALT;

    if (!finalEncryptionKey?.match(/.{8,}/)) {
        console.log("Encryption key is invalid");
        return {
            success: false,
            payload: null,
            msg: "Encryption key is invalid",
        };
    }
    if (!finalEncryptionSalt?.match(/.{8,}/)) {
        console.log("Encryption salt is invalid");
        return {
            success: false,
            payload: null,
            msg: "Encryption salt is invalid",
        };
    }

    /**
     * Check inputs
     *
     * @description Check inputs
     */

    if (!token || token?.match(/ /)) {
        return {
            success: false,
            payload: null,
            msg: "Please enter Google Access Token",
        };
    }

    /**
     * Initialize HTTP response variable
     */

    let httpResponse: APILoginFunctionReturn = {
        success: false,
    };

    /**
     * Check for local DB settings
     *
     * @description Look for local db settings in `.env` file and by pass the http request if available
     */
    const { DSQL_DB_HOST, DSQL_DB_USERNAME, DSQL_DB_PASSWORD, DSQL_DB_NAME } =
        process.env;

    if (
        DSQL_DB_HOST?.match(/./) &&
        DSQL_DB_USERNAME?.match(/./) &&
        DSQL_DB_PASSWORD?.match(/./) &&
        DSQL_DB_NAME?.match(/./) &&
        global.DSQL_USE_LOCAL
    ) {
        if (debug) {
            console.log(`Google login with Local Paradigm ...`);
        }

        httpResponse = await apiGoogleLogin({
            token,
            additionalFields,
            additionalData,
            debug,
        });
    } else {
        httpResponse = await new Promise((resolve, reject) => {
            const reqPayload = JSON.stringify({
                token,
                database,
                additionalFields,
                additionalData,
            });

            const httpsRequest = scheme.request(
                {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        "Content-Length": Buffer.from(reqPayload).length,
                        Authorization:
                            key ||
                            process.env.DSQL_FULL_ACCESS_API_KEY ||
                            process.env.DSQL_API_KEY,
                    },
                    port,
                    hostname: host,
                    path: `/api/user/${
                        apiUserID || grabedHostNames.user_id
                    }/google-login`,
                },

                /**
                 * Callback Function
                 *
                 * @description https request callback
                 */
                (response) => {
                    var str = "";

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

                    response.on("end", function () {
                        resolve(JSON.parse(str));
                    });

                    response.on("error", (err) => {
                        reject(err);
                    });
                }
            );
            httpsRequest.write(reqPayload);
            httpsRequest.end();
        });
    }

    /**
     * Make https request
     *
     * @description make a request to datasquirel.com
     */
    if (httpResponse?.success && httpResponse?.payload) {
        let encryptedPayload = encrypt({
            data: JSON.stringify(httpResponse.payload),
            encryptionKey: finalEncryptionKey,
            encryptionSalt: finalEncryptionSalt,
        });

        const cookieNames = getAuthCookieNames({
            database,
            userId: user_id,
        });

        if (httpResponse.csrf) {
            writeAuthFile(
                httpResponse.csrf,
                JSON.stringify(httpResponse.payload)
            );
        }

        httpResponse["cookieNames"] = cookieNames;
        httpResponse["key"] = String(encryptedPayload);

        const authKeyName = cookieNames.keyCookieName;
        const csrfName = cookieNames.csrfCookieName;

        response?.setHeader("Set-Cookie", [
            `${authKeyName}=${encryptedPayload};samesite=strict;path=/;HttpOnly=true;Secure=true`,
            `${csrfName}=${httpResponse.payload?.csrf_k};samesite=strict;path=/;HttpOnly=true`,
        ]);
    }

    return httpResponse;
}
