import { join } from 'path'
import { Location } from 'history'
import { Children, cloneElement, memo, ReactElement, ReactNode, useCallback, useContext, useMemo } from 'react'

import { Redirect, Route, Switch, SwitchProps, useLocation } from 'react-router-dom'
import { isURLAvailable } from '@utils/permissions'
import { getUserId } from '@utils/storage'

import { RootPathContext } from '@pages/Main'

export type PrivateSwitchProps = SwitchProps & {
    root?: string
    redirect?: (location?: Location<unknown>) => ReactNode
    elseRedirect?: string | string[]
}

const getDefaultRedirectPath = () => getUserId() ? '/access_denied' : '/login'

export const defaultRedirect = (location?: Location<unknown>) => (
    <Redirect to={{ pathname: getDefaultRedirectPath(), state: { from: location?.pathname } }} />
)

export const PrivateSwitch = memo<PrivateSwitchProps>(({ root, elseRedirect, redirect = defaultRedirect, children }) => {
    const rootContext = useContext(RootPathContext)
    const rootPath = useMemo(() => root ?? rootContext ?? '', [root, rootContext])

    const location = useLocation()

    const toAbsolute = useCallback((path: string) => path.startsWith('/') ? path : join(rootPath, path), [rootPath])

    const items = useMemo(() => Children.toArray(children).map((child) => {
        const element = child as ReactElement
        let key = element.key?.toString()
        if (!key) return null
        key = key.slice(key.lastIndexOf('$') + 1).replaceAll('=2', ':')
        // Ключ автоматический преобразуется в "(.+)\$ключ"
        // Все ":" в ключе заменяются на "=2"
        // TODO: улучшить метод нормализации ключа
        const path = toAbsolute(key)
        return (
            <Route
                key={key}
                path={path}
                render={({ location }) => isURLAvailable(path) ? cloneElement(element) : redirect(location)}
            />
        )
    }), [children, redirect, toAbsolute])

    const defaultRoute = useMemo(() => {
        if (!elseRedirect) {
            const path = items.map((elm) => elm?.props.path).find((path) => path && isURLAvailable(path))
            if (path) return path
        } else if (Array.isArray(elseRedirect)) {
            const path = elseRedirect.find((path) => {
                if (!path) return false
                return isURLAvailable(toAbsolute(path))
            })
            if (path) return toAbsolute(path)
        } else if(elseRedirect && isURLAvailable(toAbsolute(elseRedirect))) {
            return toAbsolute(elseRedirect)
        }
        return getDefaultRedirectPath()
    }, [items, elseRedirect, toAbsolute])

    return (
        <Switch>
            {items}
            <Route path={'/'}>
                <Redirect to={{ pathname: defaultRoute, state: { from: location.pathname } }} />
            </Route>
        </Switch>
    )
})

export default PrivateSwitch
