import { useCallback, useEffect, useMemo } from 'react'
import { Outlet, useLocation, useMatchRoute } from '@tanstack/react-router'
import { useQueryClient } from '@tanstack/react-query'
import { getTelegramWebApp } from '@/hooks/useTelegram'
import { initializeTheme } from '@/hooks/useTheme'
import { useAuth } from '@/hooks/useAuth'
import { useAuthSource } from '@/hooks/useAuthSource'
import { useSSE } from '@/hooks/useSSE'
import { queryKeys } from '@/lib/query-keys'
import { AppContextProvider } from '@/lib/app-context'
import { useAppGoBack } from '@/hooks/useAppGoBack'
import { LoginPrompt } from '@/components/LoginPrompt'
import { InstallPrompt } from '@/components/InstallPrompt'
import { OfflineBanner } from '@/components/OfflineBanner'

export function App() {
    const { authSource, isLoading: isAuthSourceLoading, setAccessToken } = useAuthSource()
    const { token, api, isLoading: isAuthLoading, error: authError } = useAuth(authSource)
    const goBack = useAppGoBack()
    const pathname = useLocation({ select: (location) => location.pathname })
    const matchRoute = useMatchRoute()

    useEffect(() => {
        const tg = getTelegramWebApp()
        tg?.ready()
        tg?.expand()
        initializeTheme()
    }, [])

    useEffect(() => {
        const preventDefault = (event: Event) => {
            event.preventDefault()
        }

        const onWheel = (event: WheelEvent) => {
            if (event.ctrlKey) {
                event.preventDefault()
            }
        }

        const onKeyDown = (event: KeyboardEvent) => {
            const modifier = event.ctrlKey || event.metaKey
            if (!modifier) return
            if (event.key === '+' || event.key === '-' || event.key === '=' || event.key === '0') {
                event.preventDefault()
            }
        }

        document.addEventListener('gesturestart', preventDefault as EventListener, { passive: false })
        document.addEventListener('gesturechange', preventDefault as EventListener, { passive: false })
        document.addEventListener('gestureend', preventDefault as EventListener, { passive: false })

        window.addEventListener('wheel', onWheel, { passive: false })
        window.addEventListener('keydown', onKeyDown)

        return () => {
            document.removeEventListener('gesturestart', preventDefault as EventListener)
            document.removeEventListener('gesturechange', preventDefault as EventListener)
            document.removeEventListener('gestureend', preventDefault as EventListener)

            window.removeEventListener('wheel', onWheel)
            window.removeEventListener('keydown', onKeyDown)
        }
    }, [])

    useEffect(() => {
        const tg = getTelegramWebApp()
        const backButton = tg?.BackButton
        if (!backButton) return

        if (pathname === '/' || pathname === '/sessions') {
            backButton.offClick(goBack)
            backButton.hide()
            return
        }

        backButton.show()
        backButton.onClick(goBack)
        return () => {
            backButton.offClick(goBack)
            backButton.hide()
        }
    }, [goBack, pathname])
    const queryClient = useQueryClient()
    const sessionMatch = matchRoute({ to: '/sessions/$sessionId' })
    const spawnMatch = matchRoute({ to: '/machines/$machineId/spawn' })
    const selectedSessionId = sessionMatch ? sessionMatch.sessionId : null
    const spawnMachineId = spawnMatch ? spawnMatch.machineId : null

    const handleSseConnect = useCallback(() => {
        void queryClient.invalidateQueries({ queryKey: queryKeys.sessions })
        if (selectedSessionId) {
            void queryClient.invalidateQueries({ queryKey: queryKeys.session(selectedSessionId) })
            void queryClient.invalidateQueries({ queryKey: queryKeys.messages(selectedSessionId) })
        }
    }, [queryClient, selectedSessionId])

    const handleSseEvent = useCallback(() => {}, [])

    const eventSubscription = useMemo(() => {
        if (selectedSessionId) {
            return { sessionId: selectedSessionId }
        }
        if (spawnMachineId) {
            return { machineId: spawnMachineId }
        }
        return { all: true }
    }, [selectedSessionId, spawnMachineId])

    useSSE({
        enabled: Boolean(api && token),
        token: token ?? '',
        subscription: eventSubscription,
        onConnect: handleSseConnect,
        onEvent: handleSseEvent,
    })

    // Loading auth source
    if (isAuthSourceLoading) {
        return (
            <div className="p-4">
                <div className="text-sm text-[var(--app-hint)]">Loading…</div>
            </div>
        )
    }

    // No auth source (browser environment, not logged in)
    if (!authSource) {
        return <LoginPrompt onLogin={setAccessToken} />
    }

    // Authenticating
    if (isAuthLoading) {
        return (
            <div className="p-4">
                <div className="text-sm text-[var(--app-hint)]">Authorizing…</div>
            </div>
        )
    }

    // Auth error
    if (authError || !token || !api) {
        // If using access token and auth failed, show login again
        if (authSource.type === 'accessToken') {
            return (
                <LoginPrompt
                    onLogin={setAccessToken}
                    error={authError ?? 'Authentication failed'}
                />
            )
        }

        // Telegram auth failed
        return (
            <div className="p-4 space-y-3">
                <div className="text-base font-semibold">Hapi</div>
                <div className="text-sm text-red-600">
                    {authError ?? 'Not authorized'}
                </div>
                <div className="text-xs text-[var(--app-hint)]">
                    Open this page from Telegram using the bot's "Open App" button (not "Open in browser").
                </div>
            </div>
        )
    }

    return (
        <AppContextProvider value={{ api, token }}>
            <OfflineBanner />
            <div className="h-full flex flex-col">
                <Outlet />
            </div>
            <InstallPrompt />
        </AppContextProvider>
    )
}
