import http from "http";
import decrypt from "../package-shared/functions/dsql/decrypt";
import getAuthCookieNames from "../package-shared/functions/backend/cookies/get-auth-cookie-names";
import { checkAuthFile } from "../package-shared/functions/backend/auth/write-auth-files";
import parseCookies from "../package-shared/utils/backend/parseCookies";
import { AuthenticatedUser } from "../package-shared/types";
import getCsrfHeaderName from "../utils/get-csrf-header-name";
import grabHostNames from "../package-shared/utils/grab-host-names";

const minuteInMilliseconds = 60000;
const hourInMilliseconds = minuteInMilliseconds * 60;
const dayInMilliseconds = hourInMilliseconds * 24;
const weekInMilliseconds = dayInMilliseconds * 7;
const monthInMilliseconds = dayInMilliseconds * 30;
const yearInMilliseconds = dayInMilliseconds * 365;

type Param = {
    request?: http.IncomingMessage & { [s: string]: any };
    req?: http.IncomingMessage & { [s: string]: any };
    cookieString?: string;
    encryptedUserString?: string;
    encryptionKey?: string;
    encryptionSalt?: string;
    level?: "deep" | "normal";
    database?: string;
    dsqlUserId?: string | number;
    expiry?: number;
    csrfHeaderName?: string;
    debug?: boolean;
};

/**
 * Authenticate User from request
 * ==============================================================================
 * @description This Function takes in a request object and returns a user object
 * with the user's data
 */
export default function userAuth({
    request,
    req,
    encryptionKey,
    encryptionSalt,
    level,
    database,
    dsqlUserId,
    encryptedUserString,
    expiry = weekInMilliseconds,
    cookieString,
    csrfHeaderName,
    debug,
}: Param): AuthenticatedUser {
    try {
        const finalRequest = req || request;

        const { user_id } = grabHostNames({ userId: dsqlUserId });

        const cookies = parseCookies({
            request: finalRequest,
            cookieString,
        });

        if (debug) {
            console.log("userAuth:cookies:", cookies);
        }

        const keyNames = getAuthCookieNames({
            userId: user_id,
            database: database || process.env.DSQL_DB_NAME,
        });

        if (debug) {
            console.log("userAuth:keyNames:", keyNames);
        }

        const key = encryptedUserString
            ? encryptedUserString
            : cookies[keyNames.keyCookieName];

        if (debug) {
            console.log("userAuth:key:", key);
        }

        /**
         * Grab the payload
         *
         * @description Grab the payload
         */
        let userPayloadJSON = decrypt({
            encryptedString: key,
            encryptionKey,
            encryptionSalt,
        });

        if (debug) {
            console.log("userAuth:userPayloadJSON:", userPayloadJSON);
        }

        /**
         * Grab the payload
         *
         * @description Grab the payload
         */
        if (!userPayloadJSON) {
            return {
                success: false,
                payload: null,
                msg: "Couldn't Decrypt cookie",
            };
        }

        /**
         * Grab the payload
         *
         * @description Grab the payload
         */

        /** @type {import("../package-shared/types").DATASQUIREL_LoggedInUser} */
        let userObject: import("../package-shared/types").DATASQUIREL_LoggedInUser =
            JSON.parse(userPayloadJSON);

        if (debug) {
            console.log("userAuth:userObject:", userObject);
        }

        if (!userObject.csrf_k) {
            return {
                success: false,
                payload: null,
                msg: "No CSRF_K in decrypted payload",
            };
        }

        if (!checkAuthFile(userObject.csrf_k)) {
            return {
                success: false,
                payload: null,
                msg: "Auth file doesn't exist",
            };
        }

        /**
         * Grab the payload
         *
         * @description Grab the payload
         */
        if (level?.match(/deep/i) && finalRequest) {
            const finalCsrfHeaderName = csrfHeaderName || getCsrfHeaderName();
            if (
                finalRequest.headers[finalCsrfHeaderName] !== userObject.csrf_k
            ) {
                return {
                    success: false,
                    payload: null,
                    msg: "CSRF_K mismatch",
                };
            }
        }

        const payloadCreationDate = Number(userObject.date);

        if (
            Number.isNaN(payloadCreationDate) ||
            typeof payloadCreationDate !== "number"
        ) {
            return {
                success: false,
                payload: null,
                msg: "Payload Creation Date is not a number",
            };
        }

        const timeElapsed = Date.now() - payloadCreationDate;

        const finalExpiry = process.env.DSQL_SESSION_EXPIRY_TIME
            ? Number(process.env.DSQL_SESSION_EXPIRY_TIME)
            : expiry;

        if (timeElapsed > finalExpiry) {
            return {
                success: false,
                payload: null,
                msg: "Session has expired",
            };
        }

        /**
         * Return User Object
         *
         * @description Return User Object
         */
        return {
            success: true,
            payload: userObject,
        };
    } catch (error: any) {
        /**
         * Return User Object
         *
         * @description Return User Object
         */
        return {
            success: false,
            payload: null,
            msg: error.message,
        };
    }
}