"use client";

import { useCallback, useEffect, useMemo, useRef, useState, type ComponentProps } from "react";
import dynamic from "next/dynamic";
import "react-quill-new/dist/quill.snow.css";
import { useShallow } from "zustand/react/shallow";

import { Modal } from "@/components/ui/modal";
import Button from "@/components/ui/button/Button";
import Label from "@/components/form/Label";

import type { WikiSnapshot } from "@/uhm/types/wiki";
import { newId } from "@/uhm/lib/utils/id";
import type ReactQuill from "react-quill-new";
import { checkWikiSlugExists, fetchWikiBySlug, searchWikisByTitle, type Wiki } from "@/uhm/api/wikis";
import NewBadge from "@/uhm/components/editor/NewBadge";
import { useEditorStore } from "@/uhm/store/editorStore";

type ReactQuillProps = ComponentProps<typeof ReactQuill>;
type QuillRange = { index: number; length: number };
type QuillLike = {
  getSelection?: () => QuillRange | null;
  getFormat?: (...args: unknown[]) => Record<string, unknown>;
  setSelection?: (...args: unknown[]) => void;
  formatText?: (...args: unknown[]) => void;
  insertText?: (...args: unknown[]) => void;
  format?: (...args: unknown[]) => void;
  getText?: (index: number, length: number) => string;
};
type QuillModule = {
  Quill?: {
    import?: (path: string) => unknown;
    register?: (pathOrModule: unknown, moduleOrOverwrite?: unknown, overwrite?: boolean) => void;
  };
};
type QuillLinkFormat = {
  sanitize?: (url: unknown) => unknown;
  __uhmAllowSlugHref?: boolean;
  __uhmOriginalSanitize?: unknown;
};
type QuillImageFormatCtor = {
  new (): {
    domNode: Element;
    format(name: string, value: string): void;
  };
  formats: (domNode: Element) => Record<string, string>;
};

const ReactQuillEditor = dynamic<ReactQuillProps>(() => import("react-quill-new"), {
  ssr: false,
  loading: () => <div className="h-[480px] w-full animate-pulse bg-gray-100 rounded-lg" />,
});

let quillLinkSanitizePatched = false;

type Props = {
  projectId: string;
  setWikis: React.Dispatch<React.SetStateAction<WikiSnapshot[]>>;
  onRemoveWiki?: (wikiId: string) => void;
};

function clampTitle(title: string) {
  const t = title.trim();
  return t.length ? t.slice(0, 120) : "Untitled wiki";
}

