import { BrowserRouter, Navigate, Outlet, Route, Routes, useLocation } from "react-router-dom"

import { Header } from "./pages/template/Header.tsx"
import "./style/app.css"
import { createContext, lazy, ReactNode, Suspense, useCallback, useContext, useEffect, useMemo, useState } from "react"
import { BASE } from "./Constants.ts"
import { Authentication, Fetcher } from "./app/Fetcher.ts"
import { User } from "./model/User.ts"

const HomePage = lazy(() => import("./pages/HomePage.tsx"))
const LoginPage = lazy(() => import("./pages/LoginPage.tsx"))
const RegisterPage = lazy(() => import("./pages/RegisterPage.tsx"))
const NotFoundPage = lazy(() => import("./pages/404.tsx"))
const CreateTeamPage = lazy(() => import("./pages/CreateTeamPage.tsx"))
const TeamPanelPage = lazy(() => import("./pages/TeamPanel.tsx"))
const NewTacticPage = lazy(() => import("./pages/NewTacticPage.tsx"))
const Editor = lazy(() => import("./pages/Editor.tsx"))
const Settings = lazy(() => import("./pages/Settings.tsx"))

const TOKEN_REFRESH_INTERVAL_MS = 60 * 1000

export default function App() {
    function suspense(node: ReactNode) {
        return (
            <Suspense fallback={<p>Loading, please wait...</p>}>
                {node}
            </Suspense>
        )
    }

    const storedAuth = useMemo(() => getStoredAuthentication(), [])
    const fetcher = useMemo(() => new Fetcher(storedAuth), [storedAuth])
    const [user, setUser] = useState<User | null>(null)

    const handleAuthSuccess = useCallback(async (auth: Authentication) => {
        fetcher.updateAuthentication(auth)
        const user = await fetchUser(fetcher)
        setUser(user)
        storeAuthentication(auth)
    }, [fetcher])


    useEffect(() => {
        const interval = setInterval(() => {
            fetcher.fetchAPIGet("auth/keep-alive")
            console.log("KEPT ALIVE !")
        }, TOKEN_REFRESH_INTERVAL_MS)

        return () => clearInterval(interval)
    }, [fetcher])

    return (
        <div id="app">
            <FetcherContext.Provider value={fetcher}>
                <SignedInUserContext.Provider value={{
                    user,
                    setUser,
                }}>
                    <BrowserRouter basename={BASE}>
                        <Outlet />

                        <Routes>
                            <Route
                                path={"/login"}
                                element={suspense(<LoginPage onSuccess={handleAuthSuccess} />)}
                            />
                            <Route
                                path={"/register"}
                                element={suspense(<RegisterPage onSuccess={handleAuthSuccess} />)}
                            />

                            <Route path={"/"} element={suspense(<AppLayout />)}>
                                <Route path={"/"} element={
                                    suspense(
                                        <LoggedInPage>
                                            <HomePage />
                                        </LoggedInPage>,
                                    )
                                }
                                />
                                <Route
                                    path={"/home"}
                                    element={
                                        suspense(
                                            <LoggedInPage>
                                                <HomePage />
                                            </LoggedInPage>,
                                        )
                                    }
                                />

                                <Route
                                    path={"/settings"}
                                    element={
                                        suspense(
                                            <LoggedInPage>
                                                <Settings />
                                            </LoggedInPage>,
                                        )
                                    }
                                />

                                <Route
                                    path={"/team/new"}
                                    element={
                                        suspense(<CreateTeamPage />)
                                    }
                                />
                                <Route
                                    path={"/team/:teamId"}
                                    element={
                                        suspense(
                                            <LoggedInPage>
                                                <TeamPanelPage />
                                            </LoggedInPage>,
                                        )
                                    }
                                />
                                <Route
                                    path={"/tactic/new"}
                                    element={
                                        suspense(
                                            <LoggedInPage>
                                                <NewTacticPage />
                                            </LoggedInPage>,
                                        )
                                    }
                                />
                                <Route
                                    path={"/tactic/:tacticId/edit"}
                                    element={
                                        suspense(
                                            <LoggedInPage>
                                                <Editor guestMode={false} />
                                            </LoggedInPage>,
                                        )
                                    }
                                />
                                <Route
                                    path={"/tactic/edit-guest"}
                                    element={suspense(<Editor guestMode={true} />)}
                                />

                                <Route
                                    path={"*"}
                                    element={suspense(<NotFoundPage />)}
                                />
                            </Route>
                        </Routes>
                    </BrowserRouter>
                </SignedInUserContext.Provider>
            </FetcherContext.Provider>
        </div>
    )
}

async function fetchUser(fetcher: Fetcher): Promise<User> {
    const response = await fetcher.fetchAPIGet("user")

    if (!response.ok) {
        throw Error("Could not retrieve user information : " + await response.text())
    }

    return await response.json()
}

const STORAGE_AUTH_KEY = "token"

function getStoredAuthentication(): Authentication {
    const storedUser = localStorage.getItem(STORAGE_AUTH_KEY)
    return storedUser == null ? null : JSON.parse(storedUser)
}

function storeAuthentication(auth: Authentication) {
    localStorage.setItem(STORAGE_AUTH_KEY, JSON.stringify(auth))
}

interface LoggedInPageProps {
    children: ReactNode
}

enum UserFetchingState {
    FETCHING,
    FETCHED,
    ERROR
}

function LoggedInPage({ children }: LoggedInPageProps) {
    const [user, setUser] = useUser()
    const fetcher = useAppFetcher()
    const [userFetchingState, setUserFetchingState] = useState(user === null ? UserFetchingState.FETCHING : UserFetchingState.FETCHED)
    const location = useLocation()

    useEffect(() => {
        async function initUser() {
            try {
                const user = await fetchUser(fetcher)
                setUser(user)
                setUserFetchingState(UserFetchingState.FETCHED)
            } catch (e) {
                setUserFetchingState(UserFetchingState.ERROR)
            }

        }

        if (userFetchingState === UserFetchingState.FETCHING)
            initUser()
    }, [fetcher, setUser, userFetchingState])

    switch (userFetchingState) {
        case UserFetchingState.ERROR:
            return <Navigate to={"/login"} replace state={{ from: location.pathname }} />
        case UserFetchingState.FETCHED:
            return children
        case UserFetchingState.FETCHING:
            return <p>Fetching user...</p>
    }
}

function AppLayout() {
    return (
        <>
            <Header />
            <Outlet />
        </>
    )
}

interface UserContext {
    user: User | null
    setUser: (user: User) => void
}

const SignedInUserContext = createContext<UserContext | null>(null)
const FetcherContext = createContext(new Fetcher())

export function useAppFetcher() {
    return useContext(FetcherContext)
}

export function useUser(): [User | null, (user: User) => void] {
    const { user, setUser } = useContext(SignedInUserContext)!
    return [user, setUser]
}


