import { StateUpdater, useContext, useEffect, useState } from 'preact/hooks'

import { createContext } from 'preact'
import { prependBaseUrl, server } from './api'
import { User } from '../shared/model'

type Metadata = {
    title?: string
    description?: string
}

export const MetadataContext = createContext<Metadata>({})

export const ServerContext = createContext<boolean>(false)
export const ClientContext = createContext<boolean>(false)

type CurrentUserHook = (onLoaded?: (user: User | null) => void) => [User | null, () => Promise<void>]

export const useCurrentUser: CurrentUserHook = onLoaded => {
    const [user, setUser] = useState(null)

    const logout = async () => {
        await server.post('/api/logout')
        setUser(null)
    }

    useEffect(() => {
        server.get('/api/current-user').then(user => {
            setUser(user)
            onLoaded?.(user)
        })
    }, [])

    return [user, logout]
}

type RefreshFunction = () => AbortController
type HeuristicStateUpdater<S> = StateUpdater<S>

type ResourceHookFunction = <T>(
    url: string | (() => string),
    initialValue: T
) => [T, RefreshFunction, HeuristicStateUpdater<T>]

export const useResource: ResourceHookFunction = (url, initialValue) => {
    const [value, setValue] = useState(initialValue)

    function refresh() {
        const controller = new AbortController()
        const realUrl = typeof url === 'function' ? url() : url

        fetch(prependBaseUrl(realUrl), { signal: controller.signal })
            .then(res => {
                if (res.ok) {
                    return res.json()
                } else {
                    return initialValue
                }
            })
            .then(newValue => {
                setValue(newValue)
            })

        return controller
    }

    useEffect(() => {
        const controller = refresh()
        return () => {
            controller.abort()
        }
    }, [])

    return [value, refresh, setValue]
}

type HeuristicListItemUpdater<S> = (index: number, value: S | ((prevValue: S) => S)) => void

export const useListResource = <T,>(
    url: string | (() => string)
): [T[], RefreshFunction, HeuristicListItemUpdater<T>, HeuristicStateUpdater<T[]>] => {
    const [list, refreshList, setListHeuristic] = useResource<T[]>(url, [])

    const setItemHeuristic: HeuristicListItemUpdater<T> = (index, newValue) => {
        setListHeuristic(list => {
            const newList = [...list]
            // @ts-ignore
            newList[index] = typeof newValue === 'function' ? newValue(list[index]) : newValue
            return newList
        })
    }

    return [list, refreshList, setItemHeuristic, setListHeuristic]
}
