/**
 * SessionProvider — fetches GET /api/session once on mount via TanStack Query.
 *
 * Contract (orchestrator-decisions.md §4, §11):
 * - 200 → authenticated; calls setCsrfToken(data.csrf_token) so write requests work.
 * - 401 → unauthenticated (not an error toast; normal state before login).
 * - Exposes { user, status } to descendants via useSession().
 *
 * Also registers the 401 → /login redirect with the API client middleware.
 */

import { createContext, useContext, useEffect, type ReactNode } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useNavigate } from 'react-router-dom'
import apiClient, { registerLoginRedirect } from '../api/client'
import { setCsrfToken } from '../api/csrf'
import type { components } from '../api/schema.d.ts'

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

type SessionUser = components['schemas']['SessionUser']

type SessionStatus = 'loading' | 'authenticated' | 'unauthenticated'

interface SessionContextValue {
  user: SessionUser | null
  status: SessionStatus
}

// ---------------------------------------------------------------------------
// Context
// ---------------------------------------------------------------------------

const SessionContext = createContext<SessionContextValue>({
  user: null,
  status: 'loading',
})

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

/** Access the current session from any descendant component. */
export function useSession(): SessionContextValue {
  return useContext(SessionContext)
}

// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------

interface SessionProviderProps {
  children: ReactNode
}

export function SessionProvider({ children }: SessionProviderProps) {
  const navigate = useNavigate()
  const queryClient = useQueryClient()

  // Register the 401 redirect callback with the API client once.
  useEffect(() => {
    registerLoginRedirect(() => {
      // Invalidate the session query so any subscriber re-fetches (→ unauthenticated).
      queryClient.invalidateQueries({ queryKey: ['session'] })
      navigate('/login', { replace: true })
    })
  }, [navigate, queryClient])

  const { data, status, error } = useQuery({
    queryKey: ['session'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/session')
      // openapi-fetch returns { data, error, response }.
      // On 401 the middleware already navigates; here data will be undefined.
      return res.data ?? null
    },
    // Don't treat 401 as a React Query "error" — it's a normal unauthenticated state.
    retry: false,
    staleTime: 1000 * 60 * 5, // 5 minutes
  })

  // When we get session data, store the CSRF token.
  useEffect(() => {
    if (data?.csrf_token) {
      setCsrfToken(data.csrf_token)
    }
  }, [data])

  let sessionStatus: SessionStatus
  if (status === 'pending') {
    sessionStatus = 'loading'
  } else if (status === 'error' || data === null || !data) {
    // 401 returns null from our queryFn; any actual network error → unauthenticated.
    sessionStatus = 'unauthenticated'
    // Suppress unused variable warning for error in non-401 cases
    void error
  } else {
    sessionStatus = 'authenticated'
  }

  const value: SessionContextValue = {
    user: data?.user ?? null,
    status: sessionStatus,
  }

  return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>
}
