// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import clsx from 'clsx';
import { getAnalyticsMetadataAttribute } from '@cloudscape-design/component-toolkit/internal/analytics-metadata';
import InternalBox from '../box/internal';
import InternalExpandableSection from '../expandable-section/internal';
import { useInternalI18n } from '../i18n/context';
import InternalIcon from '../icon/internal';
import { isPlainLeftClick } from '../internal/events';
import { useVisualRefresh } from '../internal/hooks/use-visual-mode';
import { checkSafeUrl } from '../internal/utils/check-safe-url';
import Tooltip from '../tooltip/internal';
import { hasActiveLink } from './util';
import analyticsSelectors from './analytics-metadata/styles.css.js';
import styles from './styles.css.js';
import testUtilStyles from './test-classes/styles.css.js';
export function Header({ definition, activeHref, fireFollow, collapsed }) {
    checkSafeUrl('SideNavigation', definition.href);
    const onClick = useCallback((event) => {
        if (isPlainLeftClick(event)) {
            fireFollow(definition, event);
        }
    }, [fireFollow, definition]);
    const clickActionAnalyticsMetadata = {
        action: 'click',
        detail: {
            label: `.${analyticsSelectors['header-link-text']}`,
            external: 'false',
            href: definition.href,
            position: 'header',
        },
    };
    return (React.createElement(React.Fragment, null,
        React.createElement("h2", { className: clsx(styles.header, collapsed && styles['header--collapsed']) },
            React.createElement("a", { href: definition.href, className: clsx(styles['header-link'], { [styles['header-link--has-logo']]: !!definition.logo }), "aria-current": definition.href === activeHref ? 'page' : undefined, "aria-label": collapsed ? definition.text : undefined, onClick: onClick, ...getAnalyticsMetadataAttribute(clickActionAnalyticsMetadata) },
                definition.logo &&
                    (definition.logo.svg ? (React.createElement("span", { className: clsx(styles['header-logo'], {
                            [styles['header-logo--stretched']]: !definition.text || collapsed,
                        }) }, definition.logo.svg)) : (React.createElement("img", { className: clsx(styles['header-logo'], {
                            [styles['header-logo--stretched']]: !definition.text || collapsed,
                        }), src: definition.logo.src, alt: definition.logo.alt }))),
                !collapsed && (React.createElement("span", { className: clsx(styles['header-link-text'], analyticsSelectors['header-link-text']) }, definition.text)))),
        React.createElement(Divider, { isPresentational: true, variant: "header" })));
}
export function NavigationItemsList({ items, variant, activeHref, fireChange, fireFollow, position = '', collapsed, }) {
    const lists = [];
    let currentListIndex = 0;
    lists[currentListIndex] = {
        listVariant: variant,
        items: [],
    };
    items.forEach((item, index) => {
        var _a, _b, _c, _d, _e;
        const itemid = index + 1;
        const itemPosition = `${position ? `${position},` : ''}${itemid}`;
        // Emits a divider as its own list segment (dividers break the <ul> grouping).
        function pushDivider() {
            lists.push({
                listVariant: variant,
                element: (React.createElement("div", { "data-itemid": `item-${itemid}` },
                    React.createElement(Divider, { variant: "default", collapsed: collapsed }))),
            });
            currentListIndex =
                lists.push({
                    listVariant: variant,
                    items: [],
                }) - 1;
        }
        // Renders icon-bearing children of a container item as a collapsed group.
        // The inner <ul> carries the group label so list semantics are preserved
        // for screen readers even when the visual header is hidden.
        function pushCollapsedGroup(children, label, { leadingDivider = false } = {}) {
            var _a;
            const iconChildren = children.filter(child => child.icon);
            if (iconChildren.length === 0) {
                return;
            }
            // A section's title is hidden when collapsed; render a divider in its place
            if (leadingDivider) {
                pushDivider();
            }
            const groupElements = iconChildren.map((child, childIndex) => {
                const childPosition = `${position ? `${position},` : ''}${itemid},${childIndex + 1}`;
                return (React.createElement("li", { key: childPosition, className: clsx(styles['list-item'], styles['list-item--collapsed']) },
                    React.createElement(Link, { definition: child, activeHref: activeHref, fireFollow: fireFollow, fireChange: fireChange, position: childPosition, collapsed: collapsed })));
            });
            const prevItem = index > 0 ? items[index - 1] : null;
            const nextItem = index < items.length - 1 ? items[index + 1] : null;
            (_a = lists[currentListIndex].items) === null || _a === void 0 ? void 0 : _a.push({
                element: (React.createElement("li", { key: `group-${itemid}`, className: clsx(styles['list-item--group'], (leadingDivider || (prevItem === null || prevItem === void 0 ? void 0 : prevItem.type) === 'divider') && styles['list-item--group-no-padding-start'], (nextItem === null || nextItem === void 0 ? void 0 : nextItem.type) === 'divider' && styles['list-item--group-no-padding-end']) },
                    React.createElement("ul", { className: styles['list--collapsed-group'], "aria-label": label }, groupElements))),
            });
        }
        // In collapsed mode, flatten container items to show only icon-bearing children.
        if (collapsed && (item.type === 'expandable-link-group' || item.type === 'link-group') && !item.icon) {
            const group = item;
            pushCollapsedGroup(group.items, group.text);
            return;
        }
        if (collapsed && (item.type === 'section' || item.type === 'section-group')) {
            const sectionLabel = item.type === 'section'
                ? item.text
                : item.title;
            // Section-groups may contain nested sections — flatten one level.
            const childItems = item.type === 'section'
                ? item.items
                : item.items.flatMap(child => child.type === 'section' ? child.items : [child]);
            pushCollapsedGroup(childItems, sectionLabel, { leadingDivider: true });
            return;
        }
        if (collapsed && item.type !== 'divider' && !item.icon) {
            return;
        }
        switch (item.type) {
            case 'divider': {
                pushDivider();
                return;
            }
            case 'link': {
                (_a = lists[currentListIndex].items) === null || _a === void 0 ? void 0 : _a.push({
                    element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: clsx(styles['list-item'], collapsed && styles['list-item--collapsed']) },
                        React.createElement(Link, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition, collapsed: collapsed }))),
                });
                return;
            }
            case 'section': {
                (_b = lists[currentListIndex].items) === null || _b === void 0 ? void 0 : _b.push({
                    element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: clsx(styles['list-item'], collapsed && styles['list-item--collapsed']) },
                        React.createElement(Section, { definition: item, activeHref: activeHref, variant: variant, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition, collapsed: collapsed }))),
                });
                return;
            }
            case 'section-group': {
                (_c = lists[currentListIndex].items) === null || _c === void 0 ? void 0 : _c.push({
                    element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: clsx(styles['list-item'], collapsed && styles['list-item--collapsed']) },
                        React.createElement(SectionGroup, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition, collapsed: collapsed }))),
                });
                return;
            }
            case 'link-group': {
                (_d = lists[currentListIndex].items) === null || _d === void 0 ? void 0 : _d.push({
                    element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: clsx(styles['list-item'], collapsed && styles['list-item--collapsed']) },
                        React.createElement(LinkGroup, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition, collapsed: collapsed }))),
                });
                return;
            }
            case 'expandable-link-group': {
                (_e = lists[currentListIndex].items) === null || _e === void 0 ? void 0 : _e.push({
                    element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: clsx(styles['list-item'], collapsed && styles['list-item--collapsed']) },
                        React.createElement(ExpandableLinkGroup, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, variant: variant, position: itemPosition, collapsed: collapsed }))),
                });
                return;
            }
        }
    });
    // In collapsed mode, skip empty item segments and deduplicate consecutive dividers.
    const filteredLists = collapsed
        ? lists.filter((list, index) => {
            if (list.items) {
                return list.items.length > 0;
            }
            // Divider — skip if preceded by another divider or empty segment, or followed by nothing.
            const prevVisible = lists
                .slice(0, index)
                .reverse()
                .find(l => !l.items || l.items.length > 0);
            const nextVisible = lists.slice(index + 1).find(l => !l.items || l.items.length > 0);
            return ((!prevVisible || (prevVisible.items !== undefined && prevVisible.items.length > 0)) &&
                nextVisible !== undefined &&
                (nextVisible.items === undefined || nextVisible.items.length > 0));
        })
        : lists;
    return (React.createElement(React.Fragment, null, filteredLists.map((list, index) => {
        if (!list.items || list.items.length === 0) {
            return (React.createElement("div", { key: `hr-${index}`, className: clsx(styles.list, styles[`list-variant-${variant}`], {
                    [styles['list-variant-root--first']]: list.listVariant === 'root' && index === 0,
                    [styles['list-variant-root--collapsed']]: list.listVariant === 'root' && collapsed,
                }) }, list.element));
        }
        else {
            return (React.createElement("ul", { key: `list-${index}`, className: clsx(styles.list, styles[`list-variant-${list.listVariant}`], {
                    [styles['list-variant-root--first']]: list.listVariant === 'root' && index === 0,
                    [styles['list-variant-root--collapsed']]: list.listVariant === 'root' && collapsed,
                }) }, list.items.map(item => item.element)));
        }
    })));
}
function Divider({ variant = 'default', isPresentational = false, collapsed }) {
    return (React.createElement("hr", { className: clsx(styles.divider, styles[`divider-${variant}`], collapsed && styles['divider--collapsed']), role: isPresentational ? 'presentation' : undefined }));
}
const ItemIcon = React.forwardRef(function ItemIcon({ icon, collapsed, className, ...rest }, ref) {
    if (!icon) {
        return null;
    }
    return (React.createElement("span", { ref: ref, className: clsx(styles['item-icon'], testUtilStyles['item-icon'], collapsed && styles['item-icon--collapsed'], className), ...rest }, icon));
});
// Manages a tooltip that shows the item's text label on focus or hover.
// Used in the collapsed state, where the visible labels are hidden, to give
// pointer and keyboard users a way to identify each item without relying on
// their browser's native title popup.
function useCollapsedTooltip(label) {
    const [show, setShow] = useState(false);
    const triggerRef = useRef(null);
    const triggerProps = {
        onFocus: () => setShow(true),
        onBlur: () => setShow(false),
        onMouseEnter: () => setShow(true),
        onMouseLeave: () => setShow(false),
    };
    const tooltip = show ? (React.createElement(Tooltip, { getTrack: () => triggerRef.current, content: label, position: "right", onEscape: () => setShow(false) })) : null;
    return { triggerRef, triggerProps, tooltip };
}
function Link({ definition, activeHref, fireFollow, position, collapsed }) {
    checkSafeUrl('SideNavigation', definition.href);
    const isActive = definition.href === activeHref;
    const i18n = useInternalI18n('link');
    const collapsedTooltip = useCollapsedTooltip(definition.text);
    const onClick = useCallback((event) => {
        if (isPlainLeftClick(event)) {
            fireFollow(definition, event);
        }
    }, [fireFollow, definition]);
    const clickActionAnalyticsMetadata = {
        action: 'click',
        detail: {
            label: `.${analyticsSelectors['link-text']}`,
            external: `${!!definition.external}`,
            href: definition.href,
            position,
        },
    };
    const renderedExternalIconAriaLabel = i18n('externalIconAriaLabel', definition.externalIconAriaLabel);
    const showInfo = !collapsed && definition.info;
    const internalLink = (React.createElement("a", { ref: collapsed ? collapsedTooltip.triggerRef : undefined, href: definition.href, className: clsx(styles.link, {
            [styles['link-active']]: isActive,
            [styles['link--collapsed']]: collapsed,
        }), target: definition.external ? '_blank' : undefined, rel: definition.external ? 'noopener noreferrer' : undefined, "aria-current": definition.href === activeHref ? 'page' : undefined, "aria-label": collapsed ? definition.text : undefined, onClick: onClick, ...(collapsed ? collapsedTooltip.triggerProps : {}), ...getAnalyticsMetadataAttribute(clickActionAnalyticsMetadata) },
        React.createElement(ItemIcon, { icon: definition.icon, collapsed: collapsed, "aria-hidden": collapsed ? true : undefined }),
        !collapsed && (React.createElement("span", { className: styles['link-text-wrapper'] },
            React.createElement("span", { className: analyticsSelectors['link-text'] }, definition.text),
            definition.external && (React.createElement("span", { "aria-label": renderedExternalIconAriaLabel, role: renderedExternalIconAriaLabel ? 'img' : undefined },
                React.createElement(InternalIcon, { name: "external", className: styles['external-icon'] }))))),
        collapsed && collapsedTooltip.tooltip));
    return showInfo ? (React.createElement("div", { className: styles['list-item--link-with-info'] },
        internalLink,
        React.createElement("span", { className: clsx(styles.info, testUtilStyles.info) }, definition.info))) : (internalLink);
}
function Section({ definition, activeHref, fireFollow, fireChange, variant, position, collapsed }) {
    var _a;
    const [expanded, setExpanded] = useState((_a = definition.defaultExpanded) !== null && _a !== void 0 ? _a : true);
    const isVisualRefresh = useVisualRefresh();
    const onExpandedChange = useCallback((e) => {
        fireChange(definition, e.detail.expanded);
        setExpanded(e.detail.expanded);
    }, [definition, fireChange]);
    useEffect(() => {
        var _a;
        setExpanded((_a = definition.defaultExpanded) !== null && _a !== void 0 ? _a : true);
    }, [definition]);
    if (collapsed) {
        return null;
    }
    const isInSectionGroup = variant === 'section-group';
    return (React.createElement(InternalExpandableSection, { variant: "footer", expanded: expanded, onChange: onExpandedChange, className: clsx(styles.section, isInSectionGroup && styles['section--no-ident'], isVisualRefresh && styles.refresh), headerText: definition.text },
        React.createElement(NavigationItemsList, { variant: "section", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
function SectionGroup({ definition, activeHref, fireFollow, fireChange, position, collapsed }) {
    if (collapsed) {
        return null;
    }
    return (React.createElement("div", { className: styles['section-group'] },
        React.createElement(InternalBox, { className: styles['section-group-title'], variant: "h3" }, definition.title),
        React.createElement(NavigationItemsList, { variant: "section-group", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
function LinkGroup({ definition, activeHref, fireFollow, fireChange, position, collapsed }) {
    checkSafeUrl('SideNavigation', definition.href);
    return (React.createElement(React.Fragment, null,
        React.createElement(Link, { definition: {
                type: 'link',
                href: definition.href,
                text: definition.text,
                info: definition.info,
                icon: definition.icon,
            }, fireFollow: (_, event) => fireFollow(definition, event), fireChange: fireChange, activeHref: activeHref, position: position, collapsed: collapsed }),
        !collapsed && (React.createElement(NavigationItemsList, { variant: "link-group", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position }))));
}
function ExpandableLinkGroup({ definition, fireFollow, fireChange, activeHref, variant, position, collapsed, }) {
    // Check whether the definition contains an active link and memoize it to avoid
    // rechecking every time.
    const containsActiveLink = useMemo(() => {
        return activeHref ? hasActiveLink(definition.items, activeHref) : false;
    }, [activeHref, definition.items]);
    const [expanded, setExpanded] = useState(() => {
        var _a;
        return (_a = definition.defaultExpanded) !== null && _a !== void 0 ? _a : (definition.href === activeHref || containsActiveLink);
    });
    const [userExpanded, setUserExpanded] = useState();
    // Reset user expansion status when the items property is updated.
    useEffect(() => setUserExpanded(undefined), [definition]);
    // By default, the expandable section is open when there's an active link inside.
    useEffect(() => {
        setExpanded(definition.href === activeHref || containsActiveLink);
    }, [definition.href, containsActiveLink, activeHref]);
    // If the definition object itself is updated, reset the expansion state to default.
    useEffect(() => {
        if (definition.defaultExpanded !== undefined) {
            setExpanded(definition.defaultExpanded);
        }
    }, [definition]);
    const onExpandedChange = useCallback((e) => {
        fireChange(definition, e.detail.expanded);
        setUserExpanded(e.detail.expanded);
    }, [definition, fireChange]);
    const onHeaderFollow = (_, event) => {
        fireFollow(definition, event);
        setUserExpanded(true);
        if (!expanded) {
            fireChange(definition, true);
        }
    };
    if (collapsed) {
        return (React.createElement(Link, { definition: { type: 'link', href: definition.href, text: definition.text, icon: definition.icon }, fireFollow: onHeaderFollow, fireChange: fireChange, activeHref: activeHref, position: position, collapsed: collapsed }));
    }
    return (React.createElement(InternalExpandableSection, { className: clsx(styles['expandable-link-group'], variant === 'section-group' && styles['expandable-link-group--no-ident'], definition.href === activeHref && styles['expandable-link-group--active']), variant: "navigation", expanded: userExpanded !== null && userExpanded !== void 0 ? userExpanded : expanded, onChange: onExpandedChange, headerText: React.createElement(Link, { definition: { type: 'link', href: definition.href, text: definition.text, icon: definition.icon }, fireFollow: onHeaderFollow, fireChange: fireChange, activeHref: activeHref, position: position }) },
        React.createElement(NavigationItemsList, { variant: "expandable-link-group", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
//# sourceMappingURL=parts.js.map