import NextAuth from "next-auth"

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

/** 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

          const data = await res.json()

          return {
            id: data.member.id,
            email: data.member.email,
            name: data.member.clubName,
            role: data.member.role,
            clubId: data.member.clubId,
            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 }) {
      // Handle relative URLs
      if (url.startsWith("/")) return `${baseUrl}${url}`
      // Handle same-origin URLs
      if (new URL(url).origin === baseUrl) return url
      return baseUrl
    },
  },
  pages: {
    signIn: "/login",
    error: "/login",
  },
  session: {
    strategy: "jwt",
  },
})
