import React from 'react'
import ProductSwitcherList from './parts/List'
import ProductSwitcherListItem from './parts/ListItem'
import ProductSwitcherLoading from './parts/Loading'
import { Tools } from '@planview/pv-icons'
import { defineMessages, useIntl } from 'react-intl'
import styled from 'styled-components'
import { align, sizePx, useClickOutside } from '@planview/pv-utilities'
import { ToolbarButtonEmptyInverse } from '../button'
import type { Product, ProductSwitcherOrganization } from './types'
import { Floater } from './parts/floater'
import { useMainNavigationContext } from '../utils/context'
import { ButtonEmptyInverse, ListGroup, Tooltip } from '@planview/pv-uikit'

export type ProductSwitcherProps = {
    /**
     * This is prop is intended for use in Dovetail only.
     * When the product-switcher is used outside of MainNavigation context using a bridge to hook it up with the navigation this flag should be set to `true`.
     */
    singleTabStop?: boolean
    /**
     * Expects an array of applications the user has access to.
     * If there are not at least two, the icon will not render.
     */
    products: Product[]
    /**
     * If provided, will render co-branding for an organization
     *
     * `ProductSwitcherOrganization` has this shape:
     *
     * ```ts
     * {
     *     name: string
     *     domain: string
     *     avatar: {
     *         light: string | React.ReactElement<AvatarProps>
     *         dark: string | React.ReactElement<AvatarProps>
     *     }
     * }
     * ```
     *
     * Avatar values for light and dark can either be a URL or the [Avatar component](https://planview-ds.github.io/react-pvds/?path=/docs/pv-uikit-avatar--docs) from `@planview/pv-uikit`
     */
    organization?: ProductSwitcherOrganization
    /**
     * call-back on navigate in list
     */
    onActivate?: (product: Product) => void
}

const TriggerWrap = styled.div`
    ${align.center};
    height: ${sizePx.medium};
    width: ${sizePx.medium};
    min-height: ${sizePx.medium};
    min-width: ${sizePx.medium};
`

const messages = defineMessages({
    triggerLabel: {
        defaultMessage: 'Planview product switcher',
        description: 'Product switcher trigger label',
        id: 'pvds.toolbar.productSwitcherTriggerLabel',
    },
    groupHeader: {
        defaultMessage: 'Products',
        description: 'Product switcher group header',
        id: 'pvds.toolbar.productSwitcherGroupHeader',
    },
})

/**
 * This component, implementing the [Design System specs](https://design.planview.com/components/product-switcher/product-switcher-panel), is meant to be used when you want to
 * manually do API calls and render the Product switcher inside a `NavigationBar`, meaning you are **not** using the switcher published via dovetail.
 *
 * `import { ProductSwitcher }` from '@planview/pv-toolbar'
 */
export const ProductSwitcher = ({
    products,
    organization,
    onActivate = () => {},
    singleTabStop = false,
}: ProductSwitcherProps): React.ReactElement | null => {
    const triggerRef = React.useRef<HTMLButtonElement>(null)
    const listRef = React.useRef<HTMLDivElement>(null)
    const [active, setActive] = React.useState(false)
    const [currentFocused, setCurrentFocused] = React.useState(0)
    const intl = useIntl()
    const { wrappedInNavigation } = useMainNavigationContext()

    const triggerLabel = intl.formatMessage(messages.triggerLabel)

    const handleTriggerKeyDown: React.KeyboardEventHandler<HTMLButtonElement> =
        React.useCallback(
            (ev) => {
                if ((ev.key === 'ArrowDown' || ev.key === ' ') && !active) {
                    ev.preventDefault()
                    setActive(true)
                }
            },
            [active]
        )

    const onCloseHandler = React.useCallback(() => {
        setActive(false)
    }, [])

    const handleKeyDown: React.KeyboardEventHandler<HTMLUListElement> =
        React.useCallback(
            (ev) => {
                switch (ev.key) {
                    case 'ArrowDown':
                        ev.preventDefault()
                        setCurrentFocused(
                            Math.min(currentFocused + 1, products.length - 1)
                        )
                        break
                    case 'ArrowUp':
                        ev.preventDefault()
                        setCurrentFocused(Math.max(currentFocused - 1, 0))
                        break
                    case 'Home':
                        setCurrentFocused(0)
                        break
                    case 'End':
                        setCurrentFocused(products.length - 1)
                        break
                    case 'Escape':
                        setActive(false)
                        triggerRef.current && triggerRef.current.focus()
                        break
                    case 'Tab':
                        setActive(false)
                        break
                    default:
                        break
                }
            },
            [currentFocused, products]
        )

    const [planviewMe, productList] = products.reduce<[Product[], Product[]]>(
        (memo, prd) => {
            if (prd.name === 'planview_me' || prd.name === 'ensemble') {
                memo[0].push(prd)
            } else {
                memo[1].push(prd)
            }
            return memo
        },
        [[], []]
    )

    function renderProductSwitcherListItem(product: Product, ix: number) {
        return (
            <ProductSwitcherListItem
                key={product.name}
                app={product.name}
                label={product.label}
                secondaryLabel={product.secondaryLabel}
                url={product.url}
                active={product.active}
                onActivate={() => {
                    onActivate(product)
                    setActive(false)
                    setTimeout(() => {
                        triggerRef.current && triggerRef.current.focus()
                    }, 0)
                    if (product.url) {
                        window.open(
                            product.url,
                            '_blank',
                            'noopener,noreferrer'
                        )
                    }
                }}
                tabIndex={ix === currentFocused ? 0 : -1}
            />
        )
    }

    useClickOutside([triggerRef, listRef], onCloseHandler)

    const TriggerType = wrappedInNavigation
        ? ToolbarButtonEmptyInverse
        : ButtonEmptyInverse

    return (
        <>
            <TriggerWrap role="none" className="pvds-static">
                <Tooltip text={triggerLabel} show={!wrappedInNavigation}>
                    <TriggerType
                        ref={triggerRef}
                        icon={<Tools />}
                        tooltip={triggerLabel}
                        activated={active}
                        tabIndex={
                            wrappedInNavigation
                                ? undefined
                                : singleTabStop
                                  ? -1
                                  : 0
                        }
                        onClick={() => setActive(!active)}
                        onKeyDown={handleTriggerKeyDown}
                        aria-haspopup="menu"
                        aria-expanded={active}
                        aria-label={triggerLabel}
                        aria-controls="app-switcher-list"
                        aria-describedby={
                            active && products.length === 0
                                ? 'app-switcher-loader'
                                : undefined
                        }
                    />
                </Tooltip>
            </TriggerWrap>
            {active ? (
                <Floater anchor={triggerRef}>
                    <ProductSwitcherList
                        ref={listRef}
                        onClose={onCloseHandler}
                        onKeyDown={handleKeyDown}
                        organization={organization}
                    >
                        {products.length === 0 ? (
                            <ProductSwitcherLoading />
                        ) : (
                            <>
                                {planviewMe
                                    ?.sort((a, b) =>
                                        b.name.localeCompare(a.name)
                                    )
                                    .map((product, ix) =>
                                        renderProductSwitcherListItem(
                                            product,
                                            ix
                                        )
                                    )}
                                <ListGroup
                                    label={intl.formatMessage(
                                        messages.groupHeader
                                    )}
                                >
                                    {productList.map((product, ix) =>
                                        renderProductSwitcherListItem(
                                            product,
                                            ix + planviewMe.length
                                        )
                                    )}
                                </ListGroup>
                            </>
                        )}
                    </ProductSwitcherList>
                </Floater>
            ) : null}
        </>
    )
}
