import { useCallback, useEffect, useRef, useState } from 'react';
import type { DocSection } from '@/docs/types';
import { cn } from '@/lib/utils';

type Props = {
    sections: DocSection[];
    activeId: string;
};

export function DocsToc({ sections, activeId }: Props) {
    const navRef = useRef<HTMLElement>(null);
    const containerRef = useRef<HTMLDivElement>(null);
    const itemRefs = useRef<Map<string, HTMLAnchorElement>>(new Map());
    const [indicatorStyle, setIndicatorStyle] = useState<{
        top: number;
        height: number;
    } | null>(null);

    const updateIndicator = useCallback(() => {
        const container = containerRef.current;
        const activeLink = itemRefs.current.get(activeId);

        if (!container || !activeLink) {
            return;
        }

        const containerRect = container.getBoundingClientRect();
        const linkRect = activeLink.getBoundingClientRect();

        setIndicatorStyle({
            top: linkRect.top - containerRect.top,
            height: linkRect.height,
        });
    }, [activeId]);

    useEffect(() => {
        updateIndicator();

        const activeLink = itemRefs.current.get(activeId);
        const nav = navRef.current;

        if (!activeLink || !nav) {
            return;
        }

        const navRect = nav.getBoundingClientRect();
        const linkRect = activeLink.getBoundingClientRect();

        if (linkRect.top < navRect.top) {
            nav.scrollBy({
                top: linkRect.top - navRect.top - 8,
                behavior: 'smooth',
            });
        } else if (linkRect.bottom > navRect.bottom) {
            nav.scrollBy({
                top: linkRect.bottom - navRect.bottom + 8,
                behavior: 'smooth',
            });
        }
    }, [updateIndicator, activeId, sections]);

    useEffect(() => {
        const nav = navRef.current;

        if (!nav) {
            return;
        }

        nav.addEventListener('scroll', updateIndicator, { passive: true });
        window.addEventListener('resize', updateIndicator, { passive: true });

        return () => {
            nav.removeEventListener('scroll', updateIndicator);
            window.removeEventListener('resize', updateIndicator);
        };
    }, [updateIndicator]);

    const handleClick = (
        event: React.MouseEvent<HTMLAnchorElement>,
        sectionId: string,
    ): void => {
        event.preventDefault();

        const target = document.getElementById(sectionId);

        if (!target) {
            return;
        }

        target.scrollIntoView({ behavior: 'smooth', block: 'start' });
        window.history.replaceState(null, '', `#${sectionId}`);
    };

    if (sections.length === 0) {
        return null;
    }

    return (
        <aside className="hidden w-52 shrink-0 self-start xl:block">
            <nav
                ref={navRef}
                className="sticky top-20 max-h-[calc(100vh-6rem)] overflow-y-auto overscroll-contain pr-1"
                aria-label="On this page"
            >
                <p className="mb-3 text-xs font-semibold tracking-wide text-muted-foreground uppercase">
                    On this page
                </p>
                <div ref={containerRef} className="relative">
                    {indicatorStyle ? (
                        <span
                            aria-hidden
                            className="absolute left-0 w-0.5 rounded-full bg-primary transition-all duration-300 ease-out"
                            style={{
                                top: indicatorStyle.top,
                                height: indicatorStyle.height,
                            }}
                        />
                    ) : null}
                    <ul className="relative space-y-1 border-l border-border pl-3">
                        {sections.map((section) => (
                            <li key={section.id}>
                                <a
                                    ref={(element) => {
                                        if (element) {
                                            itemRefs.current.set(
                                                section.id,
                                                element,
                                            );
                                        } else {
                                            itemRefs.current.delete(
                                                section.id,
                                            );
                                        }
                                    }}
                                    href={`#${section.id}`}
                                    onClick={(event) =>
                                        handleClick(event, section.id)
                                    }
                                    className={cn(
                                        'block rounded-r-md py-1.5 pr-2 text-sm leading-snug transition-colors hover:text-foreground',
                                        activeId === section.id
                                            ? 'font-medium text-primary'
                                            : 'text-muted-foreground',
                                    )}
                                >
                                    {section.heading}
                                </a>
                            </li>
                        ))}
                    </ul>
                </div>
            </nav>
        </aside>
    );
}
