import http from "http";
import fs from "fs";
import path from "path";
import encrypt from "../../package-shared/functions/dsql/encrypt";
import grabHostNames from "../../package-shared/utils/grab-host-names";
import apiGithubLogin from "../../package-shared/functions/api/users/social/api-github-login";

interface FunctionReturn {
    success: boolean;
    user: {
        id: number;
        first_name: string;
        last_name: string;
        csrf_k: string;
        social_id: string;
    } | null;
    dsqlUserId?: number;
    msg?: string;
}

type Param = {
    key: string;
    code: string;
    email: string | null;
    database: string;
    clientId: string;
    clientSecret: string;
    response: http.ServerResponse;
    encryptionKey: string;
    encryptionSalt: string;
    additionalFields?: string[];
    additionalData?: { [s: string]: string | number };
    user_id?: boolean;
};

/**
 * # SERVER FUNCTION: Login with google Function
 */
export default async function githubAuth({
    key,
    code,
    email,
    database,
    clientId,
    clientSecret,
    response,
    encryptionKey,
    encryptionSalt,
    additionalFields,
    user_id,
    additionalData,
}: Param): Promise<FunctionReturn | undefined> {
    /**
     * Check inputs
     *
     * @description Check inputs
     */
    const grabedHostNames = grabHostNames();
    const { host, port, scheme } = grabedHostNames;

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

    if (!database || database?.match(/ /)) {
        return {
            success: false,
            user: null,
            msg: "Please provide database slug name you want to access",
        };
    }

    if (!clientId || clientId?.match(/ /)) {
        return {
            success: false,
            user: null,
            msg: "Please enter Github OAUTH client ID",
        };
    }

    /**
     * Initialize HTTP response variable
     */
    let httpResponse;

    /**
     * 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,
        DSQL_KEY,
        DSQL_REF_DB_NAME,
        DSQL_FULL_SYNC,
    } = process.env;

    if (
        DSQL_DB_HOST?.match(/./) &&
        DSQL_DB_USERNAME?.match(/./) &&
        DSQL_DB_PASSWORD?.match(/./) &&
        DSQL_DB_NAME?.match(/./)
    ) {
        /** @type {import("../../package-shared/types").DSQL_DatabaseSchemaType | undefined | undefined} */
        let dbSchema:
            | import("../../package-shared/types").DSQL_DatabaseSchemaType
            | undefined
            | undefined;

        try {
            const localDbSchemaPath = path.resolve(
                process.cwd(),
                "dsql.schema.json"
            );
            dbSchema = JSON.parse(fs.readFileSync(localDbSchemaPath, "utf8"));
        } catch (error) {}

        httpResponse = await apiGithubLogin({
            code,
            email: email || undefined,
            clientId,
            clientSecret,
            additionalFields,
            database: DSQL_DB_NAME,
            additionalData,
        });
    } else {
        /**
         * Make https request
         *
         * @description make a request to datasquirel.com
         * @type {FunctionReturn} - Https response object
         */
        httpResponse = (await new Promise((resolve, reject) => {
            const reqPayload = JSON.stringify({
                code,
                email,
                clientId,
                clientSecret,
                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/${
                        user_id || grabedHostNames.user_id
                    }/github-login`,
                },

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

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

                    response.on("end", function () {
                        try {
                            resolve(JSON.parse(str));
                        } catch (error) {
                            console.log(error);

                            resolve({
                                success: false,
                                user: null,
                                msg: "Something went wrong",
                            });
                        }
                    });

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

    ////////////////////////////////////////
    ////////////////////////////////////////
    ////////////////////////////////////////

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

        const { user, dsqlUserId } = httpResponse;

        const authKeyName = `datasquirel_${dsqlUserId}_${database}_auth_key`;
        const csrfName = `datasquirel_${dsqlUserId}_${database}_csrf`;

        response.setHeader("Set-Cookie", [
            `${authKeyName}=${encryptedPayload};samesite=strict;path=/;HttpOnly=true;Secure=true`,
            `${csrfName}=${user.csrf_k};samesite=strict;path=/;HttpOnly=true`,
            `dsqluid=${dsqlUserId};samesite=strict;path=/;HttpOnly=true`,
            `datasquirel_social_id=${user.social_id};samesite=strict;path=/`,
        ]);
    }

    return httpResponse;
}