"use client"

import { useCallback, useEffect, useState, type ReactNode } from "react"
import { usePathname, useRouter } from "next/navigation"
import { useQueryClient } from "@tanstack/react-query"
import { useAuthStore } from "@/lib/api/auth-store"
import { isOidcConfigured } from "@/lib/auth/oidc-config"
import { clearClientAuthState } from "@/lib/auth/clear-client-auth-state"
import {
  fetchSession,
  applySessionToStore,
  type SessionPayload,
} from "@/lib/auth/session-sync"
import {
  useSessionGuardStore,
} from "@/lib/auth/session-guard-store"
import { isAuthPublicPath } from "@/lib/auth/public-paths"
import { getOidcEndSessionPath, POST_LOGOUT_PATH } from "@/lib/auth/oidc-config"
import { useNativeRuntime } from "@/lib/platform"
import {
  ensureNativeAccessToken,
  loadNativeSession,
} from "@/lib/auth/native-session"
import { nativeLogout } from "@/lib/auth/native-auth"
import { getRuntimeConfig } from "@/lib/runtime-config"
import { hydrateNativeRuntimeConfig } from "@/lib/runtime-config/native"

const REFRESH_LEAD_MS = 5 * 60 * 1000
const REFRESH_CHECK_MS = 60 * 1000

function isPublicPath(pathname: string) {
  return isAuthPublicPath(pathname)
}

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

  const applySession = useCallback(
    (data: SessionPayload) => applySessionToStore(data),
    []
  )

  const native = useNativeRuntime()

  const syncSession = useCallback(async () => {
    if (native) {
      const token = await ensureNativeAccessToken()
      if (token) return true
      logout()
      return false
    }
    const data = await fetchSession()
    if (data && applySession(data)) return true
    if (useAuthStore.getState().isAuthenticated()) return true
    logout()
    return false
  }, [applySession, logout, native])

  useEffect(() => {
    let cancelled = false

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

      if (native) {
        // Native: session lives in the OS secure store; no server selected yet
        // means the user must run the server picker first.
        await hydrateNativeRuntimeConfig()
        if (cancelled) return
        if (getRuntimeConfig()) {
          const ok = await loadNativeSession()
          if (!cancelled && ok) {
            await ensureNativeAccessToken()
          }
        }
        if (!cancelled) setReady(true)
        return
      }

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

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

      if (useAuthStore.getState().isAuthenticated()) {
        setReady(true)
        return
      }

      logout()
      setReady(true)
    }

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

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

  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 (authenticated) return
    // NativeAuthGate shows picker/login inline — avoid fighting redirects.
    if (native) return

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

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

  return <>{children}</>
}

export function useAuthLogout() {
  const router = useRouter()
  const queryClient = useQueryClient()
  const native = useNativeRuntime()

  return async () => {
    if (native) {
      // Unregister the push device token before dropping the session.
      try {
        const { unregisterPushOnLogout } = await import("@/lib/native/push")
        await unregisterPushOnLogout()
      } catch {
        /* best effort */
      }
      await nativeLogout()
      clearClientAuthState(queryClient)
      router.replace(POST_LOGOUT_PATH)
      return
    }

    await fetch("/api/auth/logout", { method: "POST", credentials: "include" })
    clearClientAuthState(queryClient)
    if (typeof window !== "undefined") {
      // Clear Ultimail session then terminate Authentik SSO (provider invalidation flow).
      window.location.assign(getOidcEndSessionPath())
    }
  }
}
