'use client';

import { useEffect, useRef, useState } from 'react';

/**
 * 목차 아이템 인터페이스
 */
interface TocItem {
    id: string;
    text: string;
    level: number;
    number?: string; // [ADDED] 표시할 번호
}

/**
 * 나무위키 스타일의 사이드바 목차 컴포넌트
 */
interface TableOfContentsProps {
    content: string;
    variant?: 'wiki' | 'daily';
    isMobile?: boolean;
    onClose?: () => void;
    // NEW
    isEditing?: boolean;
    editContent?: string; // 편집 중인 마크다운 content (실시간 업데이트용)
}

/**
 * 나무위키 스타일의 사이드바 목차 컴포넌트
 * - Desktop: Sticky Sidebar
 * - Mobile: Drawer/Popover Content
 */
export default function TableOfContents({ content, variant = 'wiki', isMobile = false, onClose, isEditing = false, editContent }: TableOfContentsProps) {
    const [toc, setToc] = useState<TocItem[]>([]);
    const [activeId, setActiveId] = useState<string>('');
    const isClickedRef = useRef(false); // [MODIFIED] stale closure 방지를 위해 ref로 변경
    const tocRef = useRef<HTMLElement>(null);

    // [ADDED] activeId ref (stale closure 방지)
    const activeIdRef = useRef(activeId);
    useEffect(() => { activeIdRef.current = activeId; }, [activeId]);

    const isDaily = variant === 'daily';
    const activeTextColor = isDaily ? 'text-emerald-600' : 'text-indigo-600';
    const hoverTextColor = isDaily ? 'hover:text-emerald-400' : 'hover:text-indigo-400';
    const iconColor = isDaily ? 'text-emerald-500' : 'text-indigo-500';
    // [ADDED] 수정 3: 현재 위치 표시 강화를 위한 색상 변수
    const activeBorderColor = isDaily ? 'border-emerald-500' : 'border-indigo-500';
    const activeBgColor = isDaily ? 'bg-emerald-50' : 'bg-indigo-50';

    // [ADDED] 텍스트 정규화 함수 (edit 모드 heading 매칭 강화)
    const normalizeText = (s: string) => s.replace(/\s+/g, ' ').replace(/[^\w\sㄱ-ㅎㅏ-ㅣ가-힣]/g, '').trim();

    // [ADDED] TOC 스크롤 영역 분리를 위한 wheel 이벤트 핸들러 (이슈 4)
    const handleTocWheel = (e: React.WheelEvent) => {
        const el = tocRef.current;
        if (!el) return;
        const { scrollTop, scrollHeight, clientHeight } = el;
        const atTop = scrollTop === 0 && e.deltaY < 0;
        const atBottom = scrollTop + clientHeight >= scrollHeight && e.deltaY > 0;
        if (!atTop && !atBottom) {
            e.stopPropagation();
        }
    };

    // 콘텐츠에서 헤더 및 리스트 항목 추출
    useEffect(() => {
        // 편집 모드일 때는 editContent를 소스로 사용, 아니면 content 사용
        const source = isEditing ? (editContent ?? '') : content;
        const lines = source.split('\n');
        const items: TocItem[] = [];

        // 텍스트 정화 함수: 마크다운 링크, 위키링크 문법 제거 및 특수문자 정화
        const cleanText = (raw: string) => {
            return raw
                .replace(/\\/g, '') // 원화표시/백슬래시 제거 (Shift+Enter 잔재)
                .replace(/\[\[(.+?)\]\]/g, '$1') // [[위키링크]] -> 위키링크
                .replace(/\[(.+?)\]\(.+?\)/g, '$1') // [제목](링크) -> 제목
                .replace(/\*\*(.+?)\*\*/g, '$1') // **볼드** -> 볼드
                .replace(/\*(.+?)\*/g, '$1') // *이탤릭* -> 이탤릭
                .replace(/__(.+?)__/g, '$1') // __볼드__ -> 볼드
                .replace(/_(.+?)_/g, '$1') // _이탤릭_ -> 이탤릭
                .replace(/~~(.+?)~~/g, '$1') // ~~취소선~~ -> 취소선
                .trim();
        };

        const seenIds = new Map<string, number>();
        let inCodeBlock = false;

        lines.forEach((line, index) => {
            // 코드블록 감지
            if (line.trim().startsWith('```') || line.trim().startsWith('~~~')) {
                inCodeBlock = !inCodeBlock;
                return;
            }
            if (inCodeBlock) return;

            // 1. 헤더 추출 (# 제목)
            const headerMatch = line.match(/^(#{1,3})\s+(.+)$/);
            if (headerMatch) {
                const level = headerMatch[1].length;

                // [MODIFIED] 레벨 2까지만 표시 (H1, H2)
                if (level > 2) return;

                const text = cleanText(headerMatch[2]);
                const sanitized = text.toLowerCase().replace(/[^\wㄱ-ㅎㅏ-ㅣ가-힣]/g, '-');
                const base = `h-${level}-${sanitized}`;
                const count = seenIds.get(base) || 0;
                seenIds.set(base, count + 1);
                const id = count === 0 ? base : `${base}-${count}`;
                items.push({ id, text, level });
                return;
            }

            // 2. 리스트 항목 추출 (목차 기능 복구)
            // Indentation 기반 레벨 파악 (2 spaces or 1 tab = 1 level increase)
            const listMatch = line.match(/^(\s*)([-*+]|\d+\.)\s+(.+)$/);
            if (listMatch) {
                const indent = listMatch[1];
                const text = cleanText(listMatch[3]);

                // 들여쓰기 레벨 계산 (탭은 2스페이스로 취급)
                // const indentLevel = Math.floor(indent.replace(/\t/g, '  ').length / 2);
                // -> 0 spaces = level 1, 2 spaces = level 2
                // Markdown standard: usually 2 or 4 spaces. Let's assume 2 spaces for sensitivity.
                // Level 1: 0 indent
                // Level 2: 2 spaces
                // Level 3: 4 spaces
                const level = Math.floor(indent.replace(/\t/g, '  ').length / 2) + 1;

                if (level > 2) return; // 레벨 2까지만 표시 (사용자 요청: 3단계부터는 안 보여야 함)

                // ID 생성
                const sanitized = text.toLowerCase().replace(/[^\wㄱ-ㅎㅏ-ㅣ가-힣]/g, '-');
                const base = `list-${level}-${sanitized}`;
                const count = seenIds.get(base) || 0;
                seenIds.set(base, count + 1);
                const id = count === 0 ? base : `${base}-${count}`;
                items.push({ id, text, level });
            }
        });

        // [ADDED] 계층형 번호 생성 로직 (1, 1.1, 1.2 ...)
        const counters = [0, 0]; // Level 1, Level 2 카운터
        const numberedItems = items.map(item => {
            if (item.level === 1) {
                counters[0]++;
                counters[1] = 0; // 하위 레벨 초기화
                return { ...item, number: `${counters[0]}.` };
            } else if (item.level === 2) {
                counters[1]++;
                // H1이 없는 경우(문서 제목만 있고 본문은 H2로 시작) 0.1 대신 1. 로 표시
                if (counters[0] === 0) {
                    return { ...item, number: `${counters[1]}.` };
                }
                return { ...item, number: `${counters[0]}.${counters[1]}` }; // 마지막 점 제거
            }
            return item;
        });

        setToc(numberedItems);
    }, [content, isEditing, editContent]);

    // 뷰 모드: 스크롤 감지에 따른 현재 위치 하이라이트 (IntersectionObserver)
    useEffect(() => {
        if (isEditing) return; // 편집 모드에서는 IntersectionObserver 비활성화
        if (toc.length === 0) return;

        const visibleIds = new Set<string>();
        let observer: IntersectionObserver | null = null;
        let retryTimeoutId: NodeJS.Timeout | null = null;

        const createObserver = (rootElement: Element | null) => {
            observer = new IntersectionObserver(
                (entries) => {
                    // 클릭으로 이동 중이면 옵저버 무시
                    if (isClickedRef.current) return;

                    // Update visibility set
                    entries.forEach((entry) => {
                        if (entry.isIntersecting) {
                            visibleIds.add(entry.target.id);
                        } else {
                            visibleIds.delete(entry.target.id);
                        }
                    });

                    // Find the first visible item from the TOC list (Top-most priority)
                    let newActiveId = '';
                    for (const item of toc) {
                        if (visibleIds.has(item.id)) {
                            newActiveId = item.id;
                            break; // Stop at the first visible item
                        }
                    }

                    if (newActiveId) {
                        setActiveId(newActiveId);
                    }
                },
                {
                    root: rootElement, // null이면 뷰포트 사용
                    rootMargin: '-140px 0px -70% 0px',
                    threshold: 0
                }
            );

            toc.forEach((item) => {
                const el = document.getElementById(item.id);
                if (el) observer?.observe(el);
            });

            // 페이지 로드 시 즉시 활성화
            requestAnimationFrame(() => {
                const containerRect = rootElement?.getBoundingClientRect();

                for (const item of toc) {
                    const el = document.getElementById(item.id);
                    if (!el) continue;
                    const rect = el.getBoundingClientRect();
                    const topBound = containerRect ? containerRect.top + 140 : 140;
                    const bottomBound = containerRect ? containerRect.bottom : window.innerHeight;
                    if (rect.top >= topBound && rect.top < bottomBound) {
                        setActiveId(item.id);
                        break;
                    }
                }
            });
        };

        // [FIX] scroll-container가 렌더링될 때까지 잠시 대기 후 observer 재생성
        // SSR/Hydration 환경에서 document.getElementById가 null을 반환하는 문제 해결
        const scrollContainer = document.getElementById('scroll-container');

        // 즉시 observer 생성 (scroll-container가 없으면 뷰포트 사용)
        createObserver(scrollContainer);

        // scroll-container가 없으면 100ms 후 재시도 (실제 컨테이너가 렌더링되었을 수 있음)
        if (!scrollContainer) {
            retryTimeoutId = setTimeout(() => {
                const retryContainer = document.getElementById('scroll-container');
                if (retryContainer && observer) {
                    // 기존 observer 해제 후 새로 생성
                    observer.disconnect();
                    createObserver(retryContainer);
                }
            }, 100);
        }

        return () => {
            if (retryTimeoutId) clearTimeout(retryTimeoutId);
            if (observer) observer.disconnect();
        };
    }, [isEditing, toc]);

    // 편집 모드: 에디터 스크롤 이벤트 감지 → TOC 하이라이트
    // [MODIFIED] deps에서 activeId 제거 → activeIdRef 사용으로 stale closure 방지 (이슈 1)
    useEffect(() => {
        if (!isEditing || toc.length === 0) return;

        const scrollContainer = document.getElementById('scroll-container');
        if (!scrollContainer) return;

        let rafId: number;
        const handleScroll = () => {
            // 클릭으로 이동 중이면 스크롤 핸들러 무시
            if (isClickedRef.current) return;

            cancelAnimationFrame(rafId);
            rafId = requestAnimationFrame(() => {
                const editorEl = scrollContainer.querySelector('.ProseMirror');
                if (!editorEl) return;

                const headings = Array.from(editorEl.querySelectorAll('h1, h2'));
                if (headings.length === 0) return;

                const containerRect = scrollContainer.getBoundingClientRect();
                let activeHeading: Element | null = null;

                for (const h of headings) {
                    const rect = h.getBoundingClientRect();
                    // heading이 스크롤 컨테이너 상단 + 160px 이내에 있으면 활성
                    if (rect.top <= containerRect.top + 160) {
                        activeHeading = h;
                    }
                }

                if (activeHeading) {
                    const rawText = activeHeading.textContent?.trim() || '';
                    const level = parseInt((activeHeading as HTMLElement).tagName.charAt(1));
                    const normalizedHeading = normalizeText(rawText);
                    // [MODIFIED] normalizeText 적용 + fallback includes 매칭 (이슈 1)
                    let matchItem = toc.find(item =>
                        normalizeText(item.text) === normalizedHeading && item.level === level
                    );
                    // fallback: includes 방식
                    if (!matchItem) {
                        matchItem = toc.find(item =>
                            item.level === level &&
                            (normalizeText(item.text).includes(normalizedHeading) ||
                             normalizedHeading.includes(normalizeText(item.text)))
                        );
                    }
                    // [MODIFIED] activeId 대신 activeIdRef.current 사용
                    if (matchItem && matchItem.id !== activeIdRef.current) {
                        setActiveId(matchItem.id);
                    }
                }
            });
        };

        scrollContainer.addEventListener('scroll', handleScroll, { passive: true });
        return () => {
            cancelAnimationFrame(rafId);
            scrollContainer.removeEventListener('scroll', handleScroll);
        };
    }, [isEditing, toc]); // [MODIFIED] activeId 제거 (activeIdRef로 대체)

    // TOC 활성 항목 자동 스크롤 (본문 스크롤 시 활성 항목이 TOC 뷰포트 안에 보이도록)
    useEffect(() => {
        if (!activeId || !tocRef.current) return;
        const activeEl = tocRef.current.querySelector(`a[href="#${activeId}"]`) as HTMLElement | null;
        if (activeEl) activeEl.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
    }, [activeId]);

    // 편집 모드 클릭 핸들러: TOC 클릭 시 에디터 내 해당 heading으로 스크롤
    // [MODIFIED] scrollIntoView를 setTimeout(0)으로 래핑 (포커스 경합 회피, 이슈 2)
    const handleEditingClick = (item: TocItem) => {
        const scrollContainer = document.getElementById('scroll-container');
        const editorEl = scrollContainer?.querySelector('.ProseMirror');
        if (!editorEl) return;

        const headings = Array.from(editorEl.querySelectorAll('h1, h2'));
        // toc에서 같은 text+level 매칭하는 heading DOM 찾기
        for (const h of headings) {
            if (h.textContent?.trim() === item.text && h.tagName === `H${item.level}`) {
                // [MODIFIED] setTimeout(0)으로 래핑하여 포커스 경합 회피
                setTimeout(() => {
                    h.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }, 0);
                break;
            }
        }
    };

    // [ADDED] View 모드 클릭 핸들러 (이슈 2, 3)
    const handleViewClick = (item: TocItem, closeCallback?: () => void) => {
        isClickedRef.current = true;
        setActiveId(item.id);

        const element = document.getElementById(item.id);
        const scrollContainer = document.getElementById('scroll-container');
        if (element && scrollContainer) {
            // scrollend 이벤트로 isClickedRef 리셋 (이슈 3)
            let timeoutId: ReturnType<typeof setTimeout>;
            const onScrollEnd = () => {
                isClickedRef.current = false;
                scrollContainer.removeEventListener('scrollend', onScrollEnd);
                clearTimeout(timeoutId);
            };
            scrollContainer.addEventListener('scrollend', onScrollEnd, { once: true });
            // 최대 2000ms fallback
            timeoutId = setTimeout(() => {
                isClickedRef.current = false;
                scrollContainer.removeEventListener('scrollend', onScrollEnd);
            }, 2000);

            // requestAnimationFrame으로 스크롤 래핑 (상태 업데이트 후 실행, 이슈 2)
            requestAnimationFrame(() => {
                const elementRect = element.getBoundingClientRect();
                const containerRect = scrollContainer.getBoundingClientRect();
                const offset = elementRect.top - containerRect.top + scrollContainer.scrollTop - 40;
                scrollContainer.scrollTo({ top: Math.max(0, offset), behavior: 'smooth' });
            });

            closeCallback?.();
        } else {
            isClickedRef.current = false;
            closeCallback?.();
        }
    };

    // [MODIFIED] 수정 2: TOC가 비어있을 때 가이드 표시
    // isEditing=true이면 안내 메시지 표시, isEditing=false이면 기존대로 null 반환
    if (toc.length === 0) {
        if (!isEditing) return null;

        const emptyGuideMessage = (
            <p className="text-xs text-slate-400 italic px-2 py-4">
                # 제목 또는 - 목록을 추가하면{'\n'}목차가 자동 생성됩니다
            </p>
        );

        if (isMobile) {
            return (
                <div className="py-2 px-4 space-y-4">
                    {emptyGuideMessage}
                </div>
            );
        }

        return (
            <aside ref={tocRef} onWheel={handleTocWheel} className="hidden lg:block sticky top-8 self-start w-64 mr-4 py-4 flex-shrink-0 max-h-[calc(100vh-4rem)] overflow-y-auto overscroll-contain [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
                <div className="relative border-l-2 border-gray-100 pl-6 space-y-4">
                    <div className="flex items-center gap-2 mb-6">
                        <svg className={`w-4 h-4 ${iconColor}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h7" />
                        </svg>
                        <p className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em]">Table of Contents</p>
                    </div>
                    {emptyGuideMessage}
                </div>
            </aside>
        );
    }

    if (isMobile) {
        return (
            <div className="py-2 px-4 space-y-4">
                <nav className="flex flex-col gap-3">
                    {toc.map((item) => (
                        <a
                            key={`${item.id}-${item.level}`}
                            href={`#${item.id}`}
                            onClick={(e) => {
                                // [MODIFIED] 최상단에 preventDefault + stopPropagation (이슈 2)
                                e.preventDefault();
                                e.stopPropagation();

                                if (isEditing) {
                                    isClickedRef.current = true;
                                    setActiveId(item.id);
                                    // [MODIFIED] setTimeout(0)으로 래핑 (이슈 2)
                                    setTimeout(() => {
                                        handleEditingClick(item);
                                        // scrollend 또는 fallback으로 isClickedRef 리셋
                                        const scrollContainer = document.getElementById('scroll-container');
                                        if (scrollContainer) {
                                            let timeoutId: ReturnType<typeof setTimeout>;
                                            const onScrollEnd = () => {
                                                isClickedRef.current = false;
                                                scrollContainer.removeEventListener('scrollend', onScrollEnd);
                                                clearTimeout(timeoutId);
                                            };
                                            scrollContainer.addEventListener('scrollend', onScrollEnd, { once: true });
                                            timeoutId = setTimeout(() => {
                                                isClickedRef.current = false;
                                                scrollContainer.removeEventListener('scrollend', onScrollEnd);
                                            }, 2000);
                                        } else {
                                            isClickedRef.current = false;
                                        }
                                    }, 0);
                                    onClose?.();
                                } else {
                                    handleViewClick(item, onClose);
                                }
                            }}
                            className={`block text-sm transition-all duration-200 py-1 border-l-2 rounded-r-sm ${
                                activeId === item.id
                                    ? `${activeBorderColor} ${activeBgColor} ${activeTextColor} font-semibold pl-3`
                                    : `border-transparent text-gray-600 font-medium ${item.level === 2 ? 'pl-4' : 'pl-2'}`
                            }`}
                        >
                            <span className="mr-2 text-xs opacity-50 font-mono">{item.number}</span>
                            {item.text}
                        </a>
                    ))}
                </nav>
            </div>
        );
    }

    return (
        // [MODIFIED] onWheel 핸들러 추가 (이슈 4)
        <aside ref={tocRef} onWheel={handleTocWheel} className="hidden lg:block sticky top-8 self-start w-64 mr-4 py-4 flex-shrink-0 max-h-[calc(100vh-4rem)] overflow-y-auto overscroll-contain [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
            <div className="relative border-l-2 border-gray-100 pl-6 space-y-4">
                <div className="flex items-center gap-2 mb-6">
                    <svg className={`w-4 h-4 ${iconColor}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h7" />
                    </svg>
                    <p className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em]">Table of Contents</p>
                </div>

                <nav className="flex flex-col gap-3">
                    {toc.map((item) => {
                        const isActive = activeId === item.id;
                        return (
                            <a
                                key={`${item.id}-${item.level}`}
                                href={`#${item.id}`}
                                onClick={(e) => {
                                    // [MODIFIED] 최상단에 preventDefault + stopPropagation (이슈 2)
                                    e.preventDefault();
                                    e.stopPropagation();

                                    if (isEditing) {
                                        isClickedRef.current = true;
                                        setActiveId(item.id);
                                        // [MODIFIED] setTimeout(0)으로 래핑 (이슈 2)
                                        setTimeout(() => {
                                            handleEditingClick(item);
                                            // scrollend 또는 fallback으로 isClickedRef 리셋
                                            const scrollContainer = document.getElementById('scroll-container');
                                            if (scrollContainer) {
                                                let timeoutId: ReturnType<typeof setTimeout>;
                                                const onScrollEnd = () => {
                                                    isClickedRef.current = false;
                                                    scrollContainer.removeEventListener('scrollend', onScrollEnd);
                                                    clearTimeout(timeoutId);
                                                };
                                                scrollContainer.addEventListener('scrollend', onScrollEnd, { once: true });
                                                timeoutId = setTimeout(() => {
                                                    isClickedRef.current = false;
                                                    scrollContainer.removeEventListener('scrollend', onScrollEnd);
                                                }, 2000);
                                            } else {
                                                isClickedRef.current = false;
                                            }
                                        }, 0);
                                    } else {
                                        handleViewClick(item);
                                    }
                                }}
                                className={`group relative block text-sm transition-all duration-200 rounded-r-md py-1 pl-3 border-l-2 ${
                                    isActive
                                        ? `${activeBorderColor} ${activeBgColor} ${activeTextColor} font-semibold`
                                        : `border-transparent text-gray-500 ${hoverTextColor} font-medium hover:bg-gray-50`
                                } ${item.level === 2 ? 'ml-4' : ''}`}
                            >
                                <span className="relative flex items-center gap-2">
                                    {/* [ADDED] 번호 표시 */}
                                    <span className={`text-[10px] opacity-50 font-mono items-center pt-0.5`}>{item.number}</span>
                                    <span>{item.text}</span>
                                </span>
                            </a>
                        );
                    })}
                </nav>
            </div>
        </aside>
    );
}
