import { useEffect, useMemo, useState, createContext, useContext } from "react";
import { Link, useLocation } from "wouter";
import { Marked } from "marked";
import DOMPurify from "dompurify";
import { useTranslation } from "react-i18next";
import { Skeleton } from "@/components/ui/skeleton";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ThemeToggle } from "@/components/theme-toggle";
import { LanguageSwitcher } from "@/components/language-switcher";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Sheet,
  SheetContent,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
import { useGetVersion, getGetVersionQueryKey } from "@workspace/api-client-react";
import {
  ArrowLeft,
  BookOpen,
  CalendarDays,
  ExternalLink,
  FileText,
  GitBranch,
  Library,
  Menu,
  Search,
  Server,
  Tag,
  Wrench,
  type LucideIcon,
} from "lucide-react";

const DOCS_BASE = `${import.meta.env.BASE_URL.replace(/\/$/, "")}/docs`;
const REPO_URL = "https://git.kubebase.de/admin/tool-evaluator";

// Active docs version (null = current docs). Shared via context so every
// sub-view builds versioned links and file paths.
const DocsVersionContext = createContext<string | null>(null);
const useDocsVersion = () => useContext(DocsVersionContext);

// Full in-app URL for a docs path, prefixed with the active version.
function docsHref(version: string | null, path: string) {
  return version === null ? `/docs/${path}` : `/docs/${version}/${path}`;
}

// Static file path under /docs, prefixed with the versioned snapshot dir.
function docsFile(version: string | null, relPath: string) {
  return version === null ? relPath : `versions/${version}/${relPath}`;
}

// Prefer the English file/title variant when the active UI language is English.
function useDocsLocale() {
  const { i18n } = useTranslation();
  return (i18n.language ?? "en").toLowerCase().startsWith("en") ? "en" : "de";
}

function localizedFile(file: string, fileEn: string | null, locale: "en" | "de"): string {
  return locale === "en" && fileEn ? fileEn : file;
}

function localizedTitle(title: string, titleEn: string | null, locale: "en" | "de"): string {
  return locale === "en" && titleEn ? titleEn : title;
}

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

type FieldType = { kind: "ref" | "type" | "array"; value: string };

type Parameter = {
  name: string;
  in: string;
  required: boolean;
  type: FieldType;
  description: string;
  constraints: string;
};

type Endpoint = {
  operationId: string;
  method: string;
  path: string;
  summary: string;
  description: string;
  parameters: Parameter[];
  requestBody: { required: boolean; schema: FieldType } | null;
  responses: { status: string; description: string; schema: FieldType }[];
};

type TagGroup = { name: string; description: string; endpoints: Endpoint[] };

type Field = {
  name: string;
  type: FieldType;
  required: boolean;
  description: string;
  constraints: string;
};

type SchemaModel = { name: string; description: string; fields: Field[] };

type Reference = { tags: TagGroup[]; schemas: SchemaModel[] };

type ReleaseDoc = {
  version: string;
  file: string;
  fileEn: string | null;
  title: string;
  titleEn: string | null;
  date: string | null;
  hasReference: boolean;
  hasHandbook: boolean;
};

type HandbookPage = {
  slug: string;
  file: string;
  fileEn: string | null;
  title: string;
  titleEn: string | null;
  order: number;
};

type SearchEntry = { title: string; href: string; kind: string; text: string };

type Heading = { id: string; text: string; level: number };

// ---------------------------------------------------------------------------
// Data hooks
// ---------------------------------------------------------------------------

function fetchJson<T>(url: string): Promise<T> {
  return fetch(url).then((res) => {
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return res.json() as Promise<T>;
  });
}

function useJson<T>(url: string | null) {
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState(false);

  useEffect(() => {
    if (!url) {
      setData(null);
      setError(false);
      return;
    }
    let cancelled = false;
    setData(null);
    setError(false);
    fetchJson<T>(url)
      .then((d) => {
        if (!cancelled) setData(d);
      })
      .catch(() => {
        if (!cancelled) setError(true);
      });
    return () => {
      cancelled = true;
    };
  }, [url]);

  return { data, error };
}

