"use client"

import { useMemo, useRef, useEffect, useState } from "react"
import { Marked, type Tokens } from "marked"
import katex from "katex"
import hljs from "highlight.js"

/* ── KaTeX extensions ────────────────────────────── */

const blockMathExtension = {
  name: "blockMath",
  level: "block" as const,
  start: (src: string) => src.indexOf("$$"),
  tokenizer(src: string) {
    const match = /^\$\$([\s\S]+?)\$\$/.exec(src)
    if (match) return { type: "blockMath", raw: match[0], text: match[1].trim() }
    return undefined
  },
  renderer(token: { text: string }) {
    try {
      return `<div class="math-block">${katex.renderToString(token.text, {
        displayMode: true,
        throwOnError: false,
        output: "html",
      })}</div>\n`
    } catch {
      return `<div class="math-block math-error">$$${token.text}$$</div>\n`
    }
  },
}

const inlineMathExtension = {
  name: "inlineMath",
  level: "inline" as const,
  start: (src: string) => {
    const i = src.indexOf("$")
    return i === -1 ? undefined : i
  },
  tokenizer(src: string) {
    const match = /^\$(?!\$)([^$\n]+?)\$/.exec(src)
    if (match) return { type: "inlineMath", raw: match[0], text: match[1].trim() }
    return undefined
  },
  renderer(token: { text: string }) {
    try {
      return katex.renderToString(token.text, {
        displayMode: false,
        throwOnError: false,
        output: "html",
      })
    } catch {
      return `<span class="math-error">$${token.text}$</span>`
    }
  },
}

/* ── Wikilink extension ──────────────────────────── */

