import { useEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from "react";
import { applyNodedcTheme, type NodedcTheme, type RgbTuple } from "@nodedc/ui-core";
import {
  AdminNavigationPanel,
  AppHeader,
  ApplicationPanel,
  ApplicationShell,
  Button,
  Checker,
  ColorField,
  ConfirmationModal,
  ControlRow,
  Dropdown,
  FieldFrame,
  GlassSurface,
  HeaderAvatar,
  HeaderNavigation,
  HeaderProfile,
  HeaderProfileButton,
  HeaderWorkspace,
  Icon,
  IconButton,
  Inspector,
  MediaSourceField,
  RangeControl,
  Select,
  ShareAccessModal,
  ShareLinkModal,
  SettingsCard,
  StatusBadge,
  Switch,
  TextAreaField,
  TextField,
  useApplicationWorkspace,
  Window,
  WindowFooterActions,
  type IconName,
  type ShareAccessMember,
} from "@nodedc/ui-react";

type CatalogSection = "foundation" | "controls" | "media" | "windows" | "modals" | "icons";

const accents: Array<{ label: string; value: RgbTuple; hex: string }> = [
  { label: "NODE.DC", value: [255, 47, 146], hex: "#ff2f92" },
  { label: "Rose", value: [255, 62, 108], hex: "#ff3e6c" },
  { label: "Blush", value: [255, 108, 175], hex: "#ff6caf" },
  { label: "Magenta", value: [215, 70, 255], hex: "#d746ff" },
];

type MaterialDraft = {
  panelHex: string;
  panelOpacity: number;
  fieldHex: string;
  fieldOpacity: number;
};

const materialDefaults: Record<NodedcTheme, MaterialDraft> = {
  dark: {
    panelHex: "#151517",
    panelOpacity: 100,
    fieldHex: "#2a2a2c",
    fieldOpacity: 100,
  },
  light: {
    panelHex: "#ffffff",
    panelOpacity: 100,
    fieldHex: "#ffffff",
    fieldOpacity: 100,
  },
};

type ShareRole = "viewer" | "editor" | "admin";

const shareRoleOptions = [
  { value: "viewer", label: "Просмотр" },
  { value: "editor", label: "Редактор" },
  { value: "admin", label: "Соавтор" },
] satisfies Array<{ value: ShareRole; label: string }>;

const selectOptions = [
  { value: "active", label: "Активен", description: "Сервис доступен пользователям" },
  { value: "maintenance", label: "Техработы", description: "Временно недоступен" },
  { value: "disabled", label: "Отключён", description: "Запуск запрещён" },
] as const;

const sectionDefinitions: Record<CatalogSection, { eyebrow: string; title: string; description: string; icon: IconName }> = {
  foundation: {
    eyebrow: "01 / FOUNDATION",
    title: "Основа",
    description: "Тема, нейтральные поверхности и неизменяемая геометрия приложения.",
    icon: "panel",
  },
  controls: {
    eyebrow: "02 / CONTROLS",
    title: "Контролы",
    description: "Кнопки, поля, выбор, Environment Settings и сворачиваемые секции нового Engine.",
    icon: "sliders",
  },
  media: {
    eyebrow: "03 / MEDIA & SETTINGS",
    title: "Медиа и настройки",
    description: "Общий CMS/Launcher-контракт файла, URL, превью и нейтральных карточек настроек.",
    icon: "image",
  },
  windows: {
    eyebrow: "04 / WINDOWS",
    title: "Окна",
    description: "Обычные окна и modeless-панели с единым portal- и focus-контрактом.",
    icon: "apps",
  },
  modals: {
    eyebrow: "05 / MODALS",
    title: "Модалки",
    description: "Полная карта modal-паттернов Launcher, нового Engine и BIM Viewer.",
    icon: "clipboard",
  },
  icons: {
    eyebrow: "06 / ICONS",
    title: "Иконки",
    description: "Канонический общий набор по Launcher, SEO, BIM Viewer и новым участкам Engine.",
    icon: "grid",
  },
};

const iconGroups: Array<{ title: string; note: string; icons: IconName[] }> = [
  {
    title: "Окно и слой",
    note: "Launcher / Hub",
    icons: ["close", "plus", "expand", "minimize", "refresh", "panel", "apps"],
  },
  {
    title: "Навигация",
    note: "Launcher / Engine / BIM",
    icons: ["chevron-left", "chevron-right", "chevron-down", "grid", "list", "sliders", "search"],
  },
  {
    title: "Редактирование",
    note: "Launcher / SEO",
    icons: ["save", "edit", "trash", "copy", "upload", "download", "external"],
  },
  {
    title: "Состояние и доступ",
    note: "Вся платформа",
    icons: ["check", "alert", "activity", "lock", "key", "shield", "circle"],
  },
  {
    title: "Сущности",
    note: "Общий словарь",
    icons: ["profile", "users", "building", "globe", "database", "network", "inbox", "mail"],
  },
  {
    title: "Контент",
    note: "SEO / BIM / Engine",
    icons: ["image", "video", "file", "folder", "clipboard", "settings"],
  },
];

const iconLabels: Record<IconName, string> = {
  activity: "Активность",
  alert: "Предупреждение",
  apps: "Приложения",
  building: "Компания",
  check: "Готово",
  "chevron-down": "Раскрыть",
  "chevron-left": "Назад",
  "chevron-right": "Вперёд",
  circle: "Маркер",
  clipboard: "Список",
  close: "Закрыть",
  copy: "Копировать",
  database: "База данных",
  download: "Скачать",
  edit: "Редактировать",
  expand: "Развернуть",
  external: "Открыть снаружи",
  file: "Файл",
  folder: "Папка",
  globe: "Публичный контур",
  grid: "Обзор",
  image: "Изображение",
  inbox: "Уведомления",
  key: "Ключ",
  list: "Список задач",
  lock: "Закрыто",
  mail: "Приглашение",
  minimize: "Свернуть",
  network: "Связи",
  panel: "Панель",
  plus: "Добавить",
  profile: "Профиль",
  refresh: "Обновить",
  save: "Сохранить",
  search: "Поиск",
  settings: "Настройки",
  shield: "Доступ",
  sliders: "Параметры",
  trash: "Удалить",
  upload: "Загрузить",
  users: "Участники",
  video: "Видео",
};

function rgbFromHex(hex: string): RgbTuple | null {
  const normalized = hex.trim().replace(/^#/, "");
  if (!/^[0-9a-f]{6}$/i.test(normalized)) return null;
  return [
    Number.parseInt(normalized.slice(0, 2), 16),
    Number.parseInt(normalized.slice(2, 4), 16),
    Number.parseInt(normalized.slice(4, 6), 16),
  ];
}

function Preview({ title, note, children, className }: {
  title: string;
  note?: string;
  children: ReactNode;
  className?: string;
}) {
  return (
    <GlassSurface className={`catalog-preview ${className ?? ""}`} padding="lg">
      <div className="catalog-preview__head">
        <strong>{title}</strong>
        {note ? <span>{note}</span> : null}
      </div>
      <div className="catalog-preview__body">{children}</div>
    </GlassSurface>
  );
}

export function CatalogApp() {
  const [theme, setTheme] = useState<NodedcTheme>("dark");
  const [accentHex, setAccentHex] = useState("#ff2f92");
  const [materialByTheme, setMaterialByTheme] = useState<Record<NodedcTheme, MaterialDraft>>(() => ({
    dark: { ...materialDefaults.dark },
    light: { ...materialDefaults.light },
  }));
  const [environmentLayoutSaved, setEnvironmentLayoutSaved] = useState(false);
  const workspace = useApplicationWorkspace<CatalogSection>();
  const { navigationOpen: guidelineOpen, activeView: activeSection, contentExpanded: panelExpanded } = workspace;
  const [selectedStatus, setSelectedStatus] = useState<(typeof selectOptions)[number]["value"]>("active");
  const [checker, setChecker] = useState(true);
  const [brightness, setBrightness] = useState(49);
  const [glowDistance, setGlowDistance] = useState(105);
  const [usePortColors, setUsePortColors] = useState(false);
  const [connectionType, setConnectionType] = useState("spline");
  const [lightColor, setLightColor] = useState("#ff2f92");
  const [connectionColor, setConnectionColor] = useState("#404040");
  const [fillColor, setFillColor] = useState("#ff6caf");
  const [fillOpacity, setFillOpacity] = useState(100);
  const [strokeColor, setStrokeColor] = useState("#2b2b36");
  const [strokeOpacity, setStrokeOpacity] = useState(100);
  const [windowOpen, setWindowOpen] = useState(false);
  const [sidePanelOpen, setSidePanelOpen] = useState(false);
  const [createModalOpen, setCreateModalOpen] = useState(false);
  const [confirmOpen, setConfirmOpen] = useState(false);
  const [shareAccessOpen, setShareAccessOpen] = useState(false);
  const [shareLinkOpen, setShareLinkOpen] = useState(false);
  const [contextActionOpen, setContextActionOpen] = useState(false);
  const [historyOpen, setHistoryOpen] = useState(false);
  const [detailOpen, setDetailOpen] = useState(false);
  const [detailExpanded, setDetailExpanded] = useState(false);
  const [projectName, setProjectName] = useState("NODE.DC Platform");
  const [notes, setNotes] = useState("Общий контракт компонентов без доменной логики приложения.");
  const [mediaSource, setMediaSource] = useState<"file" | "url">("file");
  const [mediaUrl, setMediaUrl] = useState("");
  const [mediaFileName, setMediaFileName] = useState("launcher-stage.mp4");
  const [fileMediaSrc, setFileMediaSrc] = useState("/launcher-stage.mp4");
  const [mediaError, setMediaError] = useState("");
  const [mediaVisible, setMediaVisible] = useState(true);
  const mediaObjectUrlRef = useRef<string | null>(null);
  const [shareEmail, setShareEmail] = useState("");
  const [shareRole, setShareRole] = useState<ShareRole>("editor");
  const [shareMessage, setShareMessage] = useState("");
  const [shareMembers, setShareMembers] = useState<Array<ShareAccessMember<ShareRole>>>([
    { id: "owner", name: "DC Constructions", email: "dc@nodedc.ru", role: "admin", roleLabel: "Владелец", immutable: true },
    { id: "editor", name: "Maria Petrova", email: "maria@nodedc.ru", role: "editor", roleLabel: "Редактор" },
  ]);

  const externalMediaSrc = /^(https?:)?\/\//i.test(mediaUrl) && /\.(mp4|webm|mov|m4v)(\?.*)?$/i.test(mediaUrl)
    ? mediaUrl
    : null;
  const stageMediaSrc = mediaSource === "url" && externalMediaSrc ? externalMediaSrc : fileMediaSrc;

  const accent = useMemo(() => rgbFromHex(accentHex) ?? accents[0].value, [accentHex]);
  const material = materialByTheme[theme];
  const panelMaterial = useMemo(() => rgbFromHex(material.panelHex) ?? rgbFromHex(materialDefaults[theme].panelHex)!, [material.panelHex, theme]);
  const fieldMaterial = useMemo(() => rgbFromHex(material.fieldHex) ?? rgbFromHex(materialDefaults[theme].fieldHex)!, [material.fieldHex, theme]);

  useEffect(() => {
    applyNodedcTheme(document.documentElement, {
      theme,
      accent,
      material: {
        panel: panelMaterial,
        panelOpacity: material.panelOpacity / 100,
        field: fieldMaterial,
        fieldOpacity: material.fieldOpacity / 100,
      },
    });
  }, [accent, fieldMaterial, material.fieldOpacity, material.panelOpacity, panelMaterial, theme]);

  useEffect(() => () => {
    if (mediaObjectUrlRef.current) URL.revokeObjectURL(mediaObjectUrlRef.current);
  }, []);

  const handleStageMediaFile = (file?: File) => {
    if (!file) return;
    const isMp4 = file.type === "video/mp4" || /\.mp4$/i.test(file.name);
    if (!isMp4) {
      setMediaError("Для заставки нужен MP4-файл.");
      return;
    }
    if (mediaObjectUrlRef.current) URL.revokeObjectURL(mediaObjectUrlRef.current);
    const objectUrl = URL.createObjectURL(file);
    mediaObjectUrlRef.current = objectUrl;
    setFileMediaSrc(objectUrl);
    setMediaFileName(file.name);
    setMediaSource("file");
    setMediaVisible(true);
    setMediaError("");
  };

  const inviteShareMember = () => {
    const email = shareEmail.trim().toLowerCase();
    if (!email) return;
    setShareMembers((current) => [
      ...current.filter((member) => member.email !== email),
      { id: email, name: email.split("@")[0], email, role: shareRole, roleLabel: shareRoleOptions.find((item) => item.value === shareRole)?.label },
    ]);
    setShareMessage(`Доступ выдан пользователю ${email}`);
    setShareEmail("");
  };

  const openSection = (next: string) => {
    workspace.openView(next as CatalogSection);
  };

  const closeGuideline = workspace.closeNavigation;

  const updateMaterial = (changes: Partial<MaterialDraft>) => {
    setMaterialByTheme((current) => ({
      ...current,
      [theme]: { ...current[theme], ...changes },
    }));
  };

  const saveEnvironmentLayout = () => {
    const draft = {
      theme,
      accentHex,
      material,
      environment: {
        lightColor,
        brightness,
        glowDistance,
        connectionType,
        connectionColor,
        usePortColors,
        fillColor,
        fillOpacity,
        strokeColor,
        strokeOpacity,
      },
    };
    try {
      window.localStorage.setItem("nodedc-design-guideline-draft", JSON.stringify(draft));
    } catch {
      // The preview remains useful in restricted browser contexts even without local persistence.
    }
    setEnvironmentLayoutSaved(true);
  };

  const environmentSections = [
    {
      id: "application-material",
      label: "Материал приложения",
      description: "live draft",
      tone: "accent" as const,
      content: (
        <>
          <ControlRow label="Акцент">
            <ColorField label="Акцент приложения" value={accentHex} onChange={setAccentHex} />
          </ControlRow>
          <ControlRow label="Плашки">
            <ColorField label="Цвет плашек" value={material.panelHex} onChange={(panelHex) => updateMaterial({ panelHex })} />
          </ControlRow>
          <ControlRow label="Прозрачность плашки">
            <RangeControl label="Прозрачность плашки" showLabel={false} value={material.panelOpacity} min={18} max={96} formatValue={(value) => `${value}%`} onChange={(panelOpacity) => updateMaterial({ panelOpacity })} />
          </ControlRow>
          <ControlRow label="Поля ввода">
            <ColorField label="Цвет полей ввода" value={material.fieldHex} onChange={(fieldHex) => updateMaterial({ fieldHex })} />
          </ControlRow>
          <ControlRow label="Прозрачность полей">
            <RangeControl label="Прозрачность полей" showLabel={false} value={material.fieldOpacity} min={18} max={100} formatValue={(value) => `${value}%`} onChange={(fieldOpacity) => updateMaterial({ fieldOpacity })} />
          </ControlRow>
        </>
      ),
    },
    {
      id: "illumination",
      label: "Подсветка",
      tone: "accent" as const,
      content: (
        <>
          <ControlRow label="Цвет"><ColorField value={lightColor} onChange={setLightColor} /></ControlRow>
          <RangeControl label="Яркость" value={brightness} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setBrightness} />
          <RangeControl label="Дистанция свечения" value={glowDistance} min={0} max={200} formatValue={(value) => `${value}%`} onChange={setGlowDistance} />
        </>
      ),
    },
    {
      id: "connections",
      label: "Связи",
      tone: "accent" as const,
      content: (
        <>
          <ControlRow label="Связи по умолчанию">
            <Select
              variant="split"
              label="Тип связи"
              value={connectionType}
              options={[
                { value: "spline", label: "Сплайн" },
                { value: "straight", label: "Прямая" },
                { value: "step", label: "Ступенчатая" },
              ]}
              onChange={setConnectionType}
            />
          </ControlRow>
          <Checker checked={usePortColors} label="Использовать цвет портов" onChange={setUsePortColors} />
          <ControlRow label="Цвет связей по умолчанию"><ColorField value={connectionColor} onChange={setConnectionColor} /></ControlRow>
          <ControlRow label="Применить ко всем"><Button variant="primary" shape="pill" width="full">Применить</Button></ControlRow>
        </>
      ),
    },
    {
      id: "ports",
      label: "Порты",
      tone: "accent" as const,
      content: (
        <>
          <ControlRow label="Заливка"><ColorField value={fillColor} onChange={setFillColor} /></ControlRow>
          <RangeControl label="Прозрачность заливки" value={fillOpacity} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setFillOpacity} />
          <ControlRow label="Обводка"><ColorField value={strokeColor} onChange={setStrokeColor} /></ControlRow>
          <RangeControl label="Прозрачность обводки" value={strokeOpacity} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setStrokeOpacity} />
        </>
      ),
    },
  ];

  const renderSectionContent = () => {
    switch (activeSection) {
      case "foundation":
        return (
          <div className="catalog-grid catalog-grid--foundation">
            <Preview title="Тема приложения" note={theme === "dark" ? "Launcher / dark" : "SEO / light"}>
              <div className="catalog-theme-row">
                <Button variant={theme === "dark" ? "primary" : "secondary"} shape="pill" onClick={() => setTheme("dark")}>Dark</Button>
                <Button variant={theme === "light" ? "primary" : "secondary"} shape="pill" onClick={() => setTheme("light")}>Light</Button>
              </div>
              <p className="catalog-preview__explanation">Две базовые темы фиксированы по эталонам: чёрная — Launcher/Hub, белая — SEO. Стекло используется только в модальных окнах.</p>
            </Preview>
            <Preview title="Поверхности приложения" note="Launcher / SEO">
              <div className="catalog-surface-stack">
                <GlassSurface padding="md">Основная поверхность</GlassSurface>
                <GlassSurface tone="strong" padding="md">Контентное окно</GlassSurface>
                <GlassSurface tone="soft" padding="md">Вложенная область</GlassSurface>
              </div>
            </Preview>
            <Preview title="Каркас Launcher" note="fixed header / stage / panels" className="catalog-preview--wide">
              <div className="catalog-shell-diagram" aria-label="Схема каркаса приложения">
                <span className="catalog-shell-diagram__header">Header</span>
                <span className="catalog-shell-diagram__nav">Navigation</span>
                <span className="catalog-shell-diagram__content">Content window</span>
                <span className="catalog-shell-diagram__stage">Stage</span>
              </div>
            </Preview>
          </div>
        );
      case "controls":
        return (
          <div className="catalog-grid">
            <Preview title="Действия" note="общая геометрия">
              <div className="catalog-inline catalog-inline--wrap">
                <Button variant="primary" shape="pill" icon={<Icon name="save" />}>Сохранить</Button>
                <Button icon={<Icon name="refresh" />}>Обновить</Button>
                <Button variant="ghost" icon={<Icon name="external" />}>Открыть</Button>
                <Button variant="danger" icon={<Icon name="trash" />}>Удалить</Button>
                <IconButton label="Добавить"><Icon name="plus" /></IconButton>
              </div>
            </Preview>
            <Preview title="Поля" note="label / hint / control">
              <div className="catalog-form">
                <TextField label="Название проекта" hint="обязательно" value={projectName} onChange={(event) => setProjectName(event.target.value)} />
                <TextAreaField label="Описание" value={notes} onChange={(event) => setNotes(event.target.value)} />
              </div>
            </Preview>
            <Preview title="Selection" note="integrated / split">
              <div className="catalog-form">
                <FieldFrame label="Hub / integrated select">
                  <Select label="Статус" value={selectedStatus} options={[...selectOptions]} searchable onChange={setSelectedStatus} />
                </FieldFrame>
                <FieldFrame label="Engine / split select">
                  <Select
                    variant="split"
                    label="Тип связи"
                    value={connectionType}
                    options={[
                      { value: "spline", label: "Сплайн" },
                      { value: "straight", label: "Прямая" },
                      { value: "step", label: "Ступенчатая" },
                    ]}
                    onChange={setConnectionType}
                  />
                </FieldFrame>
                <Dropdown
                  trigger={({ open, toggle, setTriggerRef, surfaceId }) => (
                    <Button ref={setTriggerRef} aria-expanded={open} aria-controls={surfaceId} icon={<Icon name="chevron-down" />} onClick={toggle}>Действия</Button>
                  )}
                >
                  {({ close }) => (
                    <>
                      <button className="nodedc-dropdown-option" data-nodedc-option onClick={close}><Icon name="edit" /><span className="nodedc-dropdown-option__label">Переименовать</span></button>
                      <button className="nodedc-dropdown-option" data-nodedc-option onClick={close}><Icon name="copy" /><span className="nodedc-dropdown-option__label">Дублировать</span></button>
                      <button className="nodedc-dropdown-option" data-nodedc-option onClick={close}><Icon name="folder" /><span className="nodedc-dropdown-option__label">Архивировать</span></button>
                    </>
                  )}
                </Dropdown>
              </div>
            </Preview>
            <Preview title="Environment controls" note="Engine / select + accordion" className="catalog-preview--wide">
              <div className="catalog-environment-demo">
                <GlassSurface tone="strong" radius="panel" className="catalog-environment-card">
                  <div className="catalog-environment-title">
                    <div><strong>Настройки окружения</strong><span>ENGINE / новый канон</span></div>
                    <IconButton label="Сохранить текущий layout" onClick={saveEnvironmentLayout}><Icon name="save" /></IconButton>
                  </div>
                  {environmentLayoutSaved ? <p className="catalog-environment-draft" aria-live="polite">Layout сохранён локально. Сервер и MCP не используются.</p> : null}
                  <Inspector sections={environmentSections} defaultOpen={["application-material"]} />
                </GlassSurface>
                <p className="catalog-preview__explanation">Environment controls — единый референс Engine: palette, range, checker, split-select и раскрывающиеся sections. Верхняя секция меняет цвет плашек и полей приложения; сохраняется весь текущий layout одной кнопкой сверху.</p>
              </div>
            </Preview>
            <Preview title="Статусы операций" note="не цвет приложения" className="catalog-preview--wide">
              <p className="catalog-preview__explanation">Статус сообщает результат операции: нейтрально, успешно, требуется внимание или ошибка. Акцент приложения для этого не переиспользуется.</p>
              <div className="catalog-inline catalog-inline--wrap">
                <StatusBadge>Черновик</StatusBadge>
                <StatusBadge tone="success">Синхронизировано</StatusBadge>
                <StatusBadge tone="warning">Нужна проверка</StatusBadge>
                <StatusBadge tone="danger">Ошибка</StatusBadge>
              </div>
            </Preview>
          </div>
        );
      case "media":
        return (
          <div className="catalog-grid catalog-grid--single">
            <SettingsCard
              eyebrow="CONTENT"
              title="Видео-окно"
              description="Окно демо-видео и подключённый медиа-файл. Хранилище и загрузка остаются в приложении; геометрия и переключение источника — в библиотеке."
              actions={<Switch checked={mediaVisible} label="Показать" onChange={setMediaVisible} />}
            >
              <MediaSourceField
                label="Видео / картинка"
                source={mediaSource}
                url={mediaUrl}
                fileName={mediaFileName}
                previewSrc={stageMediaSrc}
                previewKind="video"
                accept="video/mp4,.mp4"
                path="stage.videoSrc → runtime media source"
                hint="Выбранный MP4 сразу подставляется в главное фоновое окно; backend-adapter может сохранить его в storage."
                error={mediaError}
                onSourceChange={setMediaSource}
                onUrlChange={(nextUrl) => {
                  setMediaUrl(nextUrl);
                  setMediaError(nextUrl && !/\.(mp4|webm|mov|m4v)(\?.*)?$/i.test(nextUrl) ? "Ссылка должна вести на видеофайл." : "");
                }}
                onFileChange={handleStageMediaFile}
              />
            </SettingsCard>
            <Preview title="Граница ответственности" note="component / adapter" className="catalog-preview--wide">
              <p className="catalog-preview__explanation">
                `MediaSourceField` владеет пикселями, доступностью и состояниями file/URL. Конкретный проект передаёт upload-обработчик, storage-path и результат валидации — поэтому компонент можно подключить к CMS, Launcher или будущему приложению без копирования его вёрстки.
              </p>
            </Preview>
          </div>
        );
      case "windows":
        return (
          <div className="catalog-grid catalog-grid--single">
            <Preview title="Управляемые окна" note="center / modeless end">
              <p className="catalog-preview__explanation">Обычное окно блокирует фон и удерживает фокус. Правый инспектор не затемняет, не блокирует stage и не превращается в modal.</p>
              <div className="catalog-inline catalog-inline--wrap">
                <Button variant="primary" shape="pill" icon={<Icon name="apps" />} onClick={() => setWindowOpen(true)}>Открыть окно</Button>
                <Button shape="pill" icon={<Icon name="panel" />} onClick={() => setSidePanelOpen(true)}>Открыть боковую панель</Button>
              </div>
            </Preview>
            <Preview title="Оконные действия" note="круг `46 px`">
              <div className="catalog-window-actions-demo">
                <Button shape="pill" icon={<Icon name="refresh" />}>Обновить источник</Button>
                <IconButton label="Добавить"><Icon name="plus" /></IconButton>
                <IconButton label="Развернуть"><Icon name="expand" /></IconButton>
                <IconButton label="Закрыть"><Icon name="close" /></IconButton>
              </div>
            </Preview>
          </div>
        );
      case "modals":
        return (
          <div className="catalog-grid">
            <Preview title="Создание" note="форма адаптируется по ширине">
              <p className="catalog-preview__explanation">Поля переходят в одну колонку на узком viewport; footer остаётся видимым, прокручивается только тело окна.</p>
              <Button variant="primary" shape="pill" icon={<Icon name="plus" />} onClick={() => setCreateModalOpen(true)}>Создать проект</Button>
            </Preview>
            <Preview title="Подтверждение удаления" note="async-safe">
              <p className="catalog-preview__explanation">Destructive-цвет появляется у подтверждения, а не окрашивает всё окно заранее.</p>
              <Button variant="danger" shape="pill" icon={<Icon name="trash" />} onClick={() => setConfirmOpen(true)}>Проверить удаление</Button>
            </Preview>
            <Preview title="Workflow sharing" note="ENGINE / access modal">
              <p className="catalog-preview__explanation">Участники, роли, приглашение и immutable-владелец. API доступа остаётся в Engine.</p>
              <Button variant="primary" shape="pill" icon={<Icon name="users" />} onClick={() => setShareAccessOpen(true)}>Поделиться графом</Button>
            </Preview>
            <Preview title="Ссылка на модель" note="BIM / share link">
              <p className="catalog-preview__explanation">Read-only ссылка, copy-state и единая механика focus/Escape/backdrop.</p>
              <Button shape="pill" icon={<Icon name="copy" />} onClick={() => setShareLinkOpen(true)}>Открыть ссылку</Button>
            </Preview>
            <Preview title="Контекстные действия" note="BIM / measurement & object">
              <p className="catalog-preview__explanation">Короткое окно действий над выбранным размером или объектом без отдельной modal-системы.</p>
              <Button shape="pill" icon={<Icon name="sliders" />} onClick={() => setContextActionOpen(true)}>Действия размера</Button>
            </Preview>
            <Preview title="История версий" note="BIM / large data modal">
              <p className="catalog-preview__explanation">Большое прокручиваемое data-окно; BIM-таблица остаётся доменной композицией внутри общего Window.</p>
              <Button shape="pill" icon={<Icon name="list" />} onClick={() => setHistoryOpen(true)}>Открыть историю</Button>
            </Preview>
            <Preview title="Комментарий к модели" note="BIM / expandable detail">
              <p className="catalog-preview__explanation">Рабочая detail-modal с контентом и вложениями, которая может расширяться без смены механики слоя.</p>
              <Button shape="pill" icon={<Icon name="clipboard" />} onClick={() => setDetailOpen(true)}>Открыть комментарий</Button>
            </Preview>
          </div>
        );
      case "icons":
        return (
          <div className="catalog-icon-catalog">
            <Preview title="Размеры и поверхности" note="16 / 18 / 20 px" className="catalog-preview--wide">
              <div className="catalog-window-actions-demo">
                <IconButton label="Добавить"><Icon name="plus" size={16} /></IconButton>
                <IconButton label="Обновить"><Icon name="refresh" size={18} /></IconButton>
                <IconButton label="Развернуть"><Icon name="expand" size={20} /></IconButton>
                <IconButton label="Закрыть"><Icon name="close" size={18} /></IconButton>
              </div>
              <p className="catalog-preview__explanation">Иконка наследует `currentColor`; фон, размер кнопки и active-state задаёт компонент поверхности, а не SVG. Glyph оптически уменьшен на 18% без изменения hit target.</p>
            </Preview>
            {iconGroups.map((group) => (
              <section className="catalog-icon-group" key={group.title}>
                <header><strong>{group.title}</strong><span>{group.note}</span></header>
                <div className="catalog-icon-grid">
                  {group.icons.map((name) => (
                    <div className="catalog-icon-tile" key={name}>
                      <span className="catalog-icon-tile__surface"><Icon name={name} size={20} /></span>
                      <span><strong>{iconLabels[name]}</strong><code>{name}</code></span>
                    </div>
                  ))}
                </div>
              </section>
            ))}
          </div>
        );
      default:
        return null;
    }
  };

  const activeDefinition = activeSection ? sectionDefinitions[activeSection] : null;

  return (
    <>
    <ApplicationShell
      className="catalog-app nodedc-ui-root"
      style={{ "--catalog-accent": accentHex } as CSSProperties}
      navigationOpen={guidelineOpen}
      contentOpen={workspace.contentOpen}
      contentExpanded={panelExpanded}
      header={
        <AppHeader
          brand={<img src="/nodedc-logo.svg" alt="NODE.DC" />}
          brandHref="/"
          center={
            <>
              <HeaderWorkspace kind="mark" label="NODE.DC Design" imageUrl="/nodedc-mark.svg" />
              <HeaderNavigation
                label="Навигация приложения"
                value={guidelineOpen ? "guideline" : undefined}
                items={[{ value: "guideline", label: "Guideline" }]}
                onChange={() => guidelineOpen ? closeGuideline() : workspace.openNavigation()}
              />
            </>
          }
          right={
            <HeaderProfile>
              <IconButton label="Уведомления"><Icon name="inbox" size={20} /></IconButton>
              <HeaderProfileButton>Профиль</HeaderProfileButton>
              <HeaderAvatar label="DC" />
            </HeaderProfile>
          }
        />
      }
      stage={
        <section className="catalog-launcher-stage">
          <video key={stageMediaSrc} className="catalog-launcher-stage__media" hidden={!mediaVisible} autoPlay muted loop playsInline poster="/launcher-stage-poster.png" aria-hidden="true">
            <source src={stageMediaSrc} type="video/mp4" />
          </video>
          <div className="catalog-launcher-stage__shade" />
          <div className="catalog-launcher-stage__title">
            <span>NODE.DC</span>
            <strong>DESIGN<br />GUIDELINE</strong>
            <p>Общий форм-фактор приложений, компоненты и оконная механика платформы.</p>
          </div>
        </section>
      }
      navigation={
        <AdminNavigationPanel
          eyebrow="NODE.DC"
          title="Visual Guideline"
          closeLabel="Закрыть Guideline"
          navigationLabel="Разделы Guideline"
          onClose={closeGuideline}
          items={(Object.entries(sectionDefinitions) as Array<[CatalogSection, (typeof sectionDefinitions)[CatalogSection]]>).map(([id, item]) => ({
            id,
            label: item.title,
            icon: <Icon name={item.icon} />,
          }))}
          activeId={activeSection ?? undefined}
          onItemChange={openSection}
          footer={
            <>
              <span className="nodedc-admin-panel__nav-icon" aria-hidden="true"><Icon name="shield" /></span>
              <span>Design System 0.5.1</span>
            </>
          }
        />
      }
      content={activeDefinition ? (
        <ApplicationPanel
          key={activeSection}
          eyebrow={activeDefinition.eyebrow}
          title={activeDefinition.title}
          description={activeDefinition.description}
          expanded={panelExpanded}
          onExpandedChange={workspace.setContentExpanded}
          onClose={workspace.closeView}
        >
          <div className="catalog-panel-content">{renderSectionContent()}</div>
        </ApplicationPanel>
      ) : null}
    />

    <Window
      open={windowOpen}
      title="Настройки сервиса"
      subtitle="Launcher / CMS / SEO"
      onClose={() => setWindowOpen(false)}
      footer={
        <>
          <Button variant="ghost" onClick={() => setWindowOpen(false)}>Отмена</Button>
          <WindowFooterActions><Button variant="primary" shape="pill" onClick={() => setWindowOpen(false)}>Сохранить</Button></WindowFooterActions>
        </>
      }
    >
      <div className="catalog-form">
        <TextField label="Название" value={projectName} onChange={(event) => setProjectName(event.target.value)} />
        <FieldFrame label="Статус"><Select label="Статус" value={selectedStatus} options={[...selectOptions]} onChange={setSelectedStatus} /></FieldFrame>
        <Checker checked={checker} label="Сервис активен" onChange={setChecker} />
      </div>
    </Window>

    <Window
      open={sidePanelOpen}
      title="Боковая панель"
      subtitle="modeless / shell contract"
      placement="end"
      onClose={() => setSidePanelOpen(false)}
    >
      <div className="catalog-form">
        <TextField label="Название панели" value="Параметры рабочей области" readOnly />
        <p className="catalog-preview__explanation">Боковая панель демонстрирует только layer-механику: она modeless, не затемняет stage и не забирает фокус. Environment controls живут в разделе «Контролы».</p>
      </div>
    </Window>

    <Window
      open={createModalOpen}
      title="Создать проект"
      subtitle="Адаптивная форма"
      size="sm"
      onClose={() => setCreateModalOpen(false)}
      footer={
        <>
          <Button variant="ghost" onClick={() => setCreateModalOpen(false)}>Отмена</Button>
          <WindowFooterActions><Button variant="primary" shape="pill" icon={<Icon name="plus" />} onClick={() => setCreateModalOpen(false)}>Создать</Button></WindowFooterActions>
        </>
      }
    >
      <div className="catalog-form">
        <TextField label="Название проекта" hint="обязательно" value={projectName} onChange={(event) => setProjectName(event.target.value)} />
        <TextAreaField label="Описание" value={notes} onChange={(event) => setNotes(event.target.value)} />
        <FieldFrame label="Статус"><Select label="Статус" value={selectedStatus} options={[...selectOptions]} onChange={setSelectedStatus} /></FieldFrame>
      </div>
    </Window>

    <ShareAccessModal<ShareRole>
      open={shareAccessOpen}
      resourceName="Workflow / Rail infrastructure analysis"
      members={shareMembers}
      roleOptions={shareRoleOptions}
      inviteEmail={shareEmail}
      inviteRole={shareRole}
      message={shareMessage}
      messageTone="accent"
      onInviteEmailChange={(email) => {
        setShareEmail(email);
        setShareMessage("");
      }}
      onInviteRoleChange={setShareRole}
      onInvite={inviteShareMember}
      onMemberRoleChange={(member, role) => {
        setShareMembers((current) => current.map((item) => item.id === member.id
          ? { ...item, role, roleLabel: shareRoleOptions.find((option) => option.value === role)?.label }
          : item));
      }}
      onMemberRemove={(member) => setShareMembers((current) => current.filter((item) => item.id !== member.id))}
      onClose={() => setShareAccessOpen(false)}
    />

    <ShareLinkModal
      open={shareLinkOpen}
      link="https://bim.nodedc.ru/model/rail-hub?view=main"
      onCopy={() => undefined}
      onClose={() => setShareLinkOpen(false)}
    />

    <Window
      open={contextActionOpen}
      title="Размер"
      subtitle="BIM / contextual actions"
      size="sm"
      onClose={() => setContextActionOpen(false)}
      footer={<WindowFooterActions><Button onClick={() => setContextActionOpen(false)}>Закрыть</Button></WindowFooterActions>}
    >
      <div className="catalog-modal-action-list">
        <Button width="full" icon={<Icon name="activity" />}>Скрыть размер</Button>
        <Button width="full" variant="danger" icon={<Icon name="trash" />}>Удалить размер</Button>
      </div>
    </Window>

    <Window
      open={historyOpen}
      title="История версий"
      subtitle="rail-hub-model.glb"
      size="lg"
      onClose={() => setHistoryOpen(false)}
    >
      <div className="catalog-history-table" role="table" aria-label="История версий модели">
        <div className="catalog-history-row catalog-history-row--head" role="row">
          <span>Файл</span><span>Версия</span><span>Автор</span><span>Статус</span><span>Действия</span>
        </div>
        {[
          ["rail-hub-model.glb", "v12 · текущая", "DC", "Модель готова"],
          ["rail-hub-model.glb", "v11", "Maria", "Модель готова"],
          ["rail-hub-model.glb", "v10", "Alex", "Ожидает подготовки"],
        ].map((row) => (
          <div className="catalog-history-row" role="row" key={row[1]}>
            <strong>{row[0]}</strong><span>{row[1]}</span><span>{row[2]}</span><span>{row[3]}</span>
            <span className="catalog-history-actions">
              <IconButton label="Посмотреть"><Icon name="external" /></IconButton>
              <IconButton label="Скачать"><Icon name="download" /></IconButton>
            </span>
          </div>
        ))}
      </div>
    </Window>

    <Window
      open={detailOpen}
      title="Комментарий к модели"
      subtitle="Опора №42 · рабочая detail-modal"
      size={detailExpanded ? "lg" : "md"}
      onClose={() => setDetailOpen(false)}
      footer={
        <>
          <Button icon={<Icon name={detailExpanded ? "minimize" : "expand"} />} onClick={() => setDetailExpanded((current) => !current)}>
            {detailExpanded ? "Свернуть" : "Развернуть"}
          </Button>
          <WindowFooterActions>
            <Button onClick={() => setDetailOpen(false)}>Отмена</Button>
            <Button variant="primary" onClick={() => setDetailOpen(false)}>Сохранить</Button>
          </WindowFooterActions>
        </>
      }
    >
      <div className="catalog-detail-modal">
        <TextField label="Заголовок" value="Проверить узел крепления" readOnly />
        <TextAreaField label="Описание" value="Нужно сверить положение опоры с последней версией модели." readOnly />
        <SettingsCard title="Вложения" description="Файлы и изображения остаются BIM-domain content.">
          <Button icon={<Icon name="upload" />}>Выбрать файл</Button>
        </SettingsCard>
      </div>
    </Window>

    <ConfirmationModal
      open={confirmOpen}
      title="Удалить конфигурацию?"
      description={<>Конфигурация будет удалена. Это действие нельзя отменить.</>}
      confirmLabel="Удалить"
      danger
      onClose={() => setConfirmOpen(false)}
      onConfirm={() => setConfirmOpen(false)}
    />
    </>
  );
}
