import NextAuth, { NextAuthConfig } from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
// import LinkedinProvider from 'next-auth/providers/linkedin';
import { Session } from 'next-auth';
import { AuthenticatedUser } from './types/next-auth';
import { defaultLocale } from '../i18n';

export const myAuth = () => {
   // Use mock authentication in development when enabled via environment variable
   if (process.env.NODE_ENV === 'development' && process.env.USE_MOCK_AUTH === 'true') {
      const session: Session = {
         user: {
            id: process.env.MOCK_USER_ID || "109754742613069927799",
            name: process.env.MOCK_USER_NAME || "Nikola Derežić",
         },
         expires: "123",
      };
      
      return Promise.resolve(session);
   }

   return auth();
}

export const authConfig: NextAuthConfig = {
   callbacks: {
      // method verifies if the user is logged in or not
      // -> is called by Next-Auth when the midleware calls the `auth` method (exported below)
      authorized({ auth, request: { nextUrl } }) {
         const isLoggedIn = !!auth?.user;
         return (isLoggedIn);
      },
      // method is called when the user is not logged in
      // this is a hack which takes user ID and assigns it temporaty to the token, which is then used to extend Session.user
      // see: https://stackoverflow.com/questions/70409219/get-user-id-from-session-in-next-auth-client
      jwt({ token, account, user }) {
         if (account) {
           token.accessToken = account.access_token
           token.id = user?.id
         }
         return token
      },
      // method is called after the JWT token is created
      // this is a hack which takes user ID temporaty assigned to the token and assigns it to the Session.user
      // see: https://stackoverflow.com/questions/70409219/get-user-id-from-session-in-next-auth-client
      async session({ session, token }:{ session:Session, token:any }) {
         if(session.user && token) {
            session.user.id = token.id;
         }
         return session;
       },
   },
   providers: [
      GoogleProvider({
         clientId: process.env.GOOGLE_ID,
         clientSecret: process.env.GOOGLE_SECRET,
      }),
      // // config based on https://github.com/nextauthjs/next-auth/issues/8831
      // LinkedinProvider({
      //    clientId: process.env.LINKEDIN_ID,
      //    clientSecret: process.env.LINKEDIN_SECRET,
      //    authorization: { params: { scope: 'email openid' } },
      //    issuer: 'https://www.linkedin.com',
      //    jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks",
      //    async profile(profile) {
      //        return {
      //            id: profile.sub,
      //            name: profile.name,
      //            firstname: profile.given_name,
      //            lastname: profile.family_name,
      //            email: profile.email
      //        }
      //    },
      // })
   ],
   secret: process.env.AUTH_SECRET,
   trustHost: true, // needs to be set to false for the NextJS to work behing Traefik
   session: {
      // Use JSON Web Tokens for session instead of database sessions.
      // This option can be used with or without a database for users/accounts.
      // Note: `jwt` is automatically set to `true` if no database is specified.
      strategy: 'jwt'
   },
   pages: {
      signIn: `/${defaultLocale}/login`,
   },
};

export const { auth, handlers: { GET, POST } } = NextAuth(authConfig);

export type AuthErrorMessage = {
   message: string,
   errors: {
      message: string,
   }
}

export const isAuthErrorMessage = (obj: any): obj is AuthErrorMessage => {
   return (obj.message && obj.errors && obj.errors.message);
}

export const withUser = <T, A extends any[]>(fn: (user: AuthenticatedUser, ...args:A) => Promise<T>) => async (...args:A) => {
   const session = await myAuth();

   if(!session) {
      throw new Error("Not authenticated")
   }
   const { user } = session;

   return(fn(user, ...args));
}