const wikilinkExtension = {
  name: "wikilink",
  level: "inline" as const,
  start: (src: string) => src.indexOf("[["),
  tokenizer(src: string) {
    // [[target#fragment|text]]  —  target and fragment are both optional
    const match = /^\[\[([^\]|#]*?)(?:#([^\]|]*?))?(?:\|([^\]]+?))?\]\]/.exec(src)
    if (!match) return undefined
    const target   = match[1].trim()
    const fragment = match[2]?.trim() ?? ""
    const fallback = target + (fragment ? `#${fragment}` : "")
    return {
      type:     "wikilink",
      raw:      match[0],
      target,
      fragment,
      text:     (match[3] ?? fallback).trim(),
    }
  },
  renderer(token: { target: string; fragment: string; text: string }) {
    const escaped = token.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    const target  = token.target.replace(/"/g, "&quot;")
    const frag    = token.fragment.replace(/"/g, "&quot;")
    return `<span class="preview-wikilink" data-wikilink="${target}" data-fragment="${frag}">${escaped}</span>`
  },
}

/* ── Marked instance (isolated, not global) ──────── */

const md = new Marked()

md.use({
  renderer: {
    code(token: Tokens.Code): string {
      const lang = (token.lang ?? "").split(/\s/)[0]
      if (lang && hljs.getLanguage(lang)) {
        try {
          const highlighted = hljs.highlight(token.text, {
            language: lang,
            ignoreIllegals: true,
          }).value
          return `<pre><code class="hljs language-${lang}">${highlighted}</code></pre>\n`
        } catch { /* fall through */ }
      }
      return `<pre><code class="hljs">${hljs.highlightAuto(token.text).value}</code></pre>\n`
    },
    heading(token: Tokens.Heading): string {
      const raw = token.text.replace(/<[^>]*>/g, "")
      const id  = raw.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-")
      return `<h${token.depth} id="${id}">${token.text}</h${token.depth}>\n`
    },
    link(token: Tokens.Link): string {
      const href      = token.href ?? ""
      const isExt     = /^https?:\/\//.test(href)
      const titleAttr = token.title ? ` title="${token.title.replace(/"/g, "&quot;")}"` : ""
      const extAttrs  = isExt ? ` target="_blank" rel="noopener noreferrer"` : ""
      return `<a href="${href}"${titleAttr}${extAttrs}>${token.text}</a>`
    },
  },
  extensions: [blockMathExtension, inlineMathExtension, wikilinkExtension],
})

/* ── Frontmatter-Parser ────────────────────────────── */

interface FmData { title: string; description?: string; status?: string; lang?: string; topic?: string; draft?: boolean }

function parseFm(markdown: string): { fm: FmData | null; body: string } {
  if (!markdown.startsWith("---\n")) return { fm: null, body: markdown }
  const end = markdown.indexOf("\n---\n", 4)
  if (end === -1) return { fm: null, body: markdown }

  const lines = markdown.slice(4, end).split("\n")
  const body  = markdown.slice(end + 5)
  const fm: FmData = { title: "" }

  for (const line of lines) {
    const m = line.match(/^(\w+):\s*(.*)$/)
    if (!m) continue
    const [, key, rawVal] = m
    const val = rawVal.replace(/^["']|["']$/g, "").trim()
    switch (key) {
      case "title":       fm.title       = val; break
      case "description": fm.description = val; break
      case "status":      fm.status      = val; break
      case "lang":        fm.lang        = val; break
      case "topic":       fm.topic       = val; break
      case "draft":       fm.draft       = rawVal.trim() === "true"; break
    }
  }
  return { fm: fm.title ? fm : null, body }
}

const STATUS_COLOR: Record<string, string> = {
  seed:      "var(--seed)",
  budding:   "var(--budding)",
  evergreen: "var(--evergreen)",
}

/* ── Component ────────────────────────────────────── */

interface NoteRef { slug: string; title: string }

interface Props {
  content:        string
  className?:     string
  notes?:         NoteRef[]
  onSelectNote?:  (slug: string) => void
}

export default function Preview({ content, className, notes, onSelectNote }: Props) {
  const { fm, body } = useMemo(() => parseFm(content), [content])

  const html = useMemo(() => {
    try {
      const result = md.parse(body)
      return typeof result === "string" ? result : ""
    } catch {
      return ""
    }
  }, [body])

  // Refs so the event listener never goes stale
  const proseRef           = useRef<HTMLDivElement>(null)
  const notesRef           = useRef<NoteRef[]>(notes ?? [])
  const selectRef          = useRef(onSelectNote)
  const pendingFragmentRef = useRef<string | null>(null)
  useEffect(() => { notesRef.current = notes ?? [] },   [notes])
  useEffect(() => { selectRef.current = onSelectNote }, [onSelectNote])

  // After HTML re-renders, scroll to any pending fragment (cross-note navigation)
  useEffect(() => {
    if (!pendingFragmentRef.current || !proseRef.current) return
    const frag = pendingFragmentRef.current
    pendingFragmentRef.current = null
    const target = proseRef.current.querySelector(`#${CSS.escape(frag)}`)
    if (target) setTimeout(() => target.scrollIntoView({ behavior: "smooth", block: "start" }), 50)
  }, [html])

  // Toast for unresolvable wikilinks
  const [toast, setToast]   = useState<string | null>(null)
  const toastTimer           = useRef<ReturnType<typeof setTimeout>>()
  const showToast = (msg: string) => {
    setToast(msg)
    clearTimeout(toastTimer.current)
    toastTimer.current = setTimeout(() => setToast(null), 4000)
  }

  // Mark broken wikilinks visually after render
  useEffect(() => {
    const el = proseRef.current
    if (!el) return
    el.querySelectorAll<HTMLElement>("[data-wikilink]").forEach(span => {
      const raw   = span.dataset.wikilink ?? ""
      const lower = raw.toLowerCase().trim()
      const slug  = raw.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
      const list  = notesRef.current
      const found = !!(
        list.find(n => n.title.toLowerCase().trim() === lower) ??
        list.find(n => n.slug.toLowerCase() === lower) ??
        list.find(n => (n.slug.split("/").pop() ?? "") === slug)
      )
      span.dataset.broken = found ? "false" : "true"
    })
  }, [html, notes])

  useEffect(() => {
    const el = proseRef.current
    if (!el) return

    const handleClick = (e: MouseEvent) => {
      const t = e.target as HTMLElement

      const wikiSpan = t.closest<HTMLElement>("[data-wikilink]")
      if (wikiSpan) {
        const raw      = wikiSpan.dataset.wikilink ?? ""
        const fragment = wikiSpan.dataset.fragment ?? ""

        // In-page anchor: [[#abschnitt]]
        if (!raw && fragment) {
          const anchor = proseRef.current?.querySelector(`#${CSS.escape(fragment)}`)
          if (anchor) anchor.scrollIntoView({ behavior: "smooth", block: "start" })
          else showToast(`„#${fragment}" — Abschnitt nicht gefunden`)
          return
        }

        const lower = raw.toLowerCase().trim()
        const slug  = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
        const list  = notesRef.current
        const note  =
          list.find(n => n.title.toLowerCase().trim() === lower) ??
          list.find(n => n.slug.toLowerCase() === lower) ??
          list.find(n => (n.slug.split("/").pop() ?? "") === slug) ??
          list.find(n => (n.slug.split("/").pop() ?? "").includes(slug.slice(0, 20)))
        if (note) {
          if (fragment) pendingFragmentRef.current = fragment
          selectRef.current?.(note.slug)
        } else {
          showToast(`„${raw.length > 48 ? raw.slice(0, 48) + "…" : raw}" — Notiz nicht gefunden`)
        }
        return
      }

      const anchor = t.closest<HTMLAnchorElement>("a[href]")
      if (anchor && /^https?:\/\//.test(anchor.href)) {
        e.preventDefault()
        window.open(anchor.href, "_blank", "noopener,noreferrer")
      }
    }

    el.addEventListener("click", handleClick)
    return () => el.removeEventListener("click", handleClick)
  }, [])

  return (
    <div className={`relative overflow-auto ${className ?? ""}`}>
      {toast && (
        <div className="preview-toast">
          {toast}
        </div>
      )}
      <div className="px-7 py-10">

        {/* Frontmatter-Header */}
        {fm && (
          <div className="mb-8 pb-6 border-b border-border/40">
            {fm.topic && (
              <div className="text-[10px] font-mono text-muted-fg/40 uppercase tracking-widest mb-2">
                {fm.topic}
              </div>
            )}
            <h1
              className="text-2xl font-semibold mb-2 leading-snug"
              style={{ fontFamily: "var(--font-newsreader, Georgia, serif)", fontStyle: "italic" }}
            >
              {fm.title}
            </h1>
            {fm.description && (
              <p className="text-sm text-muted-fg mb-3 leading-relaxed">{fm.description}</p>
            )}
            <div className="flex items-center gap-2 flex-wrap">
              {fm.status && (
                <span
                  className="inline-flex items-center gap-1.5 text-[10px] font-mono px-2 py-0.5 rounded-full border"
                  style={{
                    borderColor: STATUS_COLOR[fm.status] ?? "var(--border)",
                    color:       STATUS_COLOR[fm.status] ?? "var(--muted-fg)",
                    background:  `oklch(from ${STATUS_COLOR[fm.status] ?? "var(--muted-fg)"} l c h / 0.08)`,
                  }}
                >
                  <div className="w-1.5 h-1.5 rounded-full" style={{ backgroundColor: STATUS_COLOR[fm.status] ?? "var(--muted-fg)" }} />
                  {fm.status}
                </span>
              )}
              {fm.draft && (
                <span className="text-[10px] font-mono px-2 py-0.5 rounded-full border" style={{ borderColor: "var(--draft)", color: "var(--draft)", background: "oklch(from var(--draft) l c h / 0.08)" }}>
                  in review
                </span>
              )}
              {fm.lang && (
                <span className="text-[10px] font-mono text-muted-fg/40 border border-border/40 px-2 py-0.5 rounded-full">
                  {fm.lang.toUpperCase()}
                </span>
              )}
            </div>
          </div>
        )}

        <div
          ref={proseRef}
          className="preview-prose"
          dangerouslySetInnerHTML={{ __html: html }}
        />
      </div>
    </div>
  )
}
