import type {
  GetServerSidePropsContext,
  NextApiRequest,
  NextApiResponse,
} from "next";
import {
  NextAuthOptions,
  getServerSession as internalGetServerSession,
} from "next-auth";
import Google from "next-auth/providers/google";
import Credentials from "next-auth/providers/credentials";
import Apple from "next-auth/providers/apple";

const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };

const fetchRoles = async () => {
  const url = `${process.env.ZAMMAD_URL}/api/v1/roles`;
  const res = await fetch(url, { headers });
  const roles = await res.json();
  const formattedRoles = roles.reduce((acc: any, role: any) => {
    acc[role.id] = role.name;
    return acc;
  }, {});
  return formattedRoles;
};

const fetchUser = async (email: string) => {
  const url = `${process.env.ZAMMAD_URL}/api/v1/users/search?query=login:${email}&limit=1`;
  const res = await fetch(url, { headers });
  const users = await res.json();
  const user = users?.[0];

  return user;
};

const getUserRoles = async (email: string) => {
  try {
    const user = await fetchUser(email);
    const allRoles = await fetchRoles();
    const roles = user.role_ids.map((roleID: number) => {
      const role = allRoles[roleID];
      return role ? role.toLowerCase().replace(" ", "_") : null;
    });
    return roles.filter((role: string) => role !== null);
  } catch (e) {
    console.log({ e });
    return [];
  }
};

const login = async (email: string, password: string) => {
  const url = `${process.env.ZAMMAD_URL}/api/v1/users/me`;
  const authorization =
    "Basic " + Buffer.from(email + ":" + password).toString("base64");
  const res = await fetch(url, {
    headers: {
      authorization,
    },
  });
  const user = await res.json();

  if (user && !user.error && user.id) {
    return user;
  } else {
    return null;
  }
};

const providers = [];

if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
  providers.push(
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  );
} else if (process.env.APPLE_CLIENT_ID && process.env.APPLE_CLIENT_SECRET) {
  providers.push(
    Apple({
      clientId: process.env.APPLE_CLIENT_ID,
      clientSecret: process.env.APPLE_CLIENT_SECRET,
    }),
  );
} else {
  providers.push(
    Credentials({
      name: "Zammad",
      credentials: {
        email: { label: "Email", type: "text" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        const user = await login(credentials.email, credentials.password);
        if (user) {
          return user;
        } else {
          return null;
        }
      },
    }),
  );
}

export const authOptions: NextAuthOptions = {
  pages: {
    signIn: "/login",
    error: "/login",
    signOut: "/logout",
  },
  providers,
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    signIn: async ({ user }) => {
      const roles = (await getUserRoles(user.email)) ?? [];
      return roles.includes("admin") || roles.includes("agent");
    },
    session: async ({ session, token }) => {
      // @ts-ignore
      session.user.roles = token.roles ?? [];
      // @ts-ignore
      session.user.zammadCsrfToken = token.zammadCsrfToken;

      return session;
    },
    jwt: async ({ token, user, trigger, session }) => {
      if (user) {
        token.roles = (await getUserRoles(user.email)) ?? [];
      }

      if (session && trigger === "update") {
        token.zammadCsrfToken = session.zammadCsrfToken;
      }

      return token;
    },
  },
};

export const getServerSession = (
  ...args:
    | [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
    | [NextApiRequest, NextApiResponse]
    | []
) => internalGetServerSession(...args, authOptions);
