import {
  TinaNodeBackend,
  LocalBackendAuthProvider,
} from "@tinacms/datalayer";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { getServerSession } from "next-auth/next";

import databaseClient from "../../../tina/__generated__/databaseClient";

const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === "true";

const TINA_CREDENTIALS_PROVIDER_NAME = "TinaCredentials";

const authenticate = async (
  dbClient: typeof databaseClient,
  username: string,
  password: string
) => {
  try {
    const result = await dbClient.authenticate({ username, password });
    return result.data?.authenticate || null;
  } catch (e) {
    return null;
  }
};

const authOptions = {
  callbacks: {
    jwt: async ({ token: jwt, account }: any) => {
      if (account) {
        try {
          if (jwt?.sub) {
            const result = await databaseClient.authorize({ sub: jwt.sub });
            jwt.role = result.data?.authorize ? "user" : "guest";
            jwt.passwordChangeRequired =
              result.data?.authorize?._password?.passwordChangeRequired ||
              false;
          }
        } catch {
          // ignore auth errors
        }
        if (jwt.role === undefined) {
          jwt.role = "guest";
        }
      }
      return jwt;
    },
    session: async ({ session, token: jwt }: any) => {
      session.user.role = jwt.role;
      session.user.passwordChangeRequired = jwt.passwordChangeRequired;
      session.user.sub = jwt.sub;
      return session;
    },
  },
  session: { strategy: "jwt" as const },
  secret: process.env.NEXTAUTH_SECRET as string,
  providers: [
    CredentialsProvider({
      name: TINA_CREDENTIALS_PROVIDER_NAME,
      credentials: {
        username: { label: "Username", type: "text" },
        password: { label: "Password", type: "password" },
      },
      authorize: async (credentials: any) =>
        authenticate(databaseClient, credentials.username, credentials.password),
    }),
  ],
};

const handler = TinaNodeBackend({
  authProvider: isLocal
    ? LocalBackendAuthProvider()
    : {
        initialize: async () => {},
        isAuthorized: async (req: any, res: any) => {
          const session = await getServerSession(req, res, authOptions);
          if (!req.session) {
            Object.defineProperty(req, "session", {
              value: session,
              writable: false,
            });
          }
          if (!(session as any)?.user) {
            return {
              errorCode: 401,
              errorMessage: "Unauthorized",
              isAuthorized: false,
            };
          }
          if ((session as any)?.user?.role !== "user") {
            return {
              errorCode: 403,
              errorMessage: "Forbidden",
              isAuthorized: false,
            };
          }
          return { isAuthorized: true };
        },
        extraRoutes: {
          auth: {
            secure: false,
            handler: async (req: any, res: any, opts: any) => {
              const url = new URL(
                req.url,
                `http://${req.headers?.host || "localhost"}`
              );
              const authSubRoutes = url.pathname
                ?.replace(`${opts.basePath}auth/`, "")
                ?.split("/");
              req.query.nextauth = authSubRoutes;
              await NextAuth(authOptions)(req, res);
            },
          },
        },
      },
  databaseClient,
});

export default (req: any, res: any) => {
  return handler(req, res);
};

export { authOptions };