function useMarkdown(file: string | null) {
  const [state, setState] = useState<{ html: string; headings: Heading[] } | null>(null);
  const [error, setError] = useState(false);

  useEffect(() => {
    if (!file) {
      setState(null);
      setError(false);
      return;
    }
    let cancelled = false;
    setState(null);
    setError(false);
    fetch(`${DOCS_BASE}/${file}`)
      .then((res) => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.text();
      })
      .then((md) => {
        const rendered = renderMarkdown(md);
        if (!cancelled) setState(rendered);
      })
      .catch(() => {
        if (!cancelled) setError(true);
      });
    return () => {
      cancelled = true;
    };
  }, [file]);

  return { ...state, error };
}

// ---------------------------------------------------------------------------
// Markdown rendering with heading anchors + TOC
// ---------------------------------------------------------------------------

function slugify(text: string): string {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, "")
    .replace(/[\s_]+/g, "-")
    .replace(/-+/g, "-");
}

function renderMarkdown(md: string): { html: string; headings: Heading[] } {
  const headings: Heading[] = [];
  const seen = new Map<string, number>();

  const renderer = {
    heading({ tokens, depth }: { tokens: { raw: string; text?: string }[]; depth: number }) {
      const text = tokens.map((t) => t.text ?? t.raw).join("");
      let id = slugify(text);
      const count = seen.get(id) ?? 0;
      seen.set(id, count + 1);
      if (count > 0) id = `${id}-${count}`;
      headings.push({ id, text, level: depth });
      return `<h${depth} id="${id}"><a href="#${id}" class="docs-anchor" aria-hidden="true"></a>${text}</h${depth}>`;
    },
  };

  const marked = new Marked({ gfm: true, async: false, renderer });
  const html = marked.parse(md) as string;
  return { html: DOMPurify.sanitize(html), headings };
}

// ---------------------------------------------------------------------------
// Version resolution
// ---------------------------------------------------------------------------

function parseDocsPath(location: string) {
  const rest = location.replace(/^\/docs\/?/, "");
  const segments = rest.split("/").filter(Boolean);
  const versionRe = /^v\d+\.\d+\.\d+$/;
  if (segments.length > 0 && versionRe.test(segments[0])) {
    return { version: segments[0], path: segments.slice(1) };
  }
  return { version: null, path: segments };
}

// ---------------------------------------------------------------------------
// Field type helpers
// ---------------------------------------------------------------------------

function fieldTypeLabel(t: FieldType): string {
  if (t.kind === "array") return `${t.value}[]`;
  return t.value;
}

function isLinkableType(t: FieldType): boolean {
  return t.kind === "ref" || (t.kind === "array" && /^[A-Z]/.test(t.value));
}

function resolveTypeHref(t: FieldType): string | null {
  const version = useDocsVersion();
  const href = (v: string | null) =>
    v === null ? `/docs/reference/schemas/${t.value}` : `/docs/${v}/reference/schemas/${t.value}`;
  if (t.kind === "ref") return href(version);
  if (t.kind === "array" && /^[A-Z]/.test(t.value)) return href(version);
  return null;
}

const METHOD_STYLES: Record<string, string> = {
  GET: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-400",
  POST: "bg-blue-500/15 text-blue-700 dark:text-blue-400",
  PATCH: "bg-amber-500/15 text-amber-700 dark:text-amber-400",
  PUT: "bg-indigo-500/15 text-indigo-700 dark:text-indigo-400",
  DELETE: "bg-red-500/15 text-red-700 dark:text-red-400",
};

// ---------------------------------------------------------------------------
// Sub-views
// ---------------------------------------------------------------------------

