import { Auth0Client } from "@auth0/nextjs-auth0/server"
import { createOrUpdateUser, hasCompletedOnboarding } from "@codeflash-ai/common"
import { trackUserLogin } from "@/lib/analytics/tracking"
import { APP_ROUTES } from "@/lib/types"
import { NextResponse } from "next/server"

const LOGOUT_REDIRECT_URL =
  process.env.CODEFLASH_LOGOUT_REDIRECT_URL ??
  process.env.CODEFLASH_MARKETING_URL ??
  "https://codeflash.ai"

function redirectTo(path: string): NextResponse {
  const baseUrl = process.env.APP_BASE_URL || process.env.AUTH0_BASE_URL || "http://localhost:3000"
  return NextResponse.redirect(new URL(path, baseUrl))
}

// Auth0 v4 expects AUTH0_DOMAIN; derive from v3's AUTH0_ISSUER_BASE_URL if needed
const auth0Domain =
  process.env.AUTH0_DOMAIN ?? process.env.AUTH0_ISSUER_BASE_URL?.replace(/^https?:\/\//, "")

// Lazily instantiate Auth0Client so the module can be imported during
// `next build` static analysis (e.g. /_not-found) where env vars aren't set.
let _auth0: Auth0Client | undefined

function getAuth0Client(): Auth0Client {
  if (!_auth0) {
    if (!auth0Domain) {
      // During build/CI prerendering, Auth0 env vars aren't set.
      // Return a stub — getSession() returning null is correct (no user).
      return { getSession: async () => null } as unknown as Auth0Client
    }
    _auth0 = new Auth0Client({
      domain: auth0Domain,
      authorizationParameters: {
        scope: "openid profile email offline_access",
      },
      signInReturnToPath: APP_ROUTES.BASE,
      async beforeSessionSaved(session, idToken) {
        // Decode the ID token to get claims like nickname, name, picture
        if (idToken) {
          try {
            const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString())
            return {
              ...session,
              user: {
                ...session.user,
                nickname: payload.nickname ?? session.user.nickname,
                name: payload.name ?? session.user.name,
                picture: payload.picture ?? session.user.picture,
              },
            }
          } catch {
            // If decoding fails, return session as-is
          }
        }
        return session
      },
      async onCallback(error, context, session) {
        if (error) {
          const errorMessage = error.message || ""

          if (errorMessage.includes("allowlist-fail")) {
            const re = /allowlist-fail\s(.*)\s(.*)\)/
            const match = errorMessage.match(re)
            if (match != null) {
              const userNickname = match[2]
              return redirectTo(`/waitlist?username=${userNickname}`)
            }
          }

          return redirectTo("/login?error=callback_failed")
        }

        if (!session) {
          return redirectTo("/waitlist")
        }

        const user = session.user

        if (!user.sub || !user.nickname) {
          return redirectTo(context.returnTo || APP_ROUTES.BASE)
        }

        try {
          await createOrUpdateUser(user.sub, user.nickname, user.email ?? null, user.name ?? null)

          const [, completedOnboarding] = await Promise.all([
            trackUserLogin({
              userId: user.sub,
              username: user.nickname,
              email: user.email,
              name: user.name,
            }),
            hasCompletedOnboarding(user.sub),
          ])

          const intendedDestination = context.returnTo || APP_ROUTES.BASE

          const isAuthPath =
            intendedDestination.startsWith("/codeflash/auth") ||
            intendedDestination.includes("/codeflash/auth")

          if (!completedOnboarding && !isAuthPath) {
            return redirectTo("/onboarding")
          }

          return redirectTo(intendedDestination)
        } catch {
          return redirectTo(context.returnTo || APP_ROUTES.BASE)
        }
      },
      routes: {
        login: "/auth/login",
        logout: "/auth/logout",
        callback: "/auth/callback",
      },
      appBaseUrl: process.env.APP_BASE_URL || process.env.AUTH0_BASE_URL,
    })
  }
  return _auth0
}

// Proxy preserves the `auth0.getSession()` API while deferring construction
export const auth0: Auth0Client = new Proxy({} as Auth0Client, {
  get(_, prop) {
    const client = getAuth0Client()
    const value = (client as any)[prop]
    return typeof value === "function" ? value.bind(client) : value
  },
})
