import type { TypeProvider } from "supertokens-node/recipe/thirdparty";
import axios from "axios";
import type { Request } from "express";

type TypeThirdPartyProviderGithubConfig = {
    clientId: string;
    clientSecret: string;
    scope?: string[];
    authorisationRedirect?: {
        params?: { [key: string]: string | ((request: Request) => string) };
    };
};

export function Github(config: TypeThirdPartyProviderGithubConfig): TypeProvider {
    const id = "github";

    async function get(
        redirectURI: string | undefined,
        authCodeFromRequest: string | undefined
    ) {
        let accessTokenAPIURL = "https://github.com/login/oauth/access_token";
        let accessTokenAPIParams: { [key: string]: string } = {
            client_id: config.clientId,
            client_secret: config.clientSecret,
        };
        if (authCodeFromRequest !== undefined) {
            accessTokenAPIParams.code = authCodeFromRequest;
        }
        if (redirectURI !== undefined) {
            accessTokenAPIParams.redirect_uri = redirectURI;
        }
        let authorisationRedirectURL = "https://github.com/login/oauth/authorize";
        let scopes = ["read:user", "user:email"];
        if (config.scope !== undefined) {
            scopes = Array.from(new Set(config.scope));
        }
        let additionalParams =
            config.authorisationRedirect === undefined || config.authorisationRedirect.params === undefined
                ? {}
                : config.authorisationRedirect.params;
        let authorizationRedirectParams: { [key: string]: string } = {
            scope: scopes.join(" "),
            client_id: config.clientId,
            ...additionalParams,
        };

        async function getProfileInfo(accessTokenAPIResponse: {
            access_token: string;
            expires_in: number;
            token_type: string;
        }) {
            let accessToken = accessTokenAPIResponse.access_token;
            let authHeader = `Bearer ${accessToken}`;
            let response = await axios({
                method: "get",
                url: "https://api.github.com/user",
                headers: {
                    Authorization: authHeader,
                    Accept: "application/vnd.github.v3+json",
                },
            });
            let emailsInfoResponse = await axios({
                url: "https://api.github.com/user/emails",
                headers: {
                    Authorization: authHeader,
                    Accept: "application/vnd.github.v3+json",
                },
            });
            let userInfo = response.data;
            let emailsInfo = emailsInfoResponse.data;
            let id = userInfo.id.toString();
            let emailInfo = emailsInfo.find((e: any) => e.primary);
            if (emailInfo === undefined) {
                return {
                    id,
                };
            }
            let isVerified = emailInfo !== undefined ? emailInfo.verified : false;
            return {
                id,
                email: {
                    id: emailInfo.email,
                    isVerified,
                },
            };
        }
        return {
            accessTokenAPI: {
                url: accessTokenAPIURL,
                params: accessTokenAPIParams,
            },
            authorisationRedirect: {
                url: authorisationRedirectURL,
                params: authorizationRedirectParams,
            },
            getProfileInfo,
        };
    }

    return {
        id,
        get,
    };
}