import {
  createContext,
  useContext,
  useEffect,
  type ReactNode,
} from 'react'
import { useNavigate, useRouterState } from '@tanstack/react-router'
import { Center, Loader } from '@pikku/mantine/core'
import { usePikkuQuery } from '@germantax/functions-sdk/pikku/api.gen'

export interface Session {
  userId: string
  email: string
  displayName: string
}

interface SessionContextValue {
  session: Session
  refetch: () => void
}

const SessionContext = createContext<SessionContextValue | null>(null)

const PUBLIC_ROUTES = new Set<string>(['/login', '/logout'])

const isPublicRoute = (pathname: string) => PUBLIC_ROUTES.has(pathname)

export function SessionProvider({ children }: { children: ReactNode }) {
  const navigate = useNavigate()
  const pathname = useRouterState({ select: (s) => s.location.pathname })
  const onPublicRoute = isPublicRoute(pathname)

  const me = usePikkuQuery('getMe', undefined as never, {
    enabled: !onPublicRoute,
    retry: false,
    staleTime: 60_000,
  })

  useEffect(() => {
    if (onPublicRoute) return
    if (me.error) {
      navigate({
        to: '/login',
        search: { redirect: pathname } as never,
        replace: true,
      })
    }
  }, [me.error, onPublicRoute, navigate, pathname])

  if (onPublicRoute) {
    return <>{children}</>
  }

  if (me.isLoading || me.error || !me.data) {
    return (
      <Center mih="100vh">
        <Loader size="sm" />
      </Center>
    )
  }

  return (
    <SessionContext.Provider
      value={{ session: me.data, refetch: () => me.refetch() }}
    >
      {children}
    </SessionContext.Provider>
  )
}

export function useSession(): Session {
  const ctx = useContext(SessionContext)
  if (!ctx) {
    throw new Error('useSession must be used inside <SessionProvider>')
  }
  return ctx.session
}

export function useOptionalSession(): Session | null {
  const ctx = useContext(SessionContext)
  return ctx?.session ?? null
}

export function useSessionRefetch(): () => void {
  const ctx = useContext(SessionContext)
  return ctx?.refetch ?? (() => {})
}
