"use client"

import { useCallback, useEffect, useState, type ReactNode } from "react"
import { usePathname, useRouter } from "next/navigation"
import { useAuthStore, AUTH_STORAGE_KEY, LEGACY_AUTH_KEYS } from "@/lib/api/auth-store"
import { isOidcConfigured } from "@/lib/auth/oidc-config"
import type { PlatformUser } from "@/lib/auth/jwt-claims"

const PUBLIC_PREFIXES = ["/login", "/auth/", "/api/auth/"]
const REFRESH_LEAD_MS = 5 * 60 * 1000
const REFRESH_CHECK_MS = 60 * 1000

function isPublicPath(pathname: string) {
  if (pathname.startsWith("/drive/s/")) return true
  return PUBLIC_PREFIXES.some(
    (prefix) => pathname === prefix || pathname.startsWith(prefix)
  )
}

type SessionPayload = {
  authenticated?: boolean
  accessToken?: string
  refreshToken?: string | null
  expiresAt?: number
  user?: PlatformUser | null
}

async function fetchSession(): Promise<SessionPayload | null> {
  try {
    const res = await fetch("/api/auth/session", { credentials: "include" })
    if (!res.ok) return null
    return (await res.json()) as SessionPayload
  } catch {
    return null
  }
}

function canTrustPersistedAuth() {
  return useAuthStore.persist.hasHydrated() && useAuthStore.getState().isAuthenticated()
}

export function AuthProvider({ children }: { children: ReactNode }) {
  const pathname = usePathname()
  const router = useRouter()
  const login = useAuthStore((s) => s.login)
  const logout = useAuthStore((s) => s.logout)
  const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
  const [ready, setReady] = useState(
    () => !isOidcConfigured() || canTrustPersistedAuth()
  )

  const applySession = useCallback(
    (data: SessionPayload) => {
      if (data.authenticated && data.accessToken && data.expiresAt) {
        login(
          data.accessToken,
          data.refreshToken ?? "",
          data.expiresAt,
          data.user ?? null
        )
        return true
      }
      return false
    },
    [login]
  )

  const syncSession = useCallback(async () => {
    const data = await fetchSession()
    if (data && applySession(data)) return true
    logout()
    return false
  }, [applySession, logout])

  useEffect(() => {
    let cancelled = false

    async function bootstrap() {
      if (!isOidcConfigured()) {
        setReady(true)
        return
      }

      if (canTrustPersistedAuth()) {
        setReady(true)
      }

      const data = await fetchSession()
      if (cancelled) return

      if (data && applySession(data)) {
        setReady(true)
        return
      }

      if (data?.authenticated === false || !canTrustPersistedAuth()) {
        logout()
      }
      setReady(true)
    }

    if (!useAuthStore.persist.hasHydrated()) {
      const unsubHydrate = useAuthStore.persist.onFinishHydration(() => {
        if (useAuthStore.getState().isAuthenticated()) {
          setReady(true)
        }
      })
      void bootstrap()
      return () => {
        cancelled = true
        unsubHydrate()
      }
    }

    void bootstrap()
    return () => {
      cancelled = true
    }
  }, [applySession, logout])

  useEffect(() => {
    if (!ready || !isOidcConfigured()) return

    const interval = setInterval(() => {
      const { accessToken, expiresAt } = useAuthStore.getState()
      if (!accessToken || !expiresAt) return
      if (Date.now() >= expiresAt - REFRESH_LEAD_MS) {
        void syncSession()
      }
    }, REFRESH_CHECK_MS)

    return () => clearInterval(interval)
  }, [ready, syncSession])

  useEffect(() => {
    if (!ready || !isOidcConfigured()) return
    if (isPublicPath(pathname)) return
    if (isAuthenticated()) return

    let cancelled = false
    void syncSession().then((ok) => {
      if (cancelled || ok) return
      const returnTo = encodeURIComponent(pathname)
      router.replace(`/login?returnTo=${returnTo}`)
    })

    return () => {
      cancelled = true
    }
  }, [ready, pathname, isAuthenticated, router, syncSession])

  return <>{children}</>
}

export function useAuthLogout() {
  const logout = useAuthStore((s) => s.logout)
  const router = useRouter()

  return async () => {
    await fetch("/api/auth/logout", { method: "POST", credentials: "include" })
    logout()
    if (typeof window !== "undefined") {
      localStorage.removeItem(AUTH_STORAGE_KEY)
      for (const legacy of LEGACY_AUTH_KEYS) {
        localStorage.removeItem(legacy)
      }
    }
    router.replace("/login")
  }
}