function DocsHeader({
  versions,
  activeVersion,
  onVersionChange,
  onSearchChange,
}: {
  versions: ReleaseDoc[];
  activeVersion: string | null;
  onVersionChange: (v: string | null) => void;
  onSearchChange?: (q: string) => void;
}) {
  const { t } = useTranslation();
  return (
    <div className="flex flex-wrap items-center justify-between gap-3 border-b pb-4">
      <div className="flex items-center gap-2 min-w-0">
        <BookOpen className="h-5 w-5 text-primary shrink-0" />
        <h1 className="text-xl font-bold tracking-tight truncate">{t("docs.title")}</h1>
      </div>
      <div className="flex items-center gap-2">
        {onSearchChange && (
          <div className="relative hidden md:block">
            <Search className="h-4 w-4 absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground" />
            <Input
              placeholder={t("docs.searchPlaceholder")}
              onChange={(e) => onSearchChange(e.target.value)}
              className="pl-8 w-52"
              data-testid="input-docs-search"
            />
          </div>
        )}
        {versions.length > 0 && (
          <Select
            value={activeVersion ?? "latest"}
            onValueChange={(v) => onVersionChange(v === "latest" ? null : v)}
          >
            <SelectTrigger className="w-[140px]" data-testid="select-docs-version">
              <SelectValue placeholder={t("docs.version")} />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="latest">
                {activeVersion === null ? `✓ ${t("docs.latest")}` : t("docs.latest")}
              </SelectItem>
              {versions.map((v) => (
                <SelectItem key={v.version} value={v.version}>
                  {activeVersion === v.version ? `✓ ${v.version}` : v.version}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        )}
        <Button variant="ghost" size="icon" asChild data-testid="button-docs-repo" title={t("docs.repo")}>
          <a href={REPO_URL} target="_blank" rel="noreferrer">
            <GitBranch className="h-4 w-4" />
          </a>
        </Button>
      </div>
    </div>
  );
}

function DocsNav({
  version,
  handbook,
  reference,
  versions,
}: {
  version: string | null;
  handbook: HandbookPage[] | null;
  reference: Reference | null;
  versions: ReleaseDoc[];
}) {
  const [location] = useLocation();
  const { t } = useTranslation();
  const locale = useDocsLocale();

  const navLink = (href: string) => {
    const active = location === href || (href !== "/docs" && location.startsWith(href));
    return active;
  };

  const groups: { label: string; icon: LucideIcon; items: { href: string; label: string; active: boolean }[] }[] = [];

  if (handbook && handbook.length > 0) {
    groups.push({
      label: t("docs.guides"),
      icon: BookOpen,
      items: handbook.map((p) => ({
        href: docsHref(version, `handbook/${p.slug}`),
        label: localizedTitle(p.title, p.titleEn, locale),
        active: navLink(docsHref(version, `handbook/${p.slug}`)),
      })),
    });
  }

  if (reference) {
    groups.push({
      label: t("docs.endpoints"),
      icon: Server,
      items: reference.tags.map((tag) => ({
        href: docsHref(version, `reference/endpoints/${tag.name}`),
        label: tag.name,
        active: navLink(docsHref(version, `reference/endpoints/${tag.name}`)),
      })),
    });
    groups.push({
      label: t("docs.schemas"),
      icon: Library,
      items: reference.schemas.map((s) => ({
        href: docsHref(version, `reference/schemas/${s.name}`),
        label: s.name,
        active: navLink(docsHref(version, `reference/schemas/${s.name}`)),
      })),
    });
  }

  groups.push({
    label: t("docs.releases"),
    icon: Tag,
    items: versions.map((v) => ({
      href: `/docs/releases/${v.version}`,
      label: v.version,
      active: navLink(`/docs/releases/${v.version}`),
    })),
  });

  return (
    <nav className="space-y-6" aria-label={t("docs.nav")}>
      {groups.map((group) => (
        <div key={group.label}>
          <div className="mb-1.5 flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
            <group.icon className="h-3.5 w-3.5" />
            {group.label}
          </div>
          <ul className="space-y-0.5">
            {group.items.map((item) => (
              <li key={item.href}>
                <Link
                  href={item.href}
                  className={`block rounded-md px-2 py-1.5 text-sm transition-colors ${
                    item.active
                      ? "bg-accent text-accent-foreground font-medium"
                      : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"
                  }`}
                >
                  <span className="truncate block">{item.label}</span>
                </Link>
              </li>
            ))}
          </ul>
        </div>
      ))}
    </nav>
  );
}

function Toc({ headings, title }: { headings: Heading[]; title?: string }) {
  const [activeId, setActiveId] = useState<string | null>(null);
  const { t } = useTranslation();

  useEffect(() => {
    if (headings.length === 0) return;
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting) setActiveId(entry.target.id);
        }
      },
      { rootMargin: "-80px 0px -70% 0px" },
    );
    for (const h of headings) {
      const el = document.getElementById(h.id);
      if (el) observer.observe(el);
    }
    return () => observer.disconnect();
  }, [headings]);

  if (headings.length === 0) return null;

  return (
    <aside className="hidden xl:block" aria-label={t("docs.onThisPage")}>
      <p className="mb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
        {title ?? t("docs.onThisPage")}
      </p>
      <ul className="space-y-1 border-l">
        {headings.map((h) => (
          <li key={h.id} style={{ paddingLeft: `${Math.min(h.level - 1, 2)}rem` }}>
            <a
              href={`#${h.id}`}
              className={`block border-l -ml-px px-2 py-0.5 text-xs transition-colors ${
                activeId === h.id
                  ? "border-primary text-foreground font-medium"
                  : "border-transparent text-muted-foreground hover:text-foreground"
              }`}
            >
              {h.text}
            </a>
          </li>
        ))}
      </ul>
    </aside>
  );
}

