import NextAuth from "next-auth"

import { PRE_REFRESH_TIME_IN_SECONDS } from "@/constants/auth"
import { env } from "@/env/server"

import { LoginTypeEnum } from "./types/components/tracking"

import type { NextAuthConfig, User } from "next-auth"
import type { JWT } from "next-auth/jwt"
import type { OIDCConfig } from "next-auth/providers"

function getLoginType(user: User) {
  if (user?.login_with.includes("@")) {
    return LoginTypeEnum.email
  } else if (user?.login_with.toLowerCase() == LoginTypeEnum["email link"]) {
    return LoginTypeEnum["email link"]
  } else {
    return LoginTypeEnum["membership number"]
  }
}

async function refreshTokens(token: JWT) {
  try {
    console.log("token-debug Access token expired, trying to refresh it.", {
      expires_at: token.expires_at,
      sub: token.sub,
      token: token.access_token,
    })
    const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
      body: new URLSearchParams({
        client_id: env.CURITY_CLIENT_ID_USER,
        client_secret: env.CURITY_CLIENT_SECRET_USER,
        grant_type: "refresh_token",
        refresh_token: token.refresh_token,
      }),
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      method: "POST",
    })

    const new_tokens = await response.json()

    if (!response.ok) {
      console.log("token-debug Token response was not ok", {
        status: response.status,
        statusText: response.statusText,
        sub: token.sub,
      })
      throw new_tokens
    }

    console.log("token-debug Successfully got new token(s)", {
      expires_at: new_tokens.expires_at,
      got_new_refresh_token: new_tokens.refresh_token !== token.refresh_token,
      got_new_access_token: new_tokens.access_token !== token.access_token,
      sub: token.sub,
    })

    const expiresAt = new_tokens.expires_in
      ? Date.now() + new_tokens.expires_in * 1000
      : undefined

    return {
      ...token,
      access_token: new_tokens.access_token,
      expires_at: expiresAt,
      refresh_token: new_tokens.refresh_token,
    }
  } catch (error) {
    console.log("token-debug Error thrown when trying to refresh", {
      error,
      sub: token.sub,
    })
    return {
      ...token,
      error: "RefreshAccessTokenError" as const,
    }
  }
}

const curityProvider = {
  id: "curity",
  name: "Curity",
  type: "oidc",
  clientId: env.CURITY_CLIENT_ID_USER,
  clientSecret: env.CURITY_CLIENT_SECRET_USER,
  // FIXME: This is incorrect. We should not hard code this.
  //        It should be ${env.CURITY_ISSUER_USER}.
  //        This change requires sync between Curity deploy and CurrentWeb and NewWeb.
  issuer: "https://scandichotels.com",
  authorization: {
    url: `${env.CURITY_ISSUER_USER}/oauth/v2/authorize`,
  },
  token: {
    url: `${env.CURITY_ISSUER_USER}/oauth/v2/token`,
  },
  userinfo: {
    url: `${env.CURITY_ISSUER_USER}/oauth/v2/userinfo`,
  },
  profile(profile: User) {
    return {
      id: profile.id,
      sub: profile.sub,
      given_name: profile.given_name,
      login_with: profile.login_with,
    }
  },
} satisfies OIDCConfig<User>

export const config = {
  basePath: "/api/web/auth",
  debug: env.NEXTAUTH_DEBUG,
  providers: [curityProvider],
  redirectProxyUrl: env.NEXTAUTH_REDIRECT_PROXY_URL,
  trustHost: true,
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async signIn() {
      return true
    },
    async session({ session, token }) {
      session.error = token.error
      if (session.user) {
        return {
          ...session,
          token,
          user: {
            ...session.user,
            id: token.sub,
          },
        }
      }

      return session
    },
    async redirect({ baseUrl, url }) {
      console.log(`[auth] deciding redirect URL`, { baseUrl, url })
      if (url.startsWith("/")) {
        console.log(`[auth] relative URL accepted, returning: ${baseUrl}${url}`)
        // Allows relative callback URLs
        return `${baseUrl}${url}`
      } else {
        // Assume absolute URL
        try {
          const parsedUrl = new URL(url)
          if (
            /\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname)
          ) {
            console.log(`[auth] subdomain URL accepted, returning: ${url}`)
            // Allows any subdomains on all top level domains above
            return url
          } else if (parsedUrl.origin === baseUrl) {
            // Allows callback URLs on the same origin
            console.log(`[auth] origin URL accepted, returning: ${url}`)
            return url
          }
        } catch (e) {
          console.error(`[auth] error parsing incoming URL for redirection`, e)
        }
      }
      console.log(`[auth] URL denied, returning base URL: ${baseUrl}`)
      return baseUrl
    },
    async authorized({ auth, request }) {
      return true
    },
    async jwt({ account, session, token, trigger, user, profile }) {
      const loginType = getLoginType(user)
      if (trigger === "signIn" && account) {
        const mfa_scope =
          profile?.amr ==
          "urn:se:curity:authentication:otp-authenticator:OTP-Authenticator_web"
        const tokenExpiry = account.expires_at
          ? account.expires_at * 1000
          : undefined
        const mfa_expires_at = mfa_scope && tokenExpiry ? tokenExpiry : 0
        return {
          access_token: account.access_token,
          expires_at: tokenExpiry,
          refresh_token: account.refresh_token,
          loginType,
          mfa_scope: mfa_scope,
          mfa_expires_at: mfa_expires_at,
        }
      } else if (
        token.expires_at &&
        Date.now() > token.expires_at - PRE_REFRESH_TIME_IN_SECONDS * 1000 &&
        session?.doRefresh
      ) {
        return refreshTokens(token)
      }

      return token
    },
  },
  // events: {
  //   async signIn() {
  //     console.log("#### SIGNIN EVENT ARGS ######")
  //     console.log(arguments)
  //     console.log("#### END - SIGNIN EVENT ARGS ######")
  //   },
  //   async signOut() {
  //     console.log("#### SIGNOUT EVENT ARGS ######")
  //     console.log(arguments)
  //     console.log("#### END - SIGNOUT EVENT ARGS ######")
  //   },
  //   async session() {
  //     console.log("#### SESSION EVENT ARGS ######")
  //     console.log(arguments)
  //     console.log("#### END - SESSION EVENT ARGS ######")
  //   },
  // },
  // logger: {
  //   error(code, ...message) {
  //     console.info("ERROR LOGGER")
  //     console.error(code, message)
  //   },
  //   warn(code, ...message) {
  //     console.info("WARN LOGGER")
  //     console.warn(code, message)
  //   },
  //   debug(code, ...message) {
  //     console.info("DEBUG LOGGER")
  //     console.debug(code, message)
  //   },
  // },
} satisfies NextAuthConfig

export const {
  handlers: { GET, POST },
  auth,
  signIn,
  signOut,
} = NextAuth(config)
