import NextAuth from "next-auth"

import Credentials from "next-auth/providers/credentials"

/**
 * Decode a JWT payload (no signature verification — we only need the claims for
 * populating the session). Returns {} on any parse failure.
 */
function decodeJwtPayload(token: string): Record<string, unknown> {
  try {
    const payload = token.split(".")[1]
    const json = Buffer.from(payload, "base64url").toString("utf8")
    return JSON.parse(json) as Record<string, unknown>
  } catch {
    return {}
  }
}

/** Helper: fetch with an AbortController timeout (default 5s) */
async function fetchWithTimeout(
  url: string,
  options: RequestInit,
  timeoutMs = 5000
): Promise<Response> {
  const controller = new AbortController()
  const timeout = setTimeout(() => controller.abort(), timeoutMs)
  try {
    const res = await fetch(url, { ...options, signal: controller.signal })
    return res
  } finally {
    clearTimeout(timeout)
  }
}

export const { handlers, signIn, signOut, auth } = NextAuth({
  trustHost: true,
  providers: [
    Credentials({
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        try {
          const res = await fetchWithTimeout(
            `${process.env.BACKEND_URL}/api/v1/auth/login`,
            {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({
                email: credentials.email,
                password: credentials.password,
              }),
            }
          )

          if (!res.ok) return null

          // Backend LoginResponse is flat: { accessToken, refreshToken, expiresIn, role }.
          // Identity claims (sub=userId, email, tenant_id=clubId) live inside the JWT.
          const data = await res.json()
          const claims = decodeJwtPayload(data.accessToken)

          return {
            id: (claims.sub as string) ?? data.accessToken,
            email: (claims.email as string) ?? credentials.email,
            name: (claims.email as string) ?? credentials.email,
            role: data.role ?? (claims.role as string),
            clubId: (claims.tenant_id as string) ?? "",
            accessToken: data.accessToken,
            refreshToken: data.refreshToken,
            expiresAt: Date.now() + data.expiresIn * 1000,
          }
        } catch {
          // Backend unreachable or timeout — fail gracefully
          return null
        }
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {
      // Initial sign-in: transfer user data to token
      if (user) {
        token.role = user.role
        token.clubId = user.clubId
        token.accessToken = user.accessToken
        token.refreshToken = user.refreshToken
        token.expiresAt = user.expiresAt
      }

      // Token refresh: if access token expired, use refresh token
      if (token.expiresAt && Date.now() > (token.expiresAt as number)) {
        try {
          const res = await fetchWithTimeout(
            `${process.env.BACKEND_URL}/api/v1/auth/refresh`,
            {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ refreshToken: token.refreshToken }),
            }
          )

          if (res.ok) {
            const refreshed = await res.json()
            token.accessToken = refreshed.accessToken
            token.refreshToken = refreshed.refreshToken
            token.expiresAt = Date.now() + refreshed.expiresIn * 1000
          } else {
            token.error = "RefreshTokenExpired"
          }
        } catch {
          token.error = "RefreshTokenError"
        }
      }

      return token
    },
    async session({ session, token }) {
      session.user.role = token.role as string
      session.user.clubId = token.clubId as string
      session.error = token.error as string | undefined
      return session
    },
    async redirect({ url, baseUrl }) {
      // Guard: url may be undefined during static generation
      if (!url) return baseUrl
      // Handle relative URLs
      if (url.startsWith("/")) return `${baseUrl}${url}`
      // Handle same-origin URLs
      try {
        if (new URL(url).origin === baseUrl) return url
      } catch {
        // Invalid URL — fall back to baseUrl
      }
      return baseUrl
    },
  },
  pages: {
    signIn: "/login",
    error: "/login",
  },
  session: {
    strategy: "jwt",
  },
})
