import { useState, useMemo, useCallback, useEffect, useRef, type ChangeEvent, type FormEvent, type ReactNode } from "react";
import {
  Settings,
  Image,
  Video,
  BookOpen,
  Search,
  Database,
  Save,
  Trash2,
  Plus,
  RotateCcw,
  Languages,
  Link2,
  LogOut,
  LockKeyhole,
  X,
} from "lucide-react";
import { DEFAULT_GUIDE_LOCALES } from "../guideContent";
import {
  DEFAULT_HOME_BG_URLS,
  DEFAULT_KNOWLEDGE_ENTRIES,
  DEFAULT_VIDEO_SOURCE,
  loadGuideOverrides,
  loadHomeBackgrounds,
  loadKnowledgeEntries,
  loadVideoSourceConfig,
  loadWelcome,
  mergeGuideLocales,
  newKnowledgeEntryId,
  type GuideOverrides,
  type KnowledgeEntry,
  type VideoProtocol,
  type VideoSourceConfig,
  type WelcomeMessages,
} from "../kioskStorage";
import { PAGE_CONTENT_INSET } from "../pageContentInset";
import {
  fetchAndApplyKioskBundle,
  pushGuideToServer,
  pushHomeBackgroundsToServer,
  pushKnowledgeToServer,
  pushVideoSourceToServer,
  pushWelcomeToServer,
  resolveKioskMediaUrl,
  uploadKnowledgeMediaFile,
} from "../api/kioskApi";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs";
import { toast } from "sonner";
import { useI18n } from "../i18n";

type AdminTab =
  | "background"
  | "welcome"
  | "guide"
  | "query"
  | "knowledge"
  | "stream";

/** 系统维护「信息查询」列表：展示全部筛选结果 */
const QUERY_LIST_PREVIEW_MAX = Infinity;

const WELCOME_DEFAULTS: WelcomeMessages = {
  "zh-CN": "欢迎来到稻城天文台信息查询",
  en: "Welcome to the Daocheng Observatory Information Query",
  bo: "འདབ་ཆུ་གནམ་གཟིགས་ལྟེ་གནས་ཀྱི་ཆ་འཕྲིན་འཚོལ་ཞིབ་ལ་ཕེབས་པར་དགའ་བསུ་ཞུ།",
};

const ADMIN_AUTH_KEY = "kiosk_admin_auth_v1";
const ADMIN_USERNAME = "admin";
const ADMIN_PASSWORD = "admin@123";

function linesToItems(s: string): string[] {
  return s
    .split(/\r?\n/)
    .map((l) => l.trim())
    .filter(Boolean);
}

function itemsToLines(items: string[]): string {
  return items.join("\n");
}

