import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
import { type FC } from 'react';

import type { MdxPage } from '../../types/mdxTypes';
import XIcon from '~icons/material-symbols/close';
import MenuIcon from '~icons/material-symbols/menu';

interface DocsMenuProps {
    docsPages: MdxPage[];
    currentPageUrl: string;
    title: string;
}

interface GroupedPages {
    groupedPages: Record<string, MdxPage[]>;
    indexPages: Record<string, MdxPage>;
}

const groupPagesByDirectory = (pages: MdxPage[]): GroupedPages => {
    const groupedPages: Record<string, MdxPage[]> = {};
    const indexPages: Record<string, MdxPage> = {};

    pages.forEach((page) => {
        const fileParts = page.file.split('/');
        const fileName = fileParts[fileParts.length - 1];
        const dir = fileParts[fileParts.length - 2];

        if (fileName === 'index.mdx') {
            indexPages[dir] = page;
        } else {
            if (!(dir in groupedPages)) {
                groupedPages[dir] = [];
            }
            groupedPages[dir].push(page);
        }
    });

    // Sort pages within each directory
    Object.values(groupedPages).forEach((pages) => {
        pages.sort((a, b) => {
            const orderA = (a.frontmatter.order ?? Infinity) as number;
            const orderB = (b.frontmatter.order ?? Infinity) as number;
            return orderA !== orderB
                ? orderA - orderB
                : (a.frontmatter.title as string).localeCompare(b.frontmatter.title as string);
        });
    });

    return { groupedPages, indexPages };
};

const toTitleCase = (str: string): string => str.replace(/\b\w/g, (char) => char.toUpperCase());

interface MenuItemProps {
    page: MdxPage;
    currentPageUrl: string;
}

const MenuItem: FC<MenuItemProps> = ({ page, currentPageUrl }) => {
    const isActive = page.url === currentPageUrl;
    return (
        <li>
            <a
                href={page.url}
                className={`block py-1.5 pl-4 transition-colors duration-150 ease-in-out hover:text-primary-800 ${
                    isActive ? 'text-primary-700 font-medium underline' : 'text-primary-600'
                }`}
            >
                {(page.frontmatter.menuTitle ?? page.frontmatter.title) as string}
            </a>
        </li>
    );
};

interface MenuSectionProps {
    dir: string;
    pages: MdxPage[];
    indexPage?: MdxPage;
    currentPageUrl: string;
}

const MenuSection: FC<MenuSectionProps> = ({ dir, pages, indexPage, currentPageUrl }) => {
    const indexIsActive = indexPage?.url === currentPageUrl;
    const heading = indexPage ? (
        <a
            href={indexPage.url}
            className={`block font-semibold text-primary-700 hover:text-primary-900 ${
                indexIsActive ? 'underline' : ''
            }`}
        >
            {indexPage.frontmatter.title as string}
        </a>
    ) : (
        <span className='block font-semibold text-primary-700'>{toTitleCase(dir.replaceAll('-', ' '))}</span>
    );

    return (
        <li className='mb-4 last:mb-0'>
            <div className='mb-1'>{heading}</div>
            {pages.length > 0 && (
                <ul className='list-none m-0 p-0 border-l border-primary-200'>
                    {pages.map((page) => (
                        <MenuItem key={page.url} page={page} currentPageUrl={currentPageUrl} />
                    ))}
                </ul>
            )}
        </li>
    );
};

const DocsMenu: FC<DocsMenuProps> = ({ docsPages, currentPageUrl, title }) => {
    const { groupedPages, indexPages } = groupPagesByDirectory(docsPages);

    // Sort directories based on index page order
    const sortedDirectories = Object.keys(groupedPages).sort((a, b) => {
        const orderA = ((a in indexPages ? indexPages[a].frontmatter.order : undefined) ?? Infinity) as number;
        const orderB = ((b in indexPages ? indexPages[b].frontmatter.order : undefined) ?? Infinity) as number;
        return orderA - orderB;
    });

    const menuContent = (
        <ul className='list-none m-0 p-0'>
            {sortedDirectories.map((dir) => (
                <MenuSection
                    key={dir}
                    dir={dir}
                    pages={groupedPages[dir]}
                    indexPage={indexPages[dir]}
                    currentPageUrl={currentPageUrl}
                />
            ))}
        </ul>
    );

    return (
        <Disclosure as='nav' className='docs-menu mt-5 sticky sm:min-w-64'>
            {({ open }) => (
                <>
                    <div className='flex items-center justify-between sm:block'>
                        <div className='text-2xl font-bold text-primary-800 mb-3'>{title}</div>
                        <div className='sm:hidden'>
                            <DisclosureButton className='text-gray-700 hover:text-primary-700 focus:outline-hidden'>
                                {open ? (
                                    <XIcon className='w-6 h-6' aria-hidden='true' />
                                ) : (
                                    <MenuIcon className='w-6 h-6' aria-hidden='true' />
                                )}
                            </DisclosureButton>
                        </div>
                    </div>

                    <DisclosurePanel className='md:hidden'>{menuContent}</DisclosurePanel>

                    <div className='hidden md:block md:w-64'>{menuContent}</div>
                </>
            )}
        </Disclosure>
    );
};

export default DocsMenu;