function MarkdownView({ file }: { file: string | null }) {
  const { t } = useTranslation();
  const { html, headings, error } = useMarkdown(file);

  return (
    <div className="flex flex-col lg:flex-row gap-8">
      <div className="flex-1 min-w-0 max-w-3xl">
        {error ? (
          <p className="text-sm text-muted-foreground">{t("docs.noDocs")}</p>
        ) : !html ? (
          <div className="space-y-3">
            <Skeleton className="h-8 w-64" />
            <Skeleton className="h-4 w-full" />
            <Skeleton className="h-4 w-3/4" />
          </div>
        ) : (
          <div
            className="docs-prose prose dark:prose-invert max-w-none"
            dangerouslySetInnerHTML={{ __html: html }}
          />
        )}
      </div>
      <Toc headings={headings ?? []} />
    </div>
  );
}

function FieldTypeChip({ type }: { type: FieldType }) {
  const href = resolveTypeHref(type);
  const label = fieldTypeLabel(type);
  if (href) {
    return (
      <Link href={href} className="inline-flex">
        <Badge variant="secondary" className="font-mono hover:bg-accent">
          {label}
        </Badge>
      </Link>
    );
  }
  return <Badge variant="secondary" className="font-mono">{label}</Badge>;
}

function FieldTable({ fields }: { fields: Field[] }) {
  const { t } = useTranslation();
  return (
    <div className="overflow-x-auto rounded-lg border">
      <table className="w-full text-sm">
        <thead>
          <tr className="border-b bg-muted/50 text-left text-xs uppercase tracking-wider text-muted-foreground">
            <th className="px-3 py-2 font-semibold">{t("docs.field")}</th>
            <th className="px-3 py-2 font-semibold">{t("docs.type")}</th>
            <th className="px-3 py-2 font-semibold">{t("docs.required")}</th>
            <th className="px-3 py-2 font-semibold">{t("docs.description")}</th>
          </tr>
        </thead>
        <tbody>
          {fields.map((f) => (
            <tr key={f.name} id={f.name} className="border-b last:border-0 align-top">
              <td className="px-3 py-2">
                <a href={`#${f.name}`} className="font-mono text-primary hover:underline">
                  {f.name}
                </a>
              </td>
              <td className="px-3 py-2">
                <FieldTypeChip type={f.type} />
              </td>
              <td className="px-3 py-2">
                {f.required ? (
                  <Badge className="bg-primary/10 text-primary border-primary/20">{t("docs.required")}</Badge>
                ) : (
                  <span className="text-muted-foreground">–</span>
                )}
              </td>
              <td className="px-3 py-2">
                <div className="text-muted-foreground">{f.description}</div>
                {f.constraints && (
                  <div className="mt-0.5 text-xs text-muted-foreground/70 font-mono">{f.constraints}</div>
                )}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

function SchemaView({ schema }: { schema: SchemaModel }) {
  const { t } = useTranslation();
  const headings: Heading[] = schema.fields.map((f) => ({ id: f.name, text: f.name, level: 2 }));
  return (
    <div className="flex flex-col lg:flex-row gap-8">
      <div className="flex-1 min-w-0 max-w-3xl space-y-4">
        <div className="space-y-1">
          <h1 className="text-2xl font-bold tracking-tight font-mono">{schema.name}</h1>
          {schema.description && <p className="text-muted-foreground">{schema.description}</p>}
        </div>
        <FieldTable fields={schema.fields} />
      </div>
      <Toc headings={headings} title={t("docs.fields")} />
    </div>
  );
}

function EndpointTagView({ tag }: { tag: TagGroup }) {
  const { t } = useTranslation();
  const headings: Heading[] = tag.endpoints.map((e) => ({
    id: e.operationId,
    text: `${e.method} ${e.path}`,
    level: 2,
  }));
  return (
    <div className="flex flex-col lg:flex-row gap-8">
      <div className="flex-1 min-w-0 max-w-3xl space-y-8">
        <div className="space-y-1">
          <h1 className="text-2xl font-bold tracking-tight">{tag.name}</h1>
          {tag.description && <p className="text-muted-foreground">{tag.description}</p>}
        </div>
        {tag.endpoints.map((ep) => (
          <section key={ep.operationId} id={ep.operationId} className="scroll-mt-20">
            <div className="mb-2 flex items-center gap-2">
              <Badge className={`font-mono ${METHOD_STYLES[ep.method] ?? "bg-muted text-muted-foreground"}`}>
                {ep.method}
              </Badge>
              <code className="font-mono text-sm">{ep.path}</code>
              <a href={`#${ep.operationId}`} className="ml-auto text-muted-foreground hover:text-foreground">
                <ExternalLink className="h-3.5 w-3.5" />
              </a>
            </div>
            <h2 className="mb-1 text-lg font-semibold">{ep.summary}</h2>
            {ep.description && <p className="mb-3 text-sm text-muted-foreground">{ep.description}</p>}

            {ep.parameters.length > 0 && (
              <div className="mb-3">
                <p className="mb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">{t("docs.parameter")}</p>
                <div className="overflow-x-auto rounded-lg border">
                  <table className="w-full text-sm">
                    <thead>
                      <tr className="border-b bg-muted/50 text-left text-xs uppercase tracking-wider text-muted-foreground">
                        <th className="px-3 py-2 font-semibold">{t("docs.name")}</th>
                        <th className="px-3 py-2 font-semibold">{t("docs.in")}</th>
                        <th className="px-3 py-2 font-semibold">{t("docs.type")}</th>
                        <th className="px-3 py-2 font-semibold">{t("docs.required")}</th>
                        <th className="px-3 py-2 font-semibold">{t("docs.description")}</th>
                      </tr>
                    </thead>
                    <tbody>
                      {ep.parameters.map((p) => (
                        <tr key={`${p.name}-${p.in}`} className="border-b last:border-0">
                          <td className="px-3 py-1.5 font-mono">{p.name}</td>
                          <td className="px-3 py-1.5 text-muted-foreground">{p.in}</td>
                          <td className="px-3 py-1.5"><FieldTypeChip type={p.type} /></td>
                          <td className="px-3 py-1.5">
                            {p.required ? <Badge className="bg-primary/10 text-primary border-primary/20">{t("docs.required")}</Badge> : "–"}
                          </td>
                          <td className="px-3 py-1.5 text-muted-foreground">
                            {p.description}
                            {p.constraints && <span className="block font-mono text-xs">{p.constraints}</span>}
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>
            )}

            {ep.requestBody && (
              <div className="mb-3 rounded-lg border bg-muted/30 p-3">
                <p className="mb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
                  {t("docs.requestBody")} {ep.requestBody.required && <Badge className="ml-1">{t("docs.required")}</Badge>}
                </p>
                <FieldTypeChip type={ep.requestBody.schema} />
              </div>
            )}

            <div className="overflow-x-auto rounded-lg border">
              <table className="w-full text-sm">
                <thead>
                  <tr className="border-b bg-muted/50 text-left text-xs uppercase tracking-wider text-muted-foreground">
                    <th className="px-3 py-2 font-semibold">{t("docs.status")}</th>
                    <th className="px-3 py-2 font-semibold">{t("docs.description")}</th>
                    <th className="px-3 py-2 font-semibold">{t("docs.schema")}</th>
                  </tr>
                </thead>
                <tbody>
                  {ep.responses.map((r) => (
                    <tr key={r.status} className="border-b last:border-0">
                      <td className="px-3 py-1.5 font-mono">{r.status}</td>
                      <td className="px-3 py-1.5 text-muted-foreground">{r.description}</td>
                      <td className="px-3 py-1.5"><FieldTypeChip type={r.schema} /></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </section>
        ))}
      </div>
      <Toc headings={headings} title={t("docs.endpoints")} />
    </div>
  );
}

function ReleasesView({ versions }: { versions: ReleaseDoc[] }) {
  const [, setLocation] = useLocation();
  const { t } = useTranslation();
  const locale = useDocsLocale();
  return (
    <div className="flex flex-col lg:flex-row gap-8">
      <div className="flex-1 min-w-0 max-w-3xl space-y-4">
        <div className="space-y-1">
          <h1 className="text-2xl font-bold tracking-tight">{t("docs.releases")}</h1>
          <p className="text-muted-foreground">{t("docs.subtitle")}</p>
        </div>
        <div className="space-y-2">
          {versions.map((v) => (
            <button
              key={v.version}
              type="button"
              onClick={() => setLocation(`/docs/releases/${v.version}`)}
              className="flex w-full items-center gap-3 rounded-lg border p-3 text-left transition-colors hover:bg-accent/50"
            >
              <FileText className="h-4 w-4 shrink-0 text-primary" />
              <div className="min-w-0 flex-1">
                <div className="flex items-center gap-2">
                  <span className="font-semibold">{localizedTitle(v.title, v.titleEn, locale)}</span>
                  <Badge variant="secondary">{v.version}</Badge>
                </div>
                {v.date && (
                  <span className="mt-0.5 flex items-center gap-1.5 text-xs text-muted-foreground">
                    <CalendarDays className="h-3 w-3" />
                    {new Date(`${v.date}T00:00:00`).toLocaleDateString()}
                  </span>
                )}
              </div>
              {v.hasReference && <Badge className="bg-primary/10 text-primary">{t("docs.reference")}</Badge>}
            </button>
          ))}
        </div>
      </div>
      <Toc headings={[]} />
    </div>
  );
}

function ReleaseNoteView({ version, doc }: { version: string; doc: ReleaseDoc | null }) {
  const locale = useDocsLocale();
  const file = doc ? `releases/${localizedFile(doc.file, doc.fileEn, locale)}` : `releases/${version}.md`;
  return (
    <div className="flex flex-col lg:flex-row gap-8">
      <div className="flex-1 min-w-0 max-w-3xl">
        <div className="mb-4 flex items-center gap-2 text-sm text-muted-foreground">
          <Tag className="h-4 w-4" />
          <span className="font-mono">{version}</span>
          <a
            href={`${REPO_URL}/releases/tag/${version}`}
            target="_blank"
            rel="noreferrer"
            className="inline-flex items-center gap-1 hover:text-foreground"
          >
            <ExternalLink className="h-3.5 w-3.5" />
          </a>
        </div>
        <MarkdownView file={file} />
      </div>
    </div>
  );
}

function HandbookView({ slug, pages }: { slug: string; pages: HandbookPage[] | null }) {
  const version = useDocsVersion();
  const locale = useDocsLocale();
  const page = pages?.find((p) => p.slug === slug);
  const file = page
    ? docsFile(version, `handbook/${localizedFile(page.file, page.fileEn, locale)}`)
    : docsFile(version, `handbook/${slug}.md`);
  return <MarkdownView file={file} />;
}

// ---------------------------------------------------------------------------
// Search overlay
// ---------------------------------------------------------------------------

function useDocsSearch(query: string) {
  const locale = useDocsLocale();
  const indexFile = locale === "en" ? "search.en.json" : "search.json";
  const { data, error } = useJson<SearchEntry[]>(query ? `${DOCS_BASE}/${indexFile}` : null);
  const results = useMemo(() => {
    if (!query.trim() || !data) return [];
    const q = query.trim().toLowerCase();
    return data
      .filter(
        (e) =>
          e.title.toLowerCase().includes(q) ||
          e.text.toLowerCase().includes(q),
      )
      .slice(0, 25);
  }, [query, data]);

  return { results, error };
}

function SearchOverlay({ query, onClose }: { query: string; onClose: () => void }) {
  const { t } = useTranslation();
  const { results } = useDocsSearch(query);
  if (!query.trim()) return null;
  return (
    <div className="mt-3 rounded-lg border bg-card p-2 shadow-md max-h-96 overflow-auto">
      {results.length === 0 ? (
        <p className="px-3 py-2 text-sm text-muted-foreground">{t("docs.noResults")}</p>
      ) : (
        results.map((r) => (
          <Link
            key={r.href}
            href={r.href}
            onClick={onClose}
            className="flex items-start gap-2 rounded-md px-3 py-2 text-sm hover:bg-accent"
          >
            <span className="shrink-0">
              {r.kind === "endpoint" && <Server className="h-4 w-4 text-emerald-500" />}
              {r.kind === "field" && <Library className="h-4 w-4 text-blue-500" />}
              {r.kind === "guide" && <BookOpen className="h-4 w-4 text-amber-500" />}
              {r.kind === "release" && <Tag className="h-4 w-4 text-primary" />}
            </span>
            <span className="min-w-0">
              <span className="block font-medium truncate">{r.title}</span>
              <span className="block text-xs text-muted-foreground truncate">{r.text.slice(0, 80)}</span>
            </span>
          </Link>
        ))
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------

export default function Docs() {
  const [location, setLocation] = useLocation();
  const { t } = useTranslation();
  const [searchQuery, setSearchQuery] = useState("");
  const [navOpen, setNavOpen] = useState(false);

  // Close the mobile nav drawer after navigating.
  useEffect(() => {
    setNavOpen(false);
  }, [location]);

  const { version, path } = useMemo(() => parseDocsPath(location), [location]);

  const { data: versionInfo } = useGetVersion({
    query: { queryKey: getGetVersionQueryKey(), staleTime: Infinity, retry: false },
  });

  const { data: releases, error: releasesError } = useJson<ReleaseDoc[]>(`${DOCS_BASE}/index.json`);

  const activeRelease = releases?.find((r) => r.version === version) ?? null;
  const handbookAvailable = version === null || activeRelease?.hasHandbook;
  const handbookUrl = handbookAvailable ? `${DOCS_BASE}/${docsFile(version, "handbook/index.json")}` : null;
  const { data: handbook, error: handbookError } = useJson<HandbookPage[]>(handbookUrl);

  const isCurrentVersion =
    version === null ||
    (versionInfo?.version && versionInfo.version !== "dev" && version === versionInfo.version) ||
    (version !== null && (!versionInfo?.version || versionInfo.version === "dev"));

  const refUrl = version === null
    ? `${DOCS_BASE}/reference.json`
    : activeRelease?.hasReference
      ? `${DOCS_BASE}/versions/${version}.json`
      : null;

  const { data: reference, error: refError } = useJson<Reference>(refUrl);

  // Current version detection: prefer running version, fallback newest documented
  const currentVersion = useMemo(() => {
    if (releases && releases.length > 0) {
      if (versionInfo?.version && versionInfo.version !== "dev") {
        const match = releases.find((r) => r.version === versionInfo.version);
        if (match) return match.version;
      }
      return releases[0].version;
    }
    return null;
  }, [releases, versionInfo]);

  const handleVersionChange = (v: string | null) => {
    setSearchQuery("");
    if (v === null || v === currentVersion) {
      setLocation("/docs");
      return;
    }
    setLocation(`/docs/${v}`);
  };

  // ---- route resolution ----
  const section = path[0] ?? "home";
  const param = path[1];

  // Version shown in the header dropdown: an explicit first-segment version
  // (/docs/vX.Y.Z) or the releases route (/docs/releases/vX.Y.Z).
  const headerVersion =
    version !== null
      ? version
      : section === "releases" && param
        ? param
        : null;

  let content: React.ReactNode = null;

  if (version !== null && path.length === 0) {
    content =
      handbook && handbook.length > 0 ? (
        <HandbookView slug={handbook[0].slug} pages={handbook} />
      ) : (
        <ReleaseNoteView version={version} doc={activeRelease} />
      );
  } else if (section === "home") {
    content =
      handbook && handbook.length > 0 ? (
        <HandbookView slug={handbook[0].slug} pages={handbook} />
      ) : releases && releases.length > 0 ? (
        <ReleasesView versions={releases} />
      ) : (
        <p className="text-sm text-muted-foreground">{t("docs.noDocs")}</p>
      );
  } else if (section === "handbook" && param) {
    content = <HandbookView slug={param} pages={handbook} />;
  } else if (section === "reference" && param === "endpoints" && path[2]) {
    const tag = reference?.tags.find(
      (tg) => tg.name.toLowerCase() === path[2].toLowerCase(),
    );
    content = tag ? (
      <EndpointTagView tag={tag} />
    ) : refError || (reference && !tag) ? (
      <p className="text-sm text-muted-foreground">{t("docs.noDocs")}</p>
    ) : (
      <Skeleton className="h-64 w-full" />
    );
  } else if (section === "reference" && param === "schemas" && path[2]) {
    const schema = reference?.schemas.find(
      (s) => s.name.toLowerCase() === path[2].toLowerCase(),
    );
    content = schema ? (
      <SchemaView schema={schema} />
    ) : refError || (reference && !schema) ? (
      <p className="text-sm text-muted-foreground">{t("docs.noDocs")}</p>
    ) : (
      <Skeleton className="h-64 w-full" />
    );
  } else if (section === "reference") {
    content = (
      <div className="flex flex-col lg:flex-row gap-8">
        <div className="flex-1 min-w-0 max-w-3xl space-y-4">
          <h1 className="text-2xl font-bold tracking-tight">{t("docs.reference")}</h1>
          <p className="text-muted-foreground">{t("docs.referenceIntro")}</p>
          {!reference && !refError && <Skeleton className="h-64 w-full" />}
          {reference && (
            <>
              <div>
                <h2 className="mb-1 text-lg font-semibold">{t("docs.endpoints")}</h2>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                  {reference.tags.map((tg) => (
                    <Link
                      key={tg.name}
                      href={docsHref(version, `reference/endpoints/${tg.name}`)}
                      className="rounded-lg border p-3 text-sm hover:bg-accent/50"
                    >
                      <span className="font-medium">{tg.name}</span>
                      <span className="block text-xs text-muted-foreground">
                        {tg.endpoints.length} {t("docs.endpoints")}
                      </span>
                    </Link>
                  ))}
                </div>
              </div>
              <div>
                <h2 className="mb-1 text-lg font-semibold">{t("docs.schemas")}</h2>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                  {reference.schemas.map((s) => (
                    <Link
                      key={s.name}
                      href={docsHref(version, `reference/schemas/${s.name}`)}
                      className="rounded-lg border p-3 text-sm font-mono hover:bg-accent/50"
                    >
                      {s.name}
                    </Link>
                  ))}
                </div>
              </div>
            </>
          )}
        </div>
        <Toc headings={[]} />
      </div>
    );
  } else if (section === "releases" && param) {
    content = <ReleaseNoteView version={param} doc={releases?.find((r) => r.version === param) ?? null} />;
  } else if (section === "releases") {
    content = releases ? <ReleasesView versions={releases} /> : <Skeleton className="h-64 w-full" />;
  } else {
    content = <p className="text-sm text-muted-foreground">{t("docs.noDocs")}</p>;
  }

  const showSearch = version === null && section !== "releases";

  return (
    <DocsVersionContext.Provider value={version}>
      <div className="min-h-screen bg-background flex flex-col">
        <header className="border-b bg-card shrink-0">
          <div className="mx-auto max-w-6xl px-4 md:px-6 h-14 flex items-center justify-between gap-3">
            <div className="flex items-center gap-1 min-w-0">
              <Sheet open={navOpen} onOpenChange={setNavOpen}>
                <SheetTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className="lg:hidden -ml-1 shrink-0"
                    title={t("docs.nav")}
                    data-testid="button-docs-nav-mobile"
                  >
                    <Menu className="h-5 w-5" />
                  </Button>
                </SheetTrigger>
                <SheetContent side="left" className="w-80 overflow-y-auto p-4">
                  <SheetTitle className="sr-only">{t("docs.nav")}</SheetTitle>
                  <DocsNav
                    version={version}
                    handbook={handbook}
                    reference={reference}
                    versions={releases ?? []}
                  />
                </SheetContent>
              </Sheet>
              <Link
                href="/"
                className="inline-flex items-center gap-2 text-primary font-bold text-lg min-w-0"
                data-testid="link-docs-back"
              >
                <Wrench className="w-5 h-5 shrink-0" />
                <span className="truncate">toolr</span>
                <span className="hidden md:inline-flex items-center gap-1 text-xs font-normal text-muted-foreground border-l pl-2 ml-1">
                  <ArrowLeft className="w-3.5 h-3.5" />
                  {t("docs.backToApp")}
                </span>
              </Link>
            </div>
            <div className="flex items-center gap-1.5 shrink-0">
              <LanguageSwitcher />
              <ThemeToggle />
            </div>
          </div>
        </header>

        <div className="mx-auto max-w-6xl w-full flex-1 space-y-6 px-4 md:px-6 py-6 pb-12">
          <DocsHeader
            versions={releases ?? []}
            activeVersion={headerVersion}
            onVersionChange={handleVersionChange}
            onSearchChange={showSearch ? setSearchQuery : undefined}
          />

          {showSearch && <SearchOverlay query={searchQuery} onClose={() => setSearchQuery("")} />}

          <div className="grid grid-cols-1 lg:grid-cols-[240px_minmax(0,1fr)] gap-8">
            <aside className="hidden lg:block">
              <div className="sticky top-20 max-h-[calc(100vh-6rem)] overflow-auto">
                <DocsNav
                  version={version}
                  handbook={handbook}
                  reference={reference}
                  versions={releases ?? []}
                />
              </div>
            </aside>
            <div className="min-w-0">{content}</div>
          </div>
        </div>
      </div>
    </DocsVersionContext.Provider>
  );
}
