import type { Provider } from "next-auth/providers";
import { env } from "@/env.mjs";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";
import Okta from "next-auth/providers/okta";
import Keycloak from "next-auth/providers/keycloak";
import Gitlab from "next-auth/providers/gitlab";
import MicrosoftEntraID from "next-auth/providers/microsoft-entra-id";
import { prisma } from "@/prisma";
import { OAuth2Client } from "google-auth-library";
import Credentials from "next-auth/providers/credentials";
import type { User as AuthJsUser } from "next-auth";
import { onCreateUser } from "@/lib/authUtils";
import { createLogger } from "@sourcebot/logger";
import { hasEntitlement } from "@sourcebot/shared";

const logger = createLogger('web-sso');

export const getSSOProviders = (): Provider[] => {
    const providers: Provider[] = [];

    if (env.AUTH_EE_GITHUB_CLIENT_ID && env.AUTH_EE_GITHUB_CLIENT_SECRET) {
        providers.push(GitHub({
            clientId: env.AUTH_EE_GITHUB_CLIENT_ID,
            clientSecret: env.AUTH_EE_GITHUB_CLIENT_SECRET,
            enterprise: {
                baseUrl: env.AUTH_EE_GITHUB_BASE_URL,
            },
            authorization: {
                params: {
                    scope: [
                        'read:user',
                        'user:email',
                        // Permission syncing requires the `repo` scope in order to fetch repositories
                        // for the authenticated user.
                        // @see: https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user
                        ...(env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && hasEntitlement('permission-syncing') ?
                            ['repo'] :
                            []
                        ),
                    ].join(' '),
                },
            },
        }));
    }

    if (env.AUTH_EE_GITLAB_CLIENT_ID && env.AUTH_EE_GITLAB_CLIENT_SECRET) {
        providers.push(Gitlab({
            clientId: env.AUTH_EE_GITLAB_CLIENT_ID,
            clientSecret: env.AUTH_EE_GITLAB_CLIENT_SECRET,
            authorization: {
                url: `${env.AUTH_EE_GITLAB_BASE_URL}/oauth/authorize`,
                params: {
                    scope: "read_user",
                },
            },
            token: {
                url: `${env.AUTH_EE_GITLAB_BASE_URL}/oauth/token`,
            },
            userinfo: {
                url: `${env.AUTH_EE_GITLAB_BASE_URL}/api/v4/user`,
            },
        }));
    }

    if (env.AUTH_EE_GOOGLE_CLIENT_ID && env.AUTH_EE_GOOGLE_CLIENT_SECRET) {
        providers.push(Google({
            clientId: env.AUTH_EE_GOOGLE_CLIENT_ID,
            clientSecret: env.AUTH_EE_GOOGLE_CLIENT_SECRET,
        }));
    }

    if (env.AUTH_EE_OKTA_CLIENT_ID && env.AUTH_EE_OKTA_CLIENT_SECRET && env.AUTH_EE_OKTA_ISSUER) {
        providers.push(Okta({
            clientId: env.AUTH_EE_OKTA_CLIENT_ID,
            clientSecret: env.AUTH_EE_OKTA_CLIENT_SECRET,
            issuer: env.AUTH_EE_OKTA_ISSUER,
        }));
    }

    if (env.AUTH_EE_KEYCLOAK_CLIENT_ID && env.AUTH_EE_KEYCLOAK_CLIENT_SECRET && env.AUTH_EE_KEYCLOAK_ISSUER) {
        providers.push(Keycloak({
            clientId: env.AUTH_EE_KEYCLOAK_CLIENT_ID,
            clientSecret: env.AUTH_EE_KEYCLOAK_CLIENT_SECRET,
            issuer: env.AUTH_EE_KEYCLOAK_ISSUER,
        }));
    }

    if (env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID && env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET && env.AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER) {
        providers.push(MicrosoftEntraID({
            clientId: env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID,
            clientSecret: env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET,
            issuer: env.AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER,
        }));
    }

    if (env.AUTH_EE_GCP_IAP_ENABLED && env.AUTH_EE_GCP_IAP_AUDIENCE) {
        providers.push(Credentials({
            id: "gcp-iap",
            name: "Google Cloud IAP",
            credentials: {},
            authorize: async (credentials, req) => {
                try {
                    const iapAssertion = req.headers?.get("x-goog-iap-jwt-assertion");
                    if (!iapAssertion || typeof iapAssertion !== "string") {
                        logger.warn("No IAP assertion found in headers");
                        return null;
                    }

                    const oauth2Client = new OAuth2Client();

                    const { pubkeys } = await oauth2Client.getIapPublicKeys();
                    const ticket = await oauth2Client.verifySignedJwtWithCertsAsync(
                        iapAssertion,
                        pubkeys,
                        env.AUTH_EE_GCP_IAP_AUDIENCE,
                        ['https://cloud.google.com/iap']
                    );

                    const payload = ticket.getPayload();
                    if (!payload) {
                        logger.warn("Invalid IAP token payload");
                        return null;
                    }

                    const email = payload.email;
                    const name = payload.name || payload.email;
                    const image = payload.picture;

                    if (!email) {
                        logger.warn("Missing email in IAP token");
                        return null;
                    }

                    const existingUser = await prisma.user.findUnique({
                        where: { email }
                    });

                    if (!existingUser) {
                        const newUser = await prisma.user.create({
                            data: {
                                email,
                                name,
                                image,
                            }
                        });

                        const authJsUser: AuthJsUser = {
                            id: newUser.id,
                            email: newUser.email,
                            name: newUser.name,
                            image: newUser.image,
                        };

                        await onCreateUser({ user: authJsUser });
                        return authJsUser;
                    } else {
                        return {
                            id: existingUser.id,
                            email: existingUser.email,
                            name: existingUser.name,
                            image: existingUser.image,
                        };
                    }
                } catch (error) {
                    logger.error("Error verifying IAP token:", error);
                    return null;
                }
            },
        }));
    }

    return providers;
}