export default function WikiSidebarPanel({ projectId, setWikis, onRemoveWiki }: Props) {
  const { wikis, requestedActiveId } = useEditorStore(
    useShallow((state) => ({
      wikis: state.snapshotWikis,
      requestedActiveId: state.requestedActiveWikiId,
    }))
  );
  const [open, setOpen] = useState(false);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [collapsed, setCollapsed] = useState(false);
  const activeWiki = useMemo(() => wikis.find((w) => w.id === activeId) || null, [activeId, wikis]);

  const [wikiTitle, setWikiTitle] = useState("");
  const [wikiSlug, setWikiSlug] = useState("");
  const [wikiDocHtml, setWikiDocHtml] = useState("");
  const wikiDocStorageFormat = useMemo(() => detectWikiDocStorageFormat(wikiDocHtml), [wikiDocHtml]);
  const [wikiSaveError, setWikiSaveError] = useState<string | null>(null);
  const [isCreateOpen, setIsCreateOpen] = useState(false);
  const [createTitle, setCreateTitle] = useState("");
  const [createSlug, setCreateSlug] = useState("");
  const [createSlugTouched, setCreateSlugTouched] = useState(false);
  const [createError, setCreateError] = useState<string | null>(null);
  const [isCheckingCreateSlug, setIsCheckingCreateSlug] = useState(false);

  // Quill: custom link UI (link-to-wiki by slug).
  const wikiLinkIntentRef = useRef<{
    quill: QuillLike;
    range: QuillRange | null;
    activeWikiId: string | null;
    existingHref: string | null;
  } | null>(null);
  const wikiLinkHandlerRef = useRef<(quill: QuillLike | null | undefined) => void>(() => {});
  const [isWikiLinkOpen, setIsWikiLinkOpen] = useState(false);
  const [wikiLinkQuery, setWikiLinkQuery] = useState("");
  const [wikiLinkError, setWikiLinkError] = useState<string | null>(null);
  const [wikiLinkSearchMode, setWikiLinkSearchMode] = useState<"title" | "slug">("title");
  const [globalWikiResults, setGlobalWikiResults] = useState<Wiki[]>([]);
  const [isGlobalWikiSearching, setIsGlobalWikiSearching] = useState(false);
  const [globalWikiSearchError, setGlobalWikiSearchError] = useState<string | null>(null);
  const globalWikiSearchRequestRef = useRef(0);
  const importFileInputRef = useRef<HTMLInputElement | null>(null);

  // Allow Quill to keep wiki links where href is a slug (no scheme).
  useEffect(() => {
    if (typeof window === "undefined") return;
    if (quillLinkSanitizePatched) return;
    quillLinkSanitizePatched = true;

    (async () => {
      try {
        const mod = await import("react-quill-new") as QuillModule;
        const Quill = mod?.Quill;
        if (!Quill) return;

        try {
          const BlotFormatterModule = await import("quill-blot-formatter");
          const BlotFormatter = BlotFormatterModule.default;
          // Only register if not already registered to avoid errors in dev/HMR
          Quill.register?.("modules/blotFormatter", BlotFormatter, true);
        } catch (err) {
          console.error("Failed to load quill-blot-formatter", err);
        }

        const ImageFormat = Quill.import?.("formats/image") as QuillImageFormatCtor | undefined;
        if (ImageFormat) {
          const BaseImageFormat = ImageFormat;
          class CustomImage extends BaseImageFormat {
            static formats(domNode: Element) {
              const formats = BaseImageFormat.formats(domNode) || {};
              const style = domNode.getAttribute("style");
              const width = domNode.getAttribute("width");
              const height = domNode.getAttribute("height");
              const className = domNode.getAttribute("class");
              if (style) formats.style = style;
              if (width) formats.width = width;
              if (height) formats.height = height;
              if (className) formats.class = className;
              return formats;
            }

            format(name: string, value: string) {
              if (["style", "width", "height", "class"].includes(name)) {
                if (value) {
                  this.domNode.setAttribute(name, value);
                } else {
                  this.domNode.removeAttribute(name);
                }
              } else {
                super.format(name, value);
              }
            }
          }
          Quill.register?.(CustomImage, true);
        }

        const Link = Quill.import?.("formats/link");
        if (!Link) return;

        const anyLink = Link as QuillLinkFormat;
        if (anyLink.__uhmAllowSlugHref) return;
        const original = anyLink.sanitize;
        anyLink.sanitize = (url: unknown) => {
          const value = String(url ?? "").trim();
          const lower = value.toLowerCase();
          if (lower.startsWith("javascript:")) return "";
          // Keep slug/relative/external as-is; rendering layer will rewrite slug links for navigation.
          return value;
        };
        anyLink.__uhmAllowSlugHref = true;
        anyLink.__uhmOriginalSanitize = original;
      } catch {
        // ignore
      }
    })();
  }, []);

  useEffect(() => {
    if (!requestedActiveId) return;
    if (wikis.some((w) => w.id === requestedActiveId)) {
      setActiveId(requestedActiveId);
    }
  }, [requestedActiveId, wikis]);

  // keep editor content in sync when switching wiki
  useEffect(() => {
    if (!open) return;

    setWikiTitle(activeWiki?.title || "");
    setWikiSlug(typeof activeWiki?.slug === "string" ? activeWiki.slug : "");
    setWikiDocHtml(normalizeWikiDocForQuill(activeWiki?.doc || null));
    setWikiSaveError(null);
  }, [activeWiki?.doc, activeWiki?.slug, activeWiki?.title, open]);

  const ensureActive = () => {
    if (activeId && wikis.some((w) => w.id === activeId)) return;
    setActiveId(wikis[0]?.id || null);
  };

  useEffect(() => {
    ensureActive();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [wikis.length]);

  const createWikiAndOpen = (title?: string, slug?: string | null) => {
    const id = newId();
    const seedTitle = clampTitle(title || "Untitled wiki");
    const seed: WikiSnapshot = {
      id,
      source: "inline",
      operation: "create",
      title: seedTitle,
      slug: slug ?? null,
      doc: "",
    };
    setWikis((prev) => [seed, ...prev]);
    setActiveId(id);
    setOpen(true);
  };

  const handleCreateWikiFromPanel = async () => {
    const title = clampTitle(createTitle);
    const slug = normalizeWikiSlugInput(createSlug);
    if (!slug) {
      setCreateError("Slug la bat buoc. Hay thu mot slug khac.");
      return;
    }

    setIsCheckingCreateSlug(true);
    setCreateError(null);
    try {
      const exists = await checkWikiSlugExists(slug);
      if (exists) {
        setCreateError("Slug da ton tai. Hay thu slug khac.");
        return;
      }
      createWikiAndOpen(title, slug);
      setCreateTitle("");
      setCreateSlug("");
      setCreateSlugTouched(false);
      setIsCreateOpen(false);
    } catch (err) {
      const msg = err instanceof Error ? err.message : "Khong check duoc slug.";
      setCreateError(msg);
    } finally {
      setIsCheckingCreateSlug(false);
    }
  };

  const removeWiki = (id: string) => {
    if (onRemoveWiki) {
      onRemoveWiki(id);
    } else {
      setWikis((prev) => prev.filter((w) => w.id !== id));
    }
    if (activeId === id) setActiveId(null);
  };

  const saveWiki = async () => {
    if (!activeId) return;
    const payload = wikiDocHtml;
    const nextTitle = clampTitle(wikiTitle);
    const nextSlug = normalizeWikiSlugInput(wikiSlug);

    const current = wikis.find((w) => w.id === activeId) || null;
    // Check uniqueness only when creating a brand-new wiki.
    if (current?.operation === "create" && nextSlug) {
      try {
        const exists = await checkWikiSlugExists(nextSlug);
        if (exists) {
          setWikiSaveError("Slug da ton tai. Hay thu slug khac.");
          return;
        }
      } catch (err) {
        const msg = err instanceof Error ? err.message : "Khong check duoc slug.";
        setWikiSaveError(msg);
        return;
      }
    }

    setWikiSaveError(null);
    setWikis((prev) =>
          prev.map((w) =>
            w.id !== activeId
              ? w
              : {
                  ...w,
                  source: w.source,
                  operation: w.operation === "create" ? "create" : "update",
                  title: nextTitle,
                  slug: nextSlug,
                  doc: payload,
            }
      )
    );
    setOpen(false);
  };

  const exportCurrentWikiDoc = useCallback(() => {
    if (!activeId) return;

    const fmt = detectWikiDocStorageFormat(wikiDocHtml);
    const label = fmt === "text" ? "txt" : "html";
    const mime = fmt === "text"
      ? "text/plain;charset=utf-8"
      : "text/html;charset=utf-8";

    const base =
      normalizeWikiSlugInput(wikiSlug) ||
      slugifyWikiTitle(wikiTitle) ||
      String(activeId).slice(0, 8);
    const filename = `${base}.${label}`;

    downloadTextFile(filename, wikiDocHtml || "", mime);
  }, [activeId, wikiDocHtml, wikiSlug, wikiTitle]);

  const openImportPicker = useCallback(() => {
    if (!activeId) return;
    importFileInputRef.current?.click();
  }, [activeId]);

  const handleImportFile = useCallback(
    async (e: React.ChangeEvent<HTMLInputElement>) => {
      const file = e.target.files?.[0] || null;
      // Allow selecting the same file again.
      e.target.value = "";
      if (!file) return;
      if (!activeId) return;

      try {
        // Only accept HTML import to match the current Quill storage format.
        const name = (file.name || "").toLowerCase();
        const isHtml =
          name.endsWith(".html") ||
          name.endsWith(".htm") ||
          String(file.type || "").toLowerCase().includes("text/html");
        if (!isHtml) {
          setWikiSaveError("Chi ho tro import file HTML (.html/.htm).");
          return;
        }

        const text = await file.text();
        const raw = String(text || "").trim();
        if (!raw.length) {
          setWikiDocHtml("");
          setWikiSaveError(null);
          return;
        }
        if (raw[0] !== "<") {
          setWikiSaveError("Noi dung file khong phai HTML hop le.");
          return;
        }
        setWikiDocHtml(raw);
        setWikiSaveError(null);
      } catch (err) {
        const msg = err instanceof Error ? err.message : "Khong import duoc file.";
        setWikiSaveError(msg);
      }
    },
    [activeId]
  );

  const closeWikiLinkModal = useCallback(() => {
    setIsWikiLinkOpen(false);
    setWikiLinkQuery("");
    setWikiLinkError(null);
    setGlobalWikiResults([]);
    setIsGlobalWikiSearching(false);
    setGlobalWikiSearchError(null);
    wikiLinkIntentRef.current = null;
  }, []);

  type WikiLinkOption = {
    key: string;
    title: string;
    slug: string;
    source: "local" | "global";
  };

  const localWikiLinkCandidates = useMemo<WikiLinkOption[]>(() => {
    if (!isWikiLinkOpen) return [];
    const q = wikiLinkQuery.trim().toLowerCase();
    const active = wikiLinkIntentRef.current?.activeWikiId ?? activeId;

    const base = (wikis || [])
      .filter((w) => w && typeof w.id === "string")
      .filter((w) => w.id !== active)
      // Link value must be slug.
      .filter((w) => typeof w.slug === "string" && w.slug.trim().length > 0);

    const filtered = (() => {
      if (!q.length) return base;
      if (wikiLinkSearchMode === "slug") {
        return base.filter((w) => String(w.slug || "").toLowerCase().includes(q));
      }
      return base.filter((w) => (w.title || "").toLowerCase().includes(q));
    })();

    return filtered.slice(0, 20).map((w) => ({
      key: `local:${w.id}`,
      title: (w.title || "").trim() || "Untitled wiki",
      slug: String(w.slug).trim(),
      source: "local",
    }));
  }, [activeId, isWikiLinkOpen, wikiLinkQuery, wikiLinkSearchMode, wikis]);

  useEffect(() => {
    if (!isWikiLinkOpen) return;

    const keyword = wikiLinkQuery.trim();
    if (!keyword.length) {
      setGlobalWikiResults([]);
      setIsGlobalWikiSearching(false);
      setGlobalWikiSearchError(null);
      return;
    }

    let disposed = false;
    const requestId = ++globalWikiSearchRequestRef.current;
    const timeoutId = window.setTimeout(async () => {
      setIsGlobalWikiSearching(true);
      setGlobalWikiSearchError(null);
      try {
        const rows =
          wikiLinkSearchMode === "slug"
            ? (() => fetchWikiBySlug(keyword))()
            : (() => searchWikisByTitle(keyword, { limit: 12 }))();

        const resolved = await rows;
        if (disposed || requestId !== globalWikiSearchRequestRef.current) return;

        const list = Array.isArray(resolved) ? resolved : resolved ? [resolved] : [];
        setGlobalWikiResults(list);
      } catch (err) {
        if (disposed || requestId !== globalWikiSearchRequestRef.current) return;
        console.error("Search global wikis failed", err);
        setGlobalWikiResults([]);
        setGlobalWikiSearchError("Khong search duoc wiki tren server.");
      } finally {
        if (!disposed && requestId === globalWikiSearchRequestRef.current) {
          setIsGlobalWikiSearching(false);
        }
      }
    }, 260);

    return () => {
      disposed = true;
      window.clearTimeout(timeoutId);
    };
  }, [isWikiLinkOpen, wikiLinkQuery, wikiLinkSearchMode]);

  const globalWikiLinkCandidates = useMemo<WikiLinkOption[]>(() => {
    if (!isWikiLinkOpen) return [];
    const active = wikiLinkIntentRef.current?.activeWikiId ?? activeId;
    const activeSlug = (wikis || []).find((w) => w.id === active)?.slug ?? null;
    const normalizedActiveSlug = typeof activeSlug === "string" ? activeSlug.trim() : "";

    const out: WikiLinkOption[] = [];
    for (const row of globalWikiResults || []) {
      const slug = typeof row?.slug === "string" ? row.slug.trim() : "";
      if (!slug.length) continue;
      if (normalizedActiveSlug && slug === normalizedActiveSlug) continue;
      out.push({
        key: `global:${row.id || slug}`,
        title: (row.title || "").trim() || "Untitled wiki",
        slug,
        source: "global",
      });
    }
    return out.slice(0, 20);
  }, [activeId, globalWikiResults, isWikiLinkOpen, wikis]);

  const wikiLinkCandidates = useMemo<WikiLinkOption[]>(() => {
    const localSlugs = new Set(localWikiLinkCandidates.map((w) => w.slug));
    const dedupedGlobal = globalWikiLinkCandidates.filter((w) => !localSlugs.has(w.slug));
    return [...localWikiLinkCandidates, ...dedupedGlobal];
  }, [globalWikiLinkCandidates, localWikiLinkCandidates]);

  const applyWikiLink = useCallback((target: WikiLinkOption) => {
    const intent = wikiLinkIntentRef.current;
    const quill = intent?.quill;
    if (!quill) return;

    const slug = target.slug.trim();

    const range = intent?.range ?? quill.getSelection?.() ?? null;
    if (!range) {
      setWikiLinkError("Khong lay duoc vi tri selection trong editor.");
      return;
    }

    // Restore selection to ensure format applies to the expected range.
    quill.setSelection?.(range.index, range.length, "silent");

    if (range.length > 0) {
      quill.formatText?.(range.index, range.length, "link", slug, "user");
      closeWikiLinkModal();
      return;
    }

    // No selection: insert the wiki title (or slug) and link it.
    const label = (target.title || "").trim() || slug;
    quill.insertText?.(range.index, label, { link: slug }, "user");
    quill.setSelection?.(range.index + label.length, 0, "silent");
    closeWikiLinkModal();
  }, [closeWikiLinkModal]);

  const applyMissingWikiLink = useCallback(() => {
    const intent = wikiLinkIntentRef.current;
    const quill = intent?.quill;
    if (!quill) return;

    const href = "__missing__";
    const range = intent?.range ?? quill.getSelection?.() ?? null;
    if (!range) {
      setWikiLinkError("Khong lay duoc vi tri selection trong editor.");
      return;
    }

    quill.setSelection?.(range.index, range.length, "silent");

    if (range.length > 0) {
      quill.formatText?.(range.index, range.length, "link", href, "user");
      closeWikiLinkModal();
      return;
    }

    // No selection: insert query text (best effort) and mark it as a missing link.
    const label = wikiLinkQuery.trim().slice(0, 120) || "link";
    quill.insertText?.(range.index, label, { link: href }, "user");
    quill.setSelection?.(range.index + label.length, 0, "silent");
    closeWikiLinkModal();
  }, [closeWikiLinkModal, wikiLinkQuery]);

  const removeWikiLink = useCallback(() => {
    const intent = wikiLinkIntentRef.current;
    const quill = intent?.quill;
    if (!quill) return;
    const range = intent?.range ?? quill.getSelection?.() ?? null;
    if (!range) return;
    quill.setSelection?.(range.index, range.length, "silent");
    if (range.length > 0) {
      quill.formatText?.(range.index, range.length, "link", false, "user");
    } else {
      quill.format?.("link", false, "user");
    }
    closeWikiLinkModal();
  }, [closeWikiLinkModal]);

  // Keep handler ref updated while keeping modules object stable.
  wikiLinkHandlerRef.current = (quill: QuillLike | null | undefined) => {
    if (!quill) return;
    const range = quill.getSelection?.() ?? null;
    // Try to read current link format (if any) from the selection.
    const existingHref =
      range && (quill.getFormat?.(range)?.link ?? quill.getFormat?.(range.index, range.length)?.link) || null;

    wikiLinkIntentRef.current = {
      quill,
      range,
      activeWikiId: activeId,
      existingHref: typeof existingHref === "string" ? existingHref : null,
    };

    // Seed query with selected text (best effort).
    const selectedText =
      range && range.length > 0 ? String(quill.getText?.(range.index, range.length) || "").trim() : "";
    setWikiLinkQuery(selectedText.slice(0, 80));
    setWikiLinkError(null);
    setIsWikiLinkOpen(true);
  };

  const quillModules = useMemo(() => {
    return {
      toolbar: {
        container: QUILL_TOOLBAR,
        handlers: {
          // NOTE: use function() to preserve Quill toolbar `this` binding.
          link: function (this: { quill?: QuillLike }) {
            wikiLinkHandlerRef.current(this?.quill);
          },
        },
      },
      blotFormatter: {},
    };
  }, []);

  return (
    <div
      style={{
        padding: "10px",
        background: "#0b1220",
        borderRadius: "8px",
        border: "1px solid #1f2937",
      }}
    >
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px" }}>
        <div style={{ fontWeight: 700, fontSize: "14px" }}>Wiki</div>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <div style={{ fontSize: "12px", color: "#94a3b8" }}>{wikis.length}</div>
          <button
            type="button"
            onClick={() => setCollapsed((v) => !v)}
            title={collapsed ? "Mo panel" : "Thu gon panel"}
            aria-label={collapsed ? "Mo panel Wiki" : "Thu gon panel Wiki"}
            style={{
              width: 26,
              height: 26,
              borderRadius: 6,
              border: "1px solid #334155",
              background: "#0b1220",
              color: "#e2e8f0",
              cursor: "pointer",
              display: "inline-flex",
              alignItems: "center",
              justifyContent: "center",
              flex: "0 0 auto",
            }}
          >
            {collapsed ? <PlusIcon /> : <MinusIcon />}
          </button>
        </div>
      </div>

      {collapsed ? null : wikis.length ? (
        <div style={{ marginTop: "10px", display: "grid", gap: "6px", maxHeight: 250, overflowY: "auto", paddingRight: 4 }}>
          {wikis.map((w) => (
            <div
              key={w.id}
              style={{
                display: "flex",
                alignItems: "center",
                gap: "8px",
                padding: "8px",
                borderRadius: "6px",
                border: "1px solid #1f2937",
                background: "transparent",
              }}
            >
              <button
                type="button"
                onClick={() => {
                  setActiveId(w.id);
                  setOpen(true);
                }}
                style={{
                  flex: 1,
                  textAlign: "left",
                  border: "none",
                  background: "transparent",
                  color: "#e5e7eb",
                  cursor: "pointer",
                }}
                title={w.title}
              >
                <span style={{ display: "flex", alignItems: "center", gap: 6, minWidth: 0 }}>
                  <span
                    style={{
                      fontSize: "12px",
                      whiteSpace: "nowrap",
                      overflow: "hidden",
                      textOverflow: "ellipsis",
                    }}
                  >
                    {w.title}
                  </span>
                  {isNewWiki(w) ? <NewBadge /> : null}
                </span>
              </button>
              <button
                type="button"
                onClick={() => removeWiki(w.id)}
                style={{
                  display: "inline-flex",
                  alignItems: "center",
                  justifyContent: "center",
                  width: 22,
                  height: 22,
                  borderRadius: 6,
                  border: "1px solid #334155",
                  background: "#0b1220",
                  cursor: "pointer",
                  flex: "0 0 auto",
                }}
                title="Xóa wiki khỏi dự án"
                aria-label={`Xóa wiki ${w.id}`}
              >
                <TrashIcon />
              </button>
            </div>
          ))}

        </div>
      ) : (
        <div style={{ marginTop: "10px", fontSize: "12px", color: "#94a3b8" }}>
          No wiki yet for this project.
        </div>
      )}

      {collapsed ? null : (
      <div
        style={{
          marginTop: "10px",
          display: "grid",
          gap: "8px",
          border: "1px solid #1e3a8a",
          borderRadius: "8px",
          padding: "8px",
          background: "#0f172a",
        }}
      >
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
          <div style={{ color: "#bfdbfe", fontWeight: 700, fontSize: "12px" }}>
            Tạo wiki mới
          </div>
          <button
            type="button"
            onClick={() =>
              setIsCreateOpen((v) => {
                const next = !v;
                if (next) {
                  setCreateError(null);
                  setIsCheckingCreateSlug(false);
                  setCreateSlugTouched(false);
                }
                return next;
              })
            }
            title={isCreateOpen ? "Dong" : "Mo"}
            aria-label={isCreateOpen ? "Dong tao wiki" : "Mo tao wiki"}
            style={{
              width: 26,
              height: 26,
              borderRadius: 6,
              border: "1px solid #334155",
              background: "#0b1220",
              color: "#e2e8f0",
              cursor: "pointer",
              display: "inline-flex",
              alignItems: "center",
              justifyContent: "center",
              flex: "0 0 auto",
            }}
          >
            {isCreateOpen ? <CloseIcon /> : <PlusIcon />}
          </button>
        </div>

        {isCreateOpen ? (
          <>
            <input
              value={createTitle}
              onChange={(e) => {
                const nextTitle = e.target.value;
                setCreateTitle(nextTitle);
                setCreateError(null);
                if (!createSlugTouched) {
                  setCreateSlug(slugifyWikiTitle(nextTitle));
                }
              }}
              placeholder="Tieu de wiki"
              disabled={isCheckingCreateSlug}
              style={{
                width: "100%",
                borderRadius: "6px",
                border: "1px solid #334155",
                background: "#111827",
                color: "#f8fafc",
                padding: "6px 8px",
                fontSize: "13px",
              }}
            />
            <input
              value={createSlug}
              onChange={(e) => {
                setCreateSlugTouched(true);
                setCreateSlug(e.target.value);
                setCreateError(null);
              }}
              placeholder="Slug"
              disabled={isCheckingCreateSlug}
              style={{
                width: "100%",
                borderRadius: "6px",
                border: "1px solid #334155",
                background: "#111827",
                color: "#f8fafc",
                padding: "6px 8px",
                fontSize: "13px",
              }}
            />
            <button
              type="button"
              onClick={handleCreateWikiFromPanel}
              disabled={isCheckingCreateSlug}
              style={{
                border: "none",
                borderRadius: "6px",
                padding: "7px 8px",
                cursor: isCheckingCreateSlug ? "not-allowed" : "pointer",
                background: "#2563eb",
                color: "#ffffff",
                fontWeight: 600,
                opacity: isCheckingCreateSlug ? 0.7 : 1,
              }}
            >
              Tạo wiki mới
            </button>
            {createError ? (
              <div style={{ color: "#fca5a5", fontSize: 12 }}>
                {createError}
              </div>
            ) : null}
          </>
        ) : null}
      </div>
      )}

      <Modal
        isOpen={open}
        onClose={() => setOpen(false)}
        showCloseButton={false}
        // Defensive: even if Modal defaults change, keep wiki popup free of the "X" close button.
        className="max-w-[1100px] m-4 [&>button]:hidden"
      >
        <div className="p-6 bg-white rounded-3xl dark:bg-gray-900 max-h-[calc(100vh-2rem)] overflow-hidden flex flex-col">
          <input
            ref={importFileInputRef}
            type="file"
            accept=".html,.htm,text/html"
            onChange={handleImportFile}
            style={{ display: "none" }}
          />
          <div className="flex items-center justify-between gap-4">
            <div className="min-w-0">
              <div className="text-xs text-gray-500 dark:text-gray-400">Project</div>
              <div className="text-sm font-mono break-all text-gray-700 dark:text-gray-200">{projectId}</div>
            </div>
            <div className="flex gap-2">
              <Button
                size="sm"
                variant="outline"
                onClick={openImportPicker}
                disabled={!activeId}
                title="Import HTML"
              >
                Import
              </Button>
              <Button
                size="sm"
                variant="outline"
                onClick={exportCurrentWikiDoc}
                disabled={!activeId}
                title={`Export ${wikiDocStorageFormat.toUpperCase()}`}
              >
                Export {wikiDocStorageFormat.toUpperCase()}
              </Button>
              <Button size="sm" variant="outline" onClick={() => setOpen(false)}>
                Cancel
              </Button>
              <Button size="sm" className="bg-brand-500 hover:bg-brand-600 text-white" onClick={saveWiki} disabled={!activeId}>
                Save
              </Button>
            </div>
          </div>

          <div className="mt-5 grid grid-cols-1 lg:grid-cols-4 gap-6 flex-1 min-h-0 overflow-auto">

            <div className="lg:col-span-5">
              <div className="grid grid-cols-1 gap-3">
                <div>
                  <Label>Title</Label>
                  <input
                    value={wikiTitle}
                    onChange={(e) => setWikiTitle(e.target.value)}
                    className="h-11 w-full rounded-xl border border-gray-200 bg-transparent px-4 py-2.5 text-sm text-gray-800 outline-none focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 dark:border-gray-800 dark:text-white/90 dark:focus:border-brand-800"
                    placeholder="Wiki title"
                    disabled={!activeId}
                  />
                </div>
                <div>
                  <Label>Slug</Label>
                  <input
                    value={wikiSlug}
                    onChange={(e) => setWikiSlug(e.target.value)}
                    className="h-11 w-full rounded-xl border border-gray-200 bg-transparent px-4 py-2.5 text-sm text-gray-800 outline-none focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 dark:border-gray-800 dark:text-white/90 dark:focus:border-brand-800"
                    placeholder="wiki-slug"
                    disabled={!activeId}
                  />
                </div>
                {wikiSaveError ? (
                  <div className="text-xs text-red-600 dark:text-red-300">
                    {wikiSaveError}
                  </div>
                ) : null}

                <div className="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-[#0d1117] overflow-hidden">
                  <ReactQuillEditor
                    theme="snow"
                    value={wikiDocHtml}
                    onChange={(content: string) => setWikiDocHtml(content)}
                    modules={quillModules}
                    className="min-h-[320px] uhm-wiki-quill"
                    placeholder="Nhap noi dung wiki..."
                    readOnly={!activeId}
                  />
                </div>
              </div>
            </div>
          </div>
        </div>
      </Modal>

      <style jsx global>{`
        /* Quill editor content is inheriting a light-on-dark color in some themes.
           Force paragraph/text to be readable on the wiki editor's (light) background. */
        .uhm-wiki-quill .ql-editor {
          color: #000 !important;
        }
        .uhm-wiki-quill .ql-editor p {
          color: #000 !important;
        }
        /* Differentiate missing links vs real links inside the editor. */
        .uhm-wiki-quill .ql-editor a {
          text-decoration: underline;
          text-underline-offset: 2px;
        }
        .uhm-wiki-quill .ql-editor a[href="__missing__"],
        .uhm-wiki-quill .ql-editor a:not([href]),
        .uhm-wiki-quill .ql-editor a[href=""] {
          color: #dc2626 !important;
        }
        .uhm-wiki-quill .ql-editor a[href]:not([href=""]):not([href="__missing__"]) {
          color: #2563eb !important;
        }
      `}</style>

      <Modal
        isOpen={isWikiLinkOpen}
        onClose={closeWikiLinkModal}
        className="max-w-[620px] p-6"
      >
        <div className="grid gap-4">
          <div>
            <div className="text-base font-semibold text-gray-900 dark:text-gray-100">Link wiki</div>
          </div>

          <div>
            <Label>Search</Label>
            <div className="flex items-center gap-2">
              <input
                value={wikiLinkQuery}
                onChange={(e) => setWikiLinkQuery(e.target.value)}
                className="h-11 flex-1 min-w-0 rounded-xl border border-gray-200 bg-transparent px-4 py-2.5 text-sm text-gray-800 outline-none focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 dark:border-gray-800 dark:text-white/90 dark:focus:border-brand-800"
                placeholder={wikiLinkSearchMode === "slug" ? "Nhap slug..." : "Nhap title wiki..."}
                autoFocus
              />
              <select
                value={wikiLinkSearchMode}
                onChange={(e) => setWikiLinkSearchMode(e.target.value === "slug" ? "slug" : "title")}
                className="h-11 rounded-xl border border-gray-200 bg-transparent px-3 text-sm text-gray-800 outline-none focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 dark:border-gray-800 dark:text-white/90 dark:focus:border-brand-800"
                aria-label="Search mode"
              >
                <option value="title">Title</option>
                <option value="slug">Slug</option>
              </select>
            </div>
            {wikiLinkError ? (
              <div className="mt-2 text-xs text-red-600 dark:text-red-300">{wikiLinkError}</div>
            ) : null}
            {globalWikiSearchError ? (
              <div className="mt-2 text-xs text-red-600 dark:text-red-300">{globalWikiSearchError}</div>
            ) : null}
          </div>

          <div className="max-h-[320px] overflow-auto rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-[#0d1117]">
            <div className="p-2 grid gap-1">
              {isGlobalWikiSearching ? (
                <div className="px-3 py-2 text-xs text-gray-500 dark:text-gray-400">Searching…</div>
              ) : null}
              {wikiLinkCandidates.map((w) => (
                <button
                  key={w.key}
                  type="button"
                  onClick={() => applyWikiLink(w)}
                  className="w-full text-left rounded-lg border border-transparent hover:border-gray-200 dark:hover:border-gray-800 hover:bg-gray-50 dark:hover:bg-white/[0.03] px-3 py-2 transition"
                  title={w.slug || undefined}
                >
                  <div className="flex items-center gap-2">
                    <div className="flex-1 min-w-0">
                      <div className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
                        {(w.title || "").trim() || "Untitled wiki"}
                      </div>
                      <div className="text-[11px] text-gray-500 dark:text-gray-400 truncate">
                        {String(w.slug)}
                      </div>
                    </div>
                    <span
                      className={`text-[11px] font-semibold px-2 py-0.5 rounded-full border ${
                        w.source === "local"
                          ? "border-emerald-300/60 text-emerald-600 dark:text-emerald-300"
                          : "border-blue-300/60 text-blue-600 dark:text-blue-300"
                      }`}
                    >
                      {w.source}
                    </span>
                  </div>
                </button>
              ))}
              {wikiLinkCandidates.length === 0 ? (
                <div className="px-3 py-4 text-sm text-gray-500 dark:text-gray-400">
                  Khong tim thay wiki phu hop (hoac cac wiki khac chua co slug).
                </div>
              ) : null}
            </div>
          </div>

          <div className="flex items-center justify-end gap-2">
            <Button size="sm" variant="outline" onClick={applyMissingWikiLink}>
              Empty link
            </Button>
            {wikiLinkIntentRef.current?.existingHref ? (
              <Button size="sm" variant="outline" onClick={removeWikiLink}>
                Remove link
              </Button>
            ) : null}
            <Button size="sm" variant="outline" onClick={closeWikiLinkModal}>
              Cancel
            </Button>
          </div>
        </div>
      </Modal>
    </div>
  );
}

function isNewWiki(wiki: WikiSnapshot | null | undefined): boolean {
  return wiki?.source === "inline" && wiki?.operation === "create";
}

function PlusIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path d="M12 5v14M5 12h14" stroke="#e2e8f0" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

function MinusIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path d="M5 12h14" stroke="#e2e8f0" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

function CloseIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path d="M6 6l12 12M18 6L6 18" stroke="#e2e8f0" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

function TrashIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path
        d="M19 7l-.867 12.142A2 2 0 0 1 16.138 21H7.862a2 2 0 0 1-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v3M4 7h16"
        stroke="#f87171"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

const QUILL_TOOLBAR = [
  [{ header: [1, 2, 3, false] }],
  [{ align: [] }, { align: "center" }, { align: "right" }],
  ["bold", "italic", "underline", "strike"],
  [{ list: "ordered" }, { list: "bullet" }],
  ["blockquote", "code-block"],
  ["link", "image"],
  ["clean"],
];

function normalizeWikiDocForQuill(doc: string | null): string {
  const raw = (doc || "").trim();
  if (!raw.length) return "";

  if (raw[0] === "<") return raw;

  return `<p>${escapeHtml(raw).replace(/\n/g, "<br/>")}</p>`;
}

function normalizeWikiSlugInput(raw: string): string | null {
  const s = raw.trim();
  return s.length ? s : null;
}

function slugifyWikiTitle(raw: string): string {
  const input = String(raw || "").trim();
  if (!input.length) return "";
  return input
    .toLowerCase()
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+/, "")
    .replace(/-+$/, "")
    .slice(0, 80);
}

function escapeHtml(input: string): string {
  return input
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll("\"", "&quot;")
    .replaceAll("'", "&#39;");
}

type WikiDocStorageFormat = "html" | "text";

function detectWikiDocStorageFormat(doc: string): WikiDocStorageFormat {
  const raw = String(doc || "").trim();
  if (!raw.length) return "html";
  return raw[0] === "<" ? "html" : "text";
}

function downloadTextFile(filename: string, contents: string, mime: string): void {
  if (typeof window === "undefined") return;
  const safeName = String(filename || "export.txt").replace(/[\\/]+/g, "_");
  const blob = new Blob([contents], { type: mime || "text/plain;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = safeName;
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  a.remove();
  window.setTimeout(() => URL.revokeObjectURL(url), 0);
}