export function AdminPage() {
  const { t } = useI18n();
  const [activeTab, setActiveTab] = useState<AdminTab>("background");
  const [isAuthed, setIsAuthed] = useState(() => {
    try {
      return sessionStorage.getItem(ADMIN_AUTH_KEY) === "1";
    } catch {
      return false;
    }
  });
  const [loginUser, setLoginUser] = useState("");
  const [loginPass, setLoginPass] = useState("");

  const [homeBgUrls, setHomeBgUrls] = useState<string[]>(() => loadHomeBackgrounds());
  const homeBgFileRef = useRef<HTMLInputElement>(null);
  const homeBgPickRef = useRef<{ kind: "append" } | { kind: "replace"; index: number }>({
    kind: "append",
  });
  const pairCoverFileRef = useRef<HTMLInputElement>(null);
  const pairVideoFileRef = useRef<HTMLInputElement>(null);

  const [welcome, setWelcome] = useState<WelcomeMessages>(() =>
    loadWelcome(WELCOME_DEFAULTS)
  );

  const [guideZhTouch, setGuideZhTouch] = useState("");
  const [guideZhSoft, setGuideZhSoft] = useState("");
  const [guideEnTouch, setGuideEnTouch] = useState("");
  const [guideEnSoft, setGuideEnSoft] = useState("");
  const [guideBoTouch, setGuideBoTouch] = useState("");
  const [guideBoSoft, setGuideBoSoft] = useState("");
  const [guideEditLang, setGuideEditLang] = useState<"zh-CN" | "en" | "bo">("zh-CN");

  const hydrateGuideEditors = useCallback(() => {
    const merged = mergeGuideLocales(DEFAULT_GUIDE_LOCALES, loadGuideOverrides());
    const zh = merged.find((l) => l.htmlLang === "zh-CN")!;
    const en = merged.find((l) => l.htmlLang === "en")!;
    const bo = merged.find((l) => l.htmlLang === "bo")!;
    setGuideZhTouch(itemsToLines(zh.touchItems));
    setGuideZhSoft(itemsToLines(zh.softwareItems));
    setGuideEnTouch(itemsToLines(en.touchItems));
    setGuideEnSoft(itemsToLines(en.softwareItems));
    setGuideBoTouch(itemsToLines(bo.touchItems));
    setGuideBoSoft(itemsToLines(bo.softwareItems));
  }, []);

  useEffect(() => {
    hydrateGuideEditors();
  }, [hydrateGuideEditors]);

  const pullServerState = useCallback(async (): Promise<boolean> => {
    const ok = await fetchAndApplyKioskBundle();
    if (!ok) {
      toast.error("无法从服务器加载展台数据，请检查后端与 /api/kiosk/bundle");
      return false;
    }
    setHomeBgUrls(loadHomeBackgrounds());
    setWelcome(loadWelcome(WELCOME_DEFAULTS));
    hydrateGuideEditors();
    setKnowledgeList(loadKnowledgeEntries());
    setVideoSource(loadVideoSourceConfig());
    return true;
  }, [hydrateGuideEditors]);

  useEffect(() => {
    if (!isAuthed) return;
    void pullServerState();
  }, [isAuthed, pullServerState]);

  const [queryKeyword, setQueryKeyword] = useState("");
  const [queryType, setQueryType] = useState("全部");
  const [queryTag, setQueryTag] = useState("全部");
  const [knowledgeList, setKnowledgeList] = useState<KnowledgeEntry[]>(() =>
    loadKnowledgeEntries()
  );

  const [pairTitle, setPairTitle] = useState("");
  const [pairType, setPairType] = useState<"文字" | "图片" | "视频">("文字");
  const [pairContent, setPairContent] = useState("");
  const [pairTags, setPairTags] = useState("");
  const [pairImage, setPairImage] = useState("");
  const [pairVideo, setPairVideo] = useState("");
  const [pairDate, setPairDate] = useState(() =>
    new Date().toISOString().slice(0, 10)
  );
  const [editingKnowledgeId, setEditingKnowledgeId] = useState<string | null>(null);
  const [videoSource, setVideoSource] = useState<VideoSourceConfig>(() =>
    loadVideoSourceConfig()
  );

  const queryFiltered = useMemo(() => {
    return knowledgeList.filter((item) => {
      const kw = queryKeyword.trim().toLowerCase();
      const okKw =
        kw === "" ||
        item.title.toLowerCase().includes(kw) ||
        item.content.toLowerCase().includes(kw);
      const okType = queryType === "全部" || item.type === queryType;
      const okTag = queryTag === "全部" || item.tags.includes(queryTag);
      return okKw && okType && okTag;
    });
  }, [knowledgeList, queryKeyword, queryType, queryTag]);

  const queryListPreview = useMemo(() => queryFiltered, [queryFiltered]);

  const queryTags = useMemo(() => {
    const s = new Set<string>();
    knowledgeList.forEach((k) => k.tags.forEach((t) => s.add(t)));
    return ["全部", ...Array.from(s).sort()];
  }, [knowledgeList]);

  const saveBackgrounds = async () => {
    const home = homeBgUrls.filter((u) => u.trim().length > 0);
    const urls = home.length ? home : [...DEFAULT_HOME_BG_URLS];
    const ok = await pushHomeBackgroundsToServer(urls);
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    toast.success(t("admin.toast.saved"));
  };

  const resetBackgrounds = async () => {
    const urls = [...DEFAULT_HOME_BG_URLS];
    const ok = await pushHomeBackgroundsToServer(urls);
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    toast.success(t("admin.toast.updated"));
  };

  const openHomeBgPicker = (target: { kind: "append" } | { kind: "replace"; index: number }) => {
    homeBgPickRef.current = target;
    homeBgFileRef.current?.click();
  };

  const onHomeBgFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    e.target.value = "";
    if (!file || !file.type.startsWith("image/")) return;
    const reader = new FileReader();
    reader.onload = () => {
      const dataUrl = reader.result as string;
      if (typeof dataUrl !== "string") return;
      const t = homeBgPickRef.current;
      if (t.kind === "append") {
        setHomeBgUrls((prev) => [...prev, dataUrl]);
      } else {
        setHomeBgUrls((prev) => {
          const next = [...prev];
          if (t.index >= 0 && t.index < next.length) next[t.index] = dataUrl;
          return next;
        });
      }
    };
    reader.readAsDataURL(file);
  };

  const onPairCoverFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    e.target.value = "";
    if (!file || !file.type.startsWith("image/")) return;
    const tid = toast.loading(t("admin.knowledge.uploading"));
    try {
      const url = await uploadKnowledgeMediaFile(file);
      toast.dismiss(tid);
      setPairImage(url);
    } catch (e) {
      toast.dismiss(tid);
      const desc = e instanceof Error ? e.message : String(e);
      toast.error(t("admin.knowledge.uploadFail"), {
        description: desc,
        duration: 12000,
      });
    }
  };

  const onPairVideoFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    e.target.value = "";
    if (!file || !file.type.startsWith("video/")) return;
    const tid = toast.loading(t("admin.knowledge.uploading"));
    try {
      const url = await uploadKnowledgeMediaFile(file);
      toast.dismiss(tid);
      setPairVideo(url);
    } catch (e) {
      toast.dismiss(tid);
      const desc = e instanceof Error ? e.message : String(e);
      toast.error(t("admin.knowledge.uploadFail"), {
        description: desc,
        duration: 12000,
      });
    }
  };

  const removeHomeBgAt = (index: number) => {
    setHomeBgUrls((prev) => prev.filter((_, i) => i !== index));
  };

  const saveWelcomeClick = async () => {
    const ok = await pushWelcomeToServer(welcome);
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    toast.success(t("admin.toast.saved"));
  };

  const resetWelcome = async () => {
    const ok = await pushWelcomeToServer({ ...WELCOME_DEFAULTS });
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    toast.success(t("admin.toast.updated"));
  };

  const saveGuideClick = async () => {
    const overrides: GuideOverrides = {
      "zh-CN": {
        touchItems: linesToItems(guideZhTouch),
        softwareItems: linesToItems(guideZhSoft),
      },
      en: {
        touchItems: linesToItems(guideEnTouch),
        softwareItems: linesToItems(guideEnSoft),
      },
      bo: {
        touchItems: linesToItems(guideBoTouch),
        softwareItems: linesToItems(guideBoSoft),
      },
    };
    const ok = await pushGuideToServer(overrides);
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    hydrateGuideEditors();
    toast.success(t("admin.toast.saved"));
  };

  const resetGuide = async () => {
    const ok = await pushGuideToServer({});
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    hydrateGuideEditors();
    toast.success(t("admin.toast.updated"));
  };

  const deleteKnowledge = async (id: string) => {
    const next = knowledgeList.filter((k) => k.id !== id);
    try {
      await pushKnowledgeToServer(next);
    } catch (e) {
      const desc = e instanceof Error ? e.message : String(e);
      toast.error(t("admin.knowledge.saveFailTitle"), {
        description: desc,
        duration: 14000,
      });
      await pullServerState();
      setKnowledgeList(loadKnowledgeEntries());
      return;
    }
    await pullServerState();
    setKnowledgeList(loadKnowledgeEntries());
  };

  const beginEditKnowledge = (item: KnowledgeEntry) => {
    setEditingKnowledgeId(item.id);
    setActiveTab("knowledge");
    setPairTitle(item.title ?? "");
    setPairType(item.type);
    setPairContent(item.content ?? "");
    setPairTags((item.tags ?? []).join("，"));
    setPairImage(item.image ?? "");
    setPairVideo(item.videoUrl ?? "");
    setPairDate(item.date ?? new Date().toISOString().slice(0, 10));
    toast.message(t("admin.toast.editMode"));
  };

  const resetKnowledgeEditor = () => {
    setEditingKnowledgeId(null);
    setPairTitle("");
    setPairContent("");
    setPairTags("");
    setPairImage("");
    setPairVideo("");
    setPairDate(new Date().toISOString().slice(0, 10));
    setPairType("文字");
  };

  const addKnowledge = async () => {
    const title = pairTitle.trim();
    if (!title) return;
    const tags = pairTags
      .split(/[,，;；]/)
      .map((t) => t.trim())
      .filter(Boolean);
    const row: KnowledgeEntry = {
      id: editingKnowledgeId ?? newKnowledgeEntryId(),
      type: pairType,
      title,
      content: pairContent.trim() || "—",
      date: pairDate,
      tags: tags.length ? tags : ["未分类"],
      image: pairImage.trim() || undefined,
      videoUrl: pairVideo.trim() || undefined,
    };
    const next = editingKnowledgeId
      ? knowledgeList.map((k) => (k.id === editingKnowledgeId ? row : k))
      : [row, ...knowledgeList];
    try {
      await pushKnowledgeToServer(next);
    } catch (e) {
      const desc = e instanceof Error ? e.message : String(e);
      toast.error(t("admin.knowledge.saveFailTitle"), {
        description: desc,
        duration: 14000,
      });
      return;
    }
    await pullServerState();
    setKnowledgeList(loadKnowledgeEntries());
    toast.success(editingKnowledgeId ? t("admin.toast.updated") : t("admin.toast.saved"));
    resetKnowledgeEditor();
  };

  const resetKnowledgeDb = async () => {
    const fresh = DEFAULT_KNOWLEDGE_ENTRIES.map((e) => ({ ...e }));
    try {
      await pushKnowledgeToServer(fresh);
    } catch (e) {
      const desc = e instanceof Error ? e.message : String(e);
      toast.error(t("admin.knowledge.saveFailTitle"), {
        description: desc,
        duration: 14000,
      });
      await pullServerState();
      setKnowledgeList(loadKnowledgeEntries());
      return;
    }
    await pullServerState();
    setKnowledgeList(loadKnowledgeEntries());
    toast.success(t("admin.toast.updated"));
  };

  const saveVideoSource = async () => {
    const next: VideoSourceConfig = {
      protocol: videoSource.protocol,
      url: videoSource.url.trim(),
      note: videoSource.note.trim(),
    };
    const ok = await pushVideoSourceToServer(next);
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    setVideoSource(loadVideoSourceConfig());
    toast.success(t("admin.toast.videoSaved"));
  };

  const resetVideoSource = async () => {
    const next = { ...DEFAULT_VIDEO_SOURCE };
    const ok = await pushVideoSourceToServer(next);
    if (!ok) {
      toast.error(t("admin.syncFail"));
      await pullServerState();
      return;
    }
    await pullServerState();
    setVideoSource(loadVideoSourceConfig());
    toast.success(t("admin.toast.videoReset"));
  };

  const handleLogin = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (loginUser.trim() === ADMIN_USERNAME && loginPass === ADMIN_PASSWORD) {
      try {
        sessionStorage.setItem(ADMIN_AUTH_KEY, "1");
      } catch {
        // ignore storage failures
      }
      setIsAuthed(true);
      setLoginPass("");
      toast.success(t("admin.toast.loginOk"));
      return;
    }
    toast.error(t("admin.toast.loginBad"));
  };

  const logoutAdmin = () => {
    try {
      sessionStorage.removeItem(ADMIN_AUTH_KEY);
    } catch {
      // ignore storage failures
    }
    setIsAuthed(false);
    setLoginPass("");
    toast.success(t("admin.toast.logoutOk"));
  };

  const sidebarBtn = (id: AdminTab, icon: ReactNode, label: string) => (
    <button
      type="button"
      onClick={() => setActiveTab(id)}
      className={`flex w-full items-center gap-2.5 rounded-lg px-3 py-2.5 text-left text-sm font-medium transition-all sm:gap-3 sm:px-4 sm:py-3 ${
        activeTab === id
          ? "bg-sky-500/20 text-sky-200 ring-1 ring-sky-400/45"
          : "text-blue-200 hover:bg-white/5 hover:text-white"
      }`}
    >
      <span className="shrink-0 opacity-90">{icon}</span>
      <span className="min-w-0 leading-snug">{label}</span>
    </button>
  );

  const labelCls = "mb-1.5 block text-xs font-medium text-blue-200 sm:text-sm";
  const inputCls =
    "w-full rounded-lg border border-white/20 bg-white/5 px-3 py-2 text-sm text-white placeholder-blue-500/80 focus:border-sky-400 focus:outline-none focus:ring-1 focus:ring-sky-400/30 sm:px-4 sm:py-2.5 sm:text-base";
  const textareaCls =
    "min-h-[120px] w-full rounded-lg border border-white/20 bg-white/5 px-3 py-2 font-mono text-xs leading-relaxed text-white placeholder-blue-500/80 focus:border-sky-400 focus:outline-none focus:ring-1 focus:ring-sky-400/30 sm:min-h-[140px] sm:px-4 sm:py-2.5 sm:text-sm";

  if (!isAuthed) {
    return (
      <div
        className={`flex min-h-0 w-full min-w-0 max-w-full flex-1 flex-col ${PAGE_CONTENT_INSET}`}
      >
        <div className="mb-4 shrink-0 sm:mb-6">
          <h1 className="mb-1 flex shrink-0 items-center gap-2 text-xl font-bold text-white sm:text-2xl">
            <Settings className="h-6 w-6 shrink-0 text-sky-400" />
            {t("nav.maintain")}
          </h1>
        </div>
        <div className="mx-auto w-full max-w-md rounded-xl border border-white/20 bg-white/10 p-4 backdrop-blur-xl sm:p-6">
          <h2 className="mb-2 flex items-center gap-2 text-lg font-semibold text-white">
            <LockKeyhole className="h-5 w-5 text-sky-300" />
            {t("admin.login.title")}
          </h2>
          <p className="mb-4 text-sm text-blue-200">
            {t("admin.login.desc")}
          </p>
          <form className="space-y-3" onSubmit={handleLogin}>
            <div>
              <label className={labelCls}>{t("admin.login.user")}</label>
              <input
                className={inputCls}
                value={loginUser}
                onChange={(e) => setLoginUser(e.target.value)}
                autoComplete="username"
                placeholder={t("admin.login.userPh")}
              />
            </div>
            <div>
              <label className={labelCls}>{t("admin.login.pass")}</label>
              <input
                type="password"
                className={inputCls}
                value={loginPass}
                onChange={(e) => setLoginPass(e.target.value)}
                autoComplete="current-password"
                placeholder={t("admin.login.passPh")}
              />
            </div>
            <button
              type="submit"
              className="flex w-full items-center justify-center gap-2 rounded-lg bg-sky-500/90 px-4 py-2 text-sm font-medium text-blue-950 hover:bg-sky-400"
            >
              <LockKeyhole className="h-4 w-4" />
              {t("admin.login.btn")}
            </button>
          </form>
        </div>
      </div>
    );
  }

  return (
    <div
      className={`flex min-h-0 w-full min-w-0 max-w-full flex-1 flex-col ${PAGE_CONTENT_INSET}`}
    >
      <div className="mb-4 flex shrink-0 items-center justify-between gap-3 sm:mb-6">
        <h1 className="mb-1 flex shrink-0 items-center gap-2 text-xl font-bold text-white sm:text-2xl">
          <Settings className="h-6 w-6 shrink-0 text-sky-400" />
          {t("nav.maintain")}
        </h1>
        <button
          type="button"
          onClick={logoutAdmin}
          className="inline-flex items-center gap-1.5 rounded-lg border border-white/20 bg-white/10 px-3 py-1.5 text-xs text-white hover:bg-white/15 sm:text-sm"
        >
          <LogOut className="h-4 w-4" />
          {t("admin.logout")}
        </button>
      </div>

      <div className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl border border-white/20 bg-white/10 backdrop-blur-xl md:flex-row">
        <nav
          className="flex shrink-0 flex-col gap-1 border-b border-white/10 p-3 sm:p-4 md:h-full md:w-[13.5rem] md:min-h-0 md:overflow-y-auto md:border-b-0 md:border-r md:py-5"
          aria-label={t("nav.maintain")}
        >
          {sidebarBtn("background", <Image className="h-4 w-4" />, t("admin.tab.background"))}
          {sidebarBtn("welcome", <Languages className="h-4 w-4" />, t("admin.tab.welcome"))}
          {sidebarBtn("guide", <BookOpen className="h-4 w-4" />, t("admin.tab.guide"))}
          {sidebarBtn("query", <Search className="h-4 w-4" />, t("admin.tab.query"))}
          {sidebarBtn("knowledge", <Database className="h-4 w-4" />, t("admin.tab.knowledge"))}
          {sidebarBtn("stream", <Link2 className="h-4 w-4" />, t("admin.tab.stream"))}
        </nav>

        <div className="min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-y-contain p-4 sm:p-6 [-webkit-overflow-scrolling:touch]">
        {activeTab === "background" && (
          <div className="space-y-6">
            <label htmlFor="admin-home-bg-file" className="sr-only">
              {t("admin.bg.srPick")}
            </label>
            <input
              id="admin-home-bg-file"
              ref={homeBgFileRef}
              type="file"
              accept="image/*"
              className="sr-only"
              onChange={onHomeBgFileChange}
            />
            <p className="text-sm text-blue-200">
              {t("admin.bg.help")}
            </p>
            <div>
              <span className={labelCls}>{t("admin.bg.title")}</span>
              <div className="mt-2 grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-4 lg:grid-cols-4">
                {homeBgUrls.map((url, i) => (
                  <div
                    key={`bg-${i}-${url.length}`}
                    className="relative pt-1 pr-1"
                  >
                    <button
                      type="button"
                      onClick={() => openHomeBgPicker({ kind: "replace", index: i })}
                      className="group relative aspect-video w-full overflow-hidden rounded-2xl border border-white/20 bg-white/5 shadow-sm outline-none ring-sky-400/0 transition hover:border-sky-400/40 focus-visible:ring-2"
                    >
                      <img
                        src={url}
                        alt=""
                        className="h-full w-full object-cover transition duration-300 group-hover:scale-[1.02]"
                      />
                      <span className="pointer-events-none absolute left-2 top-2 rounded-md bg-black/60 px-2 py-0.5 text-xs font-medium text-white backdrop-blur-sm">
                        {t("admin.bg.noPrefix")} {i + 1} {t("admin.bg.noSuffix")}
                      </span>
                      <span className="pointer-events-none absolute inset-0 flex items-center justify-center bg-black/0 text-sm font-medium text-white opacity-0 transition group-hover:bg-black/45 group-hover:opacity-100">
                        {t("admin.common.clickReplace")}
                      </span>
                    </button>
                    <button
                      type="button"
                      onClick={() => removeHomeBgAt(i)}
                      className="absolute -right-0.5 top-0 z-10 flex h-7 w-7 items-center justify-center rounded-full border border-white/20 bg-red-500/90 text-white shadow-md transition hover:bg-red-400"
                      aria-label={`${t("admin.bg.deletePrefix")} ${i + 1} ${t("admin.bg.deleteSuffix")}`}
                    >
                      <X className="h-4 w-4" strokeWidth={2.5} />
                    </button>
                  </div>
                ))}
                <button
                  type="button"
                  onClick={() => openHomeBgPicker({ kind: "append" })}
                  className="flex aspect-video w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-white/30 bg-white/[0.07] text-blue-200/95 transition hover:border-sky-400/55 hover:bg-white/12 hover:text-sky-100"
                >
                  <Image
                    className="h-9 w-9 opacity-85 sm:h-11 sm:w-11"
                    strokeWidth={1.15}
                    aria-hidden
                  />
                  <span className="text-sm font-medium sm:text-base">{t("admin.bg.upload")}</span>
                </button>
              </div>
            </div>
            <div className="flex flex-wrap gap-2">
              <button
                type="button"
                onClick={saveBackgrounds}
                className="flex items-center gap-2 rounded-lg bg-sky-500/90 px-4 py-2 text-sm font-medium text-white hover:bg-sky-400"
              >
                <Save className="h-4 w-4" />
                {t("admin.bg.save")}
              </button>
              <button
                type="button"
                onClick={resetBackgrounds}
                className="flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm text-white hover:bg-white/15"
              >
                <RotateCcw className="h-4 w-4" />
                {t("admin.resetDefault")}
              </button>
            </div>
          </div>
        )}

        {activeTab === "welcome" && (
          <div className="space-y-4">
            <p className="text-sm text-blue-200">{t("admin.welcome.help")}</p>
            {(["zh-CN", "en", "bo"] as const).map((key) => (
              <div key={key}>
                <label className={labelCls}>
                  {key === "zh-CN" ? "中文" : key === "en" ? "English" : "བོད་ཡིག"}
                </label>
                <textarea
                  className={textareaCls + " min-h-[72px]"}
                  value={welcome[key]}
                  onChange={(e) =>
                    setWelcome((w) => ({ ...w, [key]: e.target.value }))
                  }
                />
              </div>
            ))}
            <div className="flex flex-wrap gap-2">
              <button
                type="button"
                onClick={saveWelcomeClick}
                className="flex items-center gap-2 rounded-lg bg-sky-500/90 px-4 py-2 text-sm font-medium text-white hover:bg-sky-400"
              >
                <Save className="h-4 w-4" />
                {t("admin.welcome.save")}
              </button>
              <button
                type="button"
                onClick={resetWelcome}
                className="flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm text-white hover:bg-white/15"
              >
                <RotateCcw className="h-4 w-4" />
                {t("admin.resetDefault")}
              </button>
            </div>
          </div>
        )}

        {activeTab === "guide" && (
          <div className="space-y-6">
            <p className="text-sm text-blue-200">
              {t("admin.guide.help")}
            </p>
            <Tabs
              value={guideEditLang}
              onValueChange={(v) =>
                setGuideEditLang(v as "zh-CN" | "en" | "bo")
              }
              className="gap-4"
            >
              <TabsList className="h-auto w-full max-w-md flex-wrap justify-stretch gap-1 rounded-xl border border-white/15 bg-white/5 p-1">
                <TabsTrigger
                  value="zh-CN"
                  className="flex-1 rounded-lg px-3 py-2 text-sm font-medium text-blue-200 data-[state=active]:border-white/20 data-[state=active]:bg-sky-500/90 data-[state=active]:text-blue-950 data-[state=inactive]:bg-transparent data-[state=inactive]:hover:bg-white/10"
                >
                  中文
                </TabsTrigger>
                <TabsTrigger
                  value="en"
                  className="flex-1 rounded-lg px-3 py-2 text-sm font-medium text-blue-200 data-[state=active]:border-white/20 data-[state=active]:bg-sky-500/90 data-[state=active]:text-blue-950 data-[state=inactive]:bg-transparent data-[state=inactive]:hover:bg-white/10"
                >
                  English
                </TabsTrigger>
                <TabsTrigger
                  value="bo"
                  className="flex-1 rounded-lg px-3 py-2 font-medium text-blue-200 data-[state=active]:border-white/20 data-[state=active]:bg-sky-500/90 data-[state=active]:text-blue-950 data-[state=inactive]:bg-transparent data-[state=inactive]:hover:bg-white/10 sm:px-3"
                >
                  བོད་ཡིག
                </TabsTrigger>
              </TabsList>
              {(
                [
                  ["zh-CN", guideZhTouch, setGuideZhTouch, guideZhSoft, setGuideZhSoft],
                  ["en", guideEnTouch, setGuideEnTouch, guideEnSoft, setGuideEnSoft],
                  ["bo", guideBoTouch, setGuideBoTouch, guideBoSoft, setGuideBoSoft],
                ] as const
              ).map(([lang, touch, setTouch, soft, setSoft]) => (
                <TabsContent
                  key={lang}
                  value={lang}
                  className="mt-0 rounded-xl border border-white/10 bg-white/[0.04] p-4 outline-none data-[state=inactive]:hidden"
                >
                  <div className="space-y-3">
                    <div>
                      <span className={labelCls}>触屏操作（每行一条）</span>
                      <textarea
                        className={`${textareaCls} !min-h-[128px] sm:!min-h-[184px]`}
                        value={touch}
                        onChange={(e) => setTouch(e.target.value)}
                      />
                    </div>
                    <div>
                      <span className={labelCls}>软件说明（每行一条）</span>
                      <textarea
                        className={`${textareaCls} !min-h-[128px] sm:!min-h-[184px]`}
                        value={soft}
                        onChange={(e) => setSoft(e.target.value)}
                      />
                    </div>
                  </div>
                </TabsContent>
              ))}
            </Tabs>
            <div className="flex flex-wrap gap-2">
              <button
                type="button"
                onClick={saveGuideClick}
                className="flex items-center gap-2 rounded-lg bg-sky-500/90 px-4 py-2 text-sm font-medium text-blue-950 hover:bg-sky-400"
              >
                <Save className="h-4 w-4" />
                {t("admin.guide.save")}
              </button>
              <button
                type="button"
                onClick={resetGuide}
                className="flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm text-white hover:bg-white/15"
              >
                <RotateCcw className="h-4 w-4" />
                {t("admin.resetDefault")}
              </button>
            </div>
          </div>
        )}

        {activeTab === "query" && (
          <div className="space-y-4">
            <p className="text-sm text-blue-200">
              {t("admin.query.help")}
            </p>
            <div className="grid gap-3 sm:grid-cols-3">
              <div>
                <label className={labelCls}>{t("admin.query.keyword")}</label>
                <input
                  className={inputCls}
                  value={queryKeyword}
                  onChange={(e) => setQueryKeyword(e.target.value)}
                  placeholder={t("admin.query.placeholder")}
                />
              </div>
              <div>
                <label className={labelCls}>{t("admin.query.type")}</label>
                <select
                  className={inputCls}
                  value={queryType}
                  onChange={(e) => setQueryType(e.target.value)}
                >
                  <option key="all" value="全部" className="bg-slate-900">
                    {t("admin.query.all")}
                  </option>
                  <option key="text" value="文字" className="bg-slate-900">
                    {t("admin.type.text")}
                  </option>
                  <option key="image" value="图片" className="bg-slate-900">
                    {t("admin.type.image")}
                  </option>
                  <option key="video" value="视频" className="bg-slate-900">
                    {t("admin.type.video")}
                  </option>
                </select>
              </div>
              <div>
                <label className={labelCls}>{t("admin.query.tag")}</label>
                <select
                  className={inputCls}
                  value={queryTag}
                  onChange={(e) => setQueryTag(e.target.value)}
                >
                  {queryTags.map((tag) => (
                    <option key={tag} value={tag} className="bg-slate-900">
                      {tag === "全部" ? t("admin.query.all") : tag}
                    </option>
                  ))}
                </select>
              </div>
            </div>
            <p className="text-sm text-sky-300">
              {t("admin.query.totalPrefix")} {knowledgeList.length} {t("admin.query.itemsSuffix")}，
              {t("admin.query.filteredPrefix")} {queryFiltered.length} {t("admin.query.itemsSuffix")}
            </p>
            <div className="max-h-[60dvh] space-y-2 overflow-y-auto pr-1">
              {queryListPreview.map((item) => (
                <div
                  key={item.id}
                  className="flex flex-col gap-2 rounded-lg border border-white/10 bg-white/5 p-3 sm:flex-row sm:items-center sm:justify-between"
                >
                  <div className="min-w-0">
                    <div className="font-medium text-white">{item.title}</div>
                    <div className="mt-1 flex flex-wrap gap-2 text-xs text-blue-300">
                      <span>{item.type}</span>
                      <span>{item.date}</span>
                      {item.tags.map((t) => (
                        <span key={t} className="rounded bg-white/10 px-1.5">
                          {t}
                        </span>
                      ))}
                    </div>
                  </div>
                  <div className="flex shrink-0 flex-wrap items-center justify-end gap-2 self-end sm:self-center">
                    <button
                      type="button"
                      onClick={() => beginEditKnowledge(item)}
                      className="rounded-lg bg-white/10 px-3 py-1.5 text-sm text-white hover:bg-white/15"
                    >
                      {t("common.edit")}
                    </button>
                    <button
                      type="button"
                      onClick={() => deleteKnowledge(item.id)}
                      className="rounded-lg bg-red-500/20 px-3 py-1.5 text-sm text-red-200 hover:bg-red-500/30"
                    >
                      <Trash2 className="mr-1 inline h-4 w-4" />
                      {t("common.delete")}
                    </button>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {activeTab === "knowledge" && (
          <div className="space-y-6">
            <p className="text-sm text-blue-200">
              {t("admin.knowledge.help")}
            </p>
            <label htmlFor="admin-pair-cover-file" className="sr-only">
              {t("admin.knowledge.srCover")}
            </label>
            <input
              id="admin-pair-cover-file"
              ref={pairCoverFileRef}
              type="file"
              accept="image/*"
              className="sr-only"
              onChange={onPairCoverFileChange}
            />
            <label htmlFor="admin-pair-video-file" className="sr-only">
              {t("admin.knowledge.srVideo")}
            </label>
            <input
              id="admin-pair-video-file"
              ref={pairVideoFileRef}
              type="file"
              accept="video/*"
              className="sr-only"
              onChange={onPairVideoFileChange}
            />
            <div className="grid gap-3 rounded-xl border border-white/10 bg-white/[0.04] p-4 sm:grid-cols-2">
              <div className="sm:col-span-2">
                <div className="flex flex-col gap-3 sm:flex-row sm:items-end sm:gap-4">
                  <div className="min-w-0 flex-1">
                    <label className={labelCls}>{t("admin.knowledge.title")}</label>
                    <input
                      className={inputCls}
                      value={pairTitle}
                      onChange={(e) => setPairTitle(e.target.value)}
                    />
                  </div>
                  <div className="w-full shrink-0 sm:w-[9rem]">
                    <label className={labelCls}>{t("admin.knowledge.type")}</label>
                    <select
                      className={inputCls}
                      value={pairType}
                      onChange={(e) =>
                        setPairType(e.target.value as "文字" | "图片" | "视频")
                      }
                    >
                      <option className="bg-slate-900" value="文字">
                        {t("admin.type.text")}
                      </option>
                      <option className="bg-slate-900" value="图片">
                        {t("admin.type.image")}
                      </option>
                      <option className="bg-slate-900" value="视频">
                        {t("admin.type.video")}
                      </option>
                    </select>
                  </div>
                  <div className="w-full shrink-0 sm:w-[11.5rem]">
                    <label className={labelCls}>{t("admin.knowledge.date")}</label>
                    <input
                      type="date"
                      className={inputCls}
                      value={pairDate}
                      onChange={(e) => setPairDate(e.target.value)}
                    />
                  </div>
                </div>
              </div>
              <div className="sm:col-span-2">
                <label className={labelCls}>{t("admin.knowledge.content")}</label>
                <textarea
                  className={textareaCls + " min-h-[80px]"}
                  value={pairContent}
                  onChange={(e) => setPairContent(e.target.value)}
                />
              </div>
              <div className="sm:col-span-2">
                <label className={labelCls}>{t("admin.knowledge.tags")}</label>
                <input
                  className={inputCls}
                  value={pairTags}
                  onChange={(e) => setPairTags(e.target.value)}
                  placeholder={t("admin.knowledge.tagsPh")}
                />
              </div>
              <div className="sm:col-span-2">
                <span className={labelCls}>{t("admin.knowledge.coverOpt")}</span>
                <div className="mt-2 w-full max-w-[13rem] sm:max-w-[15rem]">
                  {pairImage ? (
                    <div className="relative pt-1 pr-1">
                      <button
                        type="button"
                        onClick={() => pairCoverFileRef.current?.click()}
                        className="group relative aspect-video w-full overflow-hidden rounded-xl border border-white/20 bg-white/5 shadow-sm outline-none ring-sky-400/0 transition hover:border-sky-400/40 focus-visible:ring-2"
                      >
                        <img
                          src={resolveKioskMediaUrl(pairImage)}
                          alt=""
                          className="h-full w-full object-cover transition duration-300 group-hover:scale-[1.02]"
                        />
                        <span className="pointer-events-none absolute inset-0 flex items-center justify-center bg-black/0 text-xs font-medium text-white opacity-0 transition group-hover:bg-black/45 group-hover:opacity-100">
                          {t("admin.common.clickReplace")}
                        </span>
                      </button>
                      <button
                        type="button"
                        onClick={() => setPairImage("")}
                        className="absolute -right-0.5 top-0 z-10 flex h-6 w-6 items-center justify-center rounded-full border border-white/20 bg-red-500/90 text-white shadow-md transition hover:bg-red-400"
                        aria-label={t("admin.knowledge.clearCover")}
                      >
                        <X className="h-3.5 w-3.5" strokeWidth={2.5} />
                      </button>
                    </div>
                  ) : (
                    <button
                      type="button"
                      onClick={() => pairCoverFileRef.current?.click()}
                      className="flex aspect-video w-full cursor-pointer flex-col items-center justify-center gap-1.5 rounded-xl border-2 border-dashed border-white/30 bg-white/[0.07] px-2 py-3 text-blue-200/95 transition hover:border-sky-400/55 hover:bg-white/12 hover:text-sky-100"
                    >
                      <Image
                        className="h-6 w-6 opacity-85 sm:h-7 sm:w-7"
                        strokeWidth={1.15}
                        aria-hidden
                      />
                      <span className="text-[11px] font-medium leading-tight sm:text-xs">
                        {t("admin.bg.upload")}
                      </span>
                    </button>
                  )}
                </div>
              </div>
              <div className="sm:col-span-2">
                <span className={labelCls}>{t("admin.knowledge.videoOpt")}</span>
                <div className="mt-2 w-full max-w-[13rem] sm:max-w-[15rem]">
                  {pairVideo ? (
                    <div className="relative pt-1 pr-1">
                      <button
                        type="button"
                        onClick={() => pairVideoFileRef.current?.click()}
                        className="group relative aspect-video w-full overflow-hidden rounded-xl border border-white/20 bg-white/5 shadow-sm outline-none ring-sky-400/0 transition hover:border-sky-400/40 focus-visible:ring-2"
                      >
                        <video
                          src={resolveKioskMediaUrl(pairVideo)}
                          className="h-full w-full object-cover transition duration-300 group-hover:scale-[1.01]"
                          muted
                          playsInline
                          preload="metadata"
                          aria-hidden
                        />
                        <span className="pointer-events-none absolute inset-0 flex items-center justify-center bg-black/0 text-xs font-medium text-white opacity-0 transition group-hover:bg-black/45 group-hover:opacity-100">
                          {t("admin.common.clickReplace")}
                        </span>
                      </button>
                      <button
                        type="button"
                        onClick={() => setPairVideo("")}
                        className="absolute -right-0.5 top-0 z-10 flex h-6 w-6 items-center justify-center rounded-full border border-white/20 bg-red-500/90 text-white shadow-md transition hover:bg-red-400"
                        aria-label={t("admin.knowledge.clearVideo")}
                      >
                        <X className="h-3.5 w-3.5" strokeWidth={2.5} />
                      </button>
                    </div>
                  ) : (
                    <button
                      type="button"
                      onClick={() => pairVideoFileRef.current?.click()}
                      className="flex aspect-video w-full cursor-pointer flex-col items-center justify-center gap-1.5 rounded-xl border-2 border-dashed border-white/30 bg-white/[0.07] px-2 py-3 text-blue-200/95 transition hover:border-sky-400/55 hover:bg-white/12 hover:text-sky-100"
                    >
                      <Video
                        className="h-6 w-6 opacity-85 sm:h-7 sm:w-7"
                        strokeWidth={1.15}
                        aria-hidden
                      />
                      <span className="text-[11px] font-medium leading-tight sm:text-xs">
                        {t("admin.type.video")}
                      </span>
                    </button>
                  )}
                </div>
              </div>
              <div className="flex flex-wrap gap-2 sm:col-span-2">
                <button
                  type="button"
                  onClick={addKnowledge}
                  className="flex items-center gap-2 rounded-lg bg-sky-500/90 px-4 py-2 text-sm font-medium text-blue-950 hover:bg-sky-400"
                >
                  <Plus className="h-4 w-4" />
                  {editingKnowledgeId ? t("admin.knowledge.saveEdit") : t("admin.knowledge.add")}
                </button>
                {editingKnowledgeId ? (
                  <button
                    type="button"
                    onClick={resetKnowledgeEditor}
                    className="flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm text-white hover:bg-white/15"
                  >
                    <RotateCcw className="h-4 w-4" />
                    {t("common.cancel")}
                  </button>
                ) : null}
                <button
                  type="button"
                  onClick={resetKnowledgeDb}
                  className="flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm text-white hover:bg-white/15"
                >
                  <RotateCcw className="h-4 w-4" />
                  {t("admin.knowledge.resetSample")}
                </button>
              </div>
            </div>
          </div>
        )}

        {activeTab === "stream" && (
          <div className="space-y-4">
            <p className="text-sm text-blue-200">
              配置共享天文台左侧实时视频源地址。HLS 可在浏览器中直接播放；WebRTC / RTSP 作为信号源信息展示，通常需网关转封装后再网页播放。
            </p>
            <div className="grid gap-3 rounded-xl border border-white/10 bg-white/[0.04] p-4 sm:grid-cols-2">
              <div>
                <label className={labelCls}>协议类型</label>
                <select
                  className={inputCls}
                  value={videoSource.protocol}
                  onChange={(e) =>
                    setVideoSource((prev) => ({
                      ...prev,
                      protocol: e.target.value as VideoProtocol,
                    }))
                  }
                >
                  <option className="bg-slate-900" value="HLS">
                    HLS（.m3u8）
                  </option>
                  <option className="bg-slate-900" value="WebRTC">
                    WebRTC
                  </option>
                  <option className="bg-slate-900" value="RTSP">
                    RTSP
                  </option>
                </select>
              </div>
              <div className="sm:col-span-2">
                <label className={labelCls}>视频源地址</label>
                <input
                  className={inputCls}
                  value={videoSource.url}
                  onChange={(e) =>
                    setVideoSource((prev) => ({ ...prev, url: e.target.value }))
                  }
                  placeholder="例如：https://example.com/live/index.m3u8"
                />
              </div>
              <div className="sm:col-span-2">
                <label className={labelCls}>备注（可选）</label>
                <textarea
                  className={textareaCls + " min-h-[80px]"}
                  value={videoSource.note}
                  onChange={(e) =>
                    setVideoSource((prev) => ({ ...prev, note: e.target.value }))
                  }
                  placeholder="例如：需要鉴权 Token；内网地址需经过 Nginx 转发"
                />
              </div>
            </div>
            <div className="flex flex-wrap gap-2">
              <button
                type="button"
                onClick={saveVideoSource}
                className="flex items-center gap-2 rounded-lg bg-sky-500/90 px-4 py-2 text-sm font-medium text-blue-950 hover:bg-sky-400"
              >
                <Save className="h-4 w-4" />
                {t("admin.stream.save")}
              </button>
              <button
                type="button"
                onClick={resetVideoSource}
                className="flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm text-white hover:bg-white/15"
              >
                <RotateCcw className="h-4 w-4" />
                清空配置
              </button>
            </div>
          </div>
        )}
        </div>
      </div>
    </div>
  );
}
