import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { marked } from 'marked';

interface Heading {
  level: number;
  text: string;
  id: string;
}

interface SearchMatch {
  type: 'heading' | 'text';
  text: string;
  id: string;       // heading id to scroll to
  context?: string;  // surrounding text for text matches
}

interface MarkdownViewerProps {
  content: string;
  headings: Heading[];
}

function renderMarkdown(content: string): string {
  const stripped = content.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '');

  marked.setOptions({ gfm: true, breaks: false });

  const renderer = new marked.Renderer();
  renderer.heading = ({ text, depth }: { text: string; depth: number }) => {
    const id = text.toLowerCase().replace(/[^\w]+/g, '-').replace(/^-|-$/g, '');
    return `<h${depth} id="${id}" class="md-heading">${text}</h${depth}>`;
  };

  renderer.code = ({ text, lang }: { text: string; lang?: string }) => {
    return `<div class="md-code-block"><div class="md-code-lang">${lang || ''}</div><pre><code class="language-${lang || ''}">${text.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre></div>`;
  };

  return marked.parse(stripped, { renderer }) as string;
}

/** Build a searchable index: map each paragraph/line to its nearest preceding heading */
function buildSearchIndex(content: string, headings: Heading[]): { text: string; headingId: string }[] {
  const stripped = content.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, '');
  const lines = stripped.split('\n');
  const entries: { text: string; headingId: string }[] = [];
  let currentHeadingId = headings[0]?.id ?? '';

  let paragraph = '';
  for (const line of lines) {
    const headingMatch = line.match(/^(#{1,4})\s+(.+)$/);
    if (headingMatch) {
      if (paragraph.trim()) {
        entries.push({ text: paragraph.trim(), headingId: currentHeadingId });
        paragraph = '';
      }
      const text = headingMatch[2].replace(/[*_`\[\]]/g, '').trim();
      currentHeadingId = text.toLowerCase().replace(/[^\w]+/g, '-').replace(/^-|-$/g, '');
      continue;
    }
    if (line.trim() === '') {
      if (paragraph.trim()) {
        entries.push({ text: paragraph.trim(), headingId: currentHeadingId });
        paragraph = '';
      }
    } else {
      paragraph += (paragraph ? ' ' : '') + line.trim();
    }
  }
  if (paragraph.trim()) {
    entries.push({ text: paragraph.trim(), headingId: currentHeadingId });
  }
  return entries;
}

export default function MarkdownViewer({ content, headings }: MarkdownViewerProps) {
  const [mode, setMode] = useState<'code' | 'preview'>('preview');
  const [expanded, setExpanded] = useState(false);
  const [activeHeading, setActiveHeading] = useState('');
  const [searchQuery, setSearchQuery] = useState('');
  const [searchOpen, setSearchOpen] = useState(false);
  const [selectedIdx, setSelectedIdx] = useState(0);
  const [highlightCount, setHighlightCount] = useState(0);
  const previewRef = useRef<HTMLDivElement>(null);
  const codeRef = useRef<HTMLDivElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);

  const html = useMemo(() => renderMarkdown(content), [content]);
  const searchIndex = useMemo(() => buildSearchIndex(content, headings), [content, headings]);

  // Search results
  const searchResults = useMemo((): SearchMatch[] => {
    if (!searchQuery.trim() || searchQuery.length < 2) return [];
    const q = searchQuery.toLowerCase();
    const results: SearchMatch[] = [];
    const seen = new Set<string>();

    // Search headings first
    for (const h of headings) {
      if (h.text.toLowerCase().includes(q)) {
        results.push({ type: 'heading', text: h.text, id: h.id });
        seen.add(h.id);
      }
    }

    // Search content paragraphs
    for (const entry of searchIndex) {
      if (entry.text.toLowerCase().includes(q) && !seen.has(entry.headingId)) {
        const idx = entry.text.toLowerCase().indexOf(q);
        const start = Math.max(0, idx - 40);
        const end = Math.min(entry.text.length, idx + q.length + 40);
        const context = (start > 0 ? '...' : '') +
          entry.text.slice(start, end) +
          (end < entry.text.length ? '...' : '');
        results.push({ type: 'text', text: entry.text.slice(idx, idx + q.length), id: entry.headingId, context });
        seen.add(entry.headingId);
      }
    }

    return results.slice(0, 15);
  }, [searchQuery, headings, searchIndex]);

  // Highlight matches inside preview
  useEffect(() => {
    if (!previewRef.current || mode !== 'preview') return;

    // Remove old highlights
    previewRef.current.querySelectorAll('mark.md-search-hl').forEach((m) => {
      const parent = m.parentNode;
      if (parent) {
        parent.replaceChild(document.createTextNode(m.textContent || ''), m);
        parent.normalize();
      }
    });

    if (!searchQuery.trim() || searchQuery.length < 2) {
      setHighlightCount(0);
      return;
    }

    const walker = document.createTreeWalker(previewRef.current, NodeFilter.SHOW_TEXT);
    const q = searchQuery.toLowerCase();
    const nodesToProcess: { node: Text; indices: number[] }[] = [];

    let node: Text | null;
    while ((node = walker.nextNode() as Text | null)) {
      const text = node.textContent || '';
      const lower = text.toLowerCase();
      const indices: number[] = [];
      let pos = 0;
      while ((pos = lower.indexOf(q, pos)) !== -1) {
        indices.push(pos);
        pos += q.length;
      }
      if (indices.length > 0) {
        nodesToProcess.push({ node, indices });
      }
    }

    let count = 0;
    for (const { node: textNode, indices } of nodesToProcess) {
      const text = textNode.textContent || '';
      const parent = textNode.parentNode;
      if (!parent || parent.nodeName === 'CODE' || parent.nodeName === 'PRE') continue;

      const frag = document.createDocumentFragment();
      let lastIdx = 0;
      for (const idx of indices) {
        if (idx > lastIdx) frag.appendChild(document.createTextNode(text.slice(lastIdx, idx)));
        const mark = document.createElement('mark');
        mark.className = 'md-search-hl';
        mark.textContent = text.slice(idx, idx + q.length);
        frag.appendChild(mark);
        lastIdx = idx + q.length;
        count++;
      }
      if (lastIdx < text.length) frag.appendChild(document.createTextNode(text.slice(lastIdx)));
      parent.replaceChild(frag, textNode);
    }
    setHighlightCount(count);
  }, [searchQuery, mode, html]);

  // Track active heading on scroll
  useEffect(() => {
    if (mode !== 'preview' || !previewRef.current) return;

    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting) setActiveHeading(entry.target.id);
        }
      },
      { rootMargin: '-80px 0px -70% 0px', threshold: 0 }
    );

    const headingEls = previewRef.current.querySelectorAll('.md-heading');
    headingEls.forEach((el) => observer.observe(el));
    return () => observer.disconnect();
  }, [mode, html]);

  // Keyboard shortcut: Cmd/Ctrl+F to open search
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'f') {
        // Only intercept if the viewer is visible
        if (previewRef.current || codeRef.current) {
          e.preventDefault();
          setSearchOpen(true);
          setTimeout(() => searchInputRef.current?.focus(), 50);
        }
      }
      if (e.key === 'Escape' && searchOpen) {
        setSearchOpen(false);
        setSearchQuery('');
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [searchOpen]);

  // Reset selection when results change
  useEffect(() => { setSelectedIdx(0); }, [searchResults]);

  const handleCopy = () => {
    navigator.clipboard.writeText(content);
  };

  const scrollToHeading = useCallback((id: string) => {
    if (!expanded) setExpanded(true);
    // Small delay to allow expand animation
    setTimeout(() => {
      const container = previewRef.current || codeRef.current;
      const el = container?.querySelector(`#${CSS.escape(id)}`);
      if (el) {
        el.scrollIntoView({ behavior: 'smooth', block: 'start' });
        setActiveHeading(id);
      }
    }, expanded ? 0 : 100);
  }, [expanded]);

  const navigateToResult = useCallback((result: SearchMatch) => {
    if (mode !== 'preview') setMode('preview');
    scrollToHeading(result.id);
    setSearchOpen(false);
  }, [mode, scrollToHeading]);

  const handleSearchKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setSelectedIdx((i) => Math.min(i + 1, searchResults.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setSelectedIdx((i) => Math.max(i - 1, 0));
    } else if (e.key === 'Enter' && searchResults[selectedIdx]) {
      e.preventDefault();
      navigateToResult(searchResults[selectedIdx]);
    }
  };

  return (
    <div className="flex gap-4">
      {/* Main content */}
      <div className="flex-1 min-w-0">
        {/* Toolbar */}
        <div className="flex items-center justify-between mb-3">
          <div className="flex items-center gap-2">
            <div className="flex items-center gap-1 bg-[var(--color-surface-3)] rounded-lg p-0.5">
              <button
                onClick={() => setMode('code')}
                className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors ${
                  mode === 'code'
                    ? 'bg-[var(--color-surface-2)] text-[var(--color-text-primary)] shadow-sm'
                    : 'text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)]'
                }`}
              >
                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
                </svg>
                Code
              </button>
              <button
                onClick={() => setMode('preview')}
                className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors ${
                  mode === 'preview'
                    ? 'bg-[var(--color-surface-2)] text-[var(--color-text-primary)] shadow-sm'
                    : 'text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)]'
                }`}
              >
                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
                  <path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                </svg>
                Preview
              </button>
            </div>

            {/* Search toggle */}
            <button
              onClick={() => {
                setSearchOpen(!searchOpen);
                if (!searchOpen) setTimeout(() => searchInputRef.current?.focus(), 50);
              }}
              className={`flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs transition-colors ${
                searchOpen
                  ? 'bg-accent-500/15 text-accent-400'
                  : 'text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] hover:bg-white/[0.04]'
              }`}
              title="Search in document (Cmd+F)"
            >
              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
              </svg>
              <span className="hidden sm:inline">Search</span>
              <kbd className="hidden sm:inline text-[9px] px-1 py-0.5 rounded bg-white/[0.06] text-[var(--color-text-tertiary)] font-mono ml-1">
                {navigator.platform?.includes('Mac') ? '\u2318' : 'Ctrl'}F
              </kbd>
            </button>
          </div>

          <button
            onClick={handleCopy}
            className="text-[11px] px-2.5 py-1 rounded-md bg-white/[0.06] hover:bg-white/[0.12] text-[var(--color-text-tertiary)] hover:text-[var(--color-text-primary)] transition-colors"
          >
            Copy
          </button>
        </div>

        {/* Search bar */}
        {searchOpen && (
          <div className="mb-3 relative">
            <div className="relative">
              <svg className="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-[var(--color-text-tertiary)] pointer-events-none" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
              </svg>
              <input
                ref={searchInputRef}
                type="text"
                value={searchQuery}
                onChange={(e) => setSearchQuery(e.target.value)}
                onKeyDown={handleSearchKeyDown}
                placeholder="Search sections and content..."
                className="w-full bg-[var(--color-surface-2)] border border-[var(--color-border)] rounded-lg pl-9 pr-20 py-2 text-sm text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:border-accent-400/50 focus:ring-1 focus:ring-accent-400/20 font-sans"
                autoFocus
              />
              <div className="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2">
                {searchQuery.length >= 2 && (
                  <span className="text-[10px] text-[var(--color-text-tertiary)]">
                    {highlightCount > 0 ? `${highlightCount} match${highlightCount !== 1 ? 'es' : ''}` : 'No matches'}
                  </span>
                )}
                <button
                  onClick={() => { setSearchOpen(false); setSearchQuery(''); }}
                  className="text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)]"
                >
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
                  </svg>
                </button>
              </div>
            </div>

            {/* Search results dropdown */}
            {searchResults.length > 0 && (
              <div className="absolute z-20 top-full left-0 right-0 mt-1 bg-[var(--color-surface-2)] border border-[var(--color-border)] rounded-lg shadow-xl overflow-hidden max-h-72 overflow-y-auto">
                {searchResults.map((result, i) => (
                  <button
                    key={`${result.id}-${i}`}
                    onClick={() => navigateToResult(result)}
                    onMouseEnter={() => setSelectedIdx(i)}
                    className={`w-full text-left px-3 py-2.5 flex items-start gap-2.5 transition-colors ${
                      i === selectedIdx ? 'bg-white/[0.06]' : 'hover:bg-white/[0.03]'
                    }`}
                  >
                    {result.type === 'heading' ? (
                      <svg className="w-3.5 h-3.5 mt-0.5 shrink-0 text-accent-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M5.25 7.5h13.5m-13.5 4.5h7.5m-7.5 4.5h13.5" />
                      </svg>
                    ) : (
                      <svg className="w-3.5 h-3.5 mt-0.5 shrink-0 text-[var(--color-text-tertiary)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
                      </svg>
                    )}
                    <div className="flex-1 min-w-0">
                      {result.type === 'heading' ? (
                        <HighlightText text={result.text} query={searchQuery} className="text-sm text-[var(--color-text-primary)] font-medium" />
                      ) : (
                        <>
                          <div className="text-[11px] text-[var(--color-text-tertiary)] mb-0.5">
                            {headings.find((h) => h.id === result.id)?.text ?? ''}
                          </div>
                          <HighlightText
                            text={result.context || ''}
                            query={searchQuery}
                            className="text-xs text-[var(--color-text-secondary)] leading-relaxed"
                          />
                        </>
                      )}
                    </div>
                    {i === selectedIdx && (
                      <kbd className="text-[9px] px-1 py-0.5 rounded bg-white/[0.06] text-[var(--color-text-tertiary)] font-mono self-center shrink-0">
                        Enter
                      </kbd>
                    )}
                  </button>
                ))}
              </div>
            )}
          </div>
        )}

        {/* Content area */}
        <div className="relative">
          {mode === 'code' ? (
            <div
              ref={codeRef}
              className={`bg-[var(--color-surface-2)] border border-[var(--color-border)] rounded-lg p-6 text-sm text-[var(--color-text-primary)] leading-relaxed whitespace-pre-wrap font-mono overflow-hidden transition-[max-height] duration-300 ${
                expanded ? '' : 'max-h-[32rem]'
              }`}
            >
              {content || 'No content available'}
            </div>
          ) : (
            <div
              ref={previewRef}
              className={`md-preview bg-[var(--color-surface-2)] border border-[var(--color-border)] rounded-lg p-6 overflow-hidden transition-[max-height] duration-300 ${
                expanded ? '' : 'max-h-[32rem]'
              }`}
              dangerouslySetInnerHTML={{ __html: html }}
            />
          )}

          {!expanded && (
            <div className="mt-4 flex justify-center">
              <button
                onClick={() => setExpanded(true)}
                className="text-[13px] font-medium text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] flex items-center gap-2 px-4 py-2 rounded-lg hover:bg-[var(--color-surface-3)] border border-[var(--color-border)] hover:border-[var(--color-primary-500)]/40 transition-all duration-150 active:scale-95"
              >
                <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
                </svg>
                Show full {mode === 'preview' ? 'document' : 'source'}
              </button>
            </div>
          )}

          {expanded && (
            <div className="flex justify-center mt-4">
              <button
                onClick={() => setExpanded(false)}
                className="text-[13px] font-medium text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] flex items-center gap-2 px-4 py-2 rounded-lg hover:bg-[var(--color-surface-3)] border border-[var(--color-border)] hover:border-[var(--color-primary-500)]/40 transition-all duration-150 active:scale-95"
              >
                <svg className="w-4 h-4 rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
                </svg>
                Collapse
              </button>
            </div>
          )}
        </div>
      </div>

      {/* Table of Contents sidebar */}
      {headings.length > 1 && mode === 'preview' && (
        <nav className="hidden xl:block w-56 shrink-0 ml-6">
          <div className="sticky top-28">
            <h4 className="text-[10px] font-bold text-[var(--color-text-tertiary)] uppercase tracking-[0.08em] mb-3 px-3">
              On this page
            </h4>
            <ul className="space-y-1 text-[12px] border-l-2 border-[var(--color-border)] max-h-[calc(100vh-12rem)] overflow-y-auto">
              {headings.map((h) => (
                <li key={h.id}>
                  <button
                    onClick={() => scrollToHeading(h.id)}
                    className={`block w-full text-left py-1.5 transition-all duration-150 border-l-2 -ml-0.5 ${
                      h.level === 1 ? 'pl-3' : h.level === 2 ? 'pl-5' : 'pl-7'
                    } ${
                      activeHeading === h.id
                        ? 'border-[var(--color-primary-400)] text-[var(--color-primary-400)] font-medium'
                        : 'border-transparent text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] hover:border-[var(--color-border)]'
                    }`}
                    title={h.text}
                  >
                    <span className="line-clamp-2 leading-snug">{h.text}</span>
                  </button>
                </li>
              ))}
            </ul>
          </div>
        </nav>
      )}
    </div>
  );
}

/** Highlights query matches within text */
function HighlightText({ text, query, className }: { text: string; query: string; className?: string }) {
  if (!query.trim()) return <span className={className}>{text}</span>;

  const parts: { text: string; match: boolean }[] = [];
  const lower = text.toLowerCase();
  const q = query.toLowerCase();
  let lastIdx = 0;

  let pos = 0;
  while ((pos = lower.indexOf(q, lastIdx)) !== -1) {
    if (pos > lastIdx) parts.push({ text: text.slice(lastIdx, pos), match: false });
    parts.push({ text: text.slice(pos, pos + q.length), match: true });
    lastIdx = pos + q.length;
  }
  if (lastIdx < text.length) parts.push({ text: text.slice(lastIdx), match: false });

  return (
    <span className={className}>
      {parts.map((p, i) =>
        p.match ? (
          <mark key={i} className="bg-accent-400/25 text-accent-300 rounded-sm px-px">{p.text}</mark>
        ) : (
          <span key={i}>{p.text}</span>
        )
      )}
    </span>
  );
}
