<script>
  import { ColorInput, FileInput, Input, ScrollArea, Textarea } from "$components/ui/index.js";
  import {
    Check,
    ChevronRight,
    Copy,
    Eye,
    EyeOff,
    FileText,
    Search,
    X,
  } from "$components/ui/icons.js";
  import * as UiIcons from "$components/ui/icons.js";
  import { Accordion, Switch } from "$components/ui/primitives.js";
  import Dialog from "$components/ui/dialog.svelte";
  import {
    AdminBadge,
    AdminButton,
    AdminEmptyState,
    AdminSelect,
  } from "$components/patterns/admin/index.js";
  import { getContext, onDestroy, onMount } from "svelte";

  export let at;
  export let onSettingsSaved;
  export let currentLang = "ru";

  const settingsStore = getContext("settingsStore");

  $: ({ settingsSections, settingsLoading, settingsDirty, settingsSaving } = $settingsStore);

  const SETTINGS_SECTION_IDS_HIDDEN_IN_GENERAL_SETTINGS = new Set(["appearance", "pricing"]);

  $: visibleSettingsSections = settingsSections.filter(
    (section) => !SETTINGS_SECTION_IDS_HIDDEN_IN_GENERAL_SETTINGS.has(section.id)
  );

  let settingsOpenSections = [];
  let settingsOpenSubsections = {};
  let revealedSecrets = new Set();
  let iconPickerField = null;
  let iconPickerSearch = "";
  let copiedWebhookKey = "";
  let copiedWebhookTimer = null;

  const PLATEGA_SBP_KEYS = new Set([
    "PLATEGA_SBP_ENABLED",
    "PLATEGA_SBP_ADMIN_ONLY_ENABLED",
    "PLATEGA_SBP_METHOD",
  ]);
  const PLATEGA_CRYPTO_KEYS = new Set([
    "PLATEGA_CRYPTO_ENABLED",
    "PLATEGA_CRYPTO_ADMIN_ONLY_ENABLED",
    "PLATEGA_CRYPTO_METHOD",
  ]);
  const PLATEGA_LEGACY_KEYS = new Set(["PLATEGA_PAYMENT_METHOD"]);
  const SEMANTIC_FIELD_GROUP_ORDER = {
    platega_common: 1,
    platega_sbp: 2,
    platega_crypto: 3,
    platega_legacy: 4,
  };

  $: settingsAllOpen =
    visibleSettingsSections.length > 0 &&
    settingsOpenSections.length === visibleSettingsSections.length;
  $: iconOptions = Object.keys(UiIcons)
    .filter((name) => /^[A-Z]/.test(name))
    .sort((a, b) => a.localeCompare(b));
  $: filteredIconOptions = iconOptions.filter((name) =>
    name.toLowerCase().includes(iconPickerSearch.trim().toLowerCase())
  );

  onMount(() => {
    settingsStore.loadSettings();
  });

  onDestroy(() => {
    if (copiedWebhookTimer && typeof window !== "undefined") {
      window.clearTimeout(copiedWebhookTimer);
    }
  });

  function toggleAllSections() {
    if (settingsOpenSections.length === visibleSettingsSections.length) {
      settingsOpenSections = [];
    } else {
      settingsOpenSections = visibleSettingsSections.map((s) => s.id);
    }
  }

  function valueFor(field) {
    if (settingsDirty[field.key]?.deleted) return "";
    if (Object.prototype.hasOwnProperty.call(settingsDirty, field.key)) {
      return settingsDirty[field.key].value;
    }
    return field.value ?? "";
  }

  function isOverridden(field) {
    return Boolean(field.overridden) && !settingsDirty[field.key]?.deleted;
  }

  function isSecretRevealed(key) {
    return revealedSecrets.has(key);
  }

  function toggleSecretReveal(key) {
    const next = new Set(revealedSecrets);
    if (next.has(key)) next.delete(key);
    else next.add(key);
    revealedSecrets = next;
  }

  function secretPlaceholder(field) {
    if (settingsDirty[field.key]?.deleted) return fieldPlaceholderText(field) || "********";
    if (field.has_value) return at("settings_secret_configured", {}, "Secret is set");
    return fieldPlaceholderText(field) || at("settings_secret_empty", {}, "Not set");
  }

  function iconComponent(name) {
    const key = String(name || "").trim();
    return key ? UiIcons[key] || null : null;
  }

  function iconValue(field) {
    return String(valueFor(field) || field?.placeholder || "").trim();
  }

  function iconIsDefault(field) {
    return !String(valueFor(field) || "").trim();
  }

  function iconLabel(field) {
    const iconName = iconValue(field);
    if (!iconName) return at("settings_icon_empty", {}, "Default icon");
    if (iconIsDefault(field)) {
      return at("settings_icon_default_value", { icon: iconName }, `Default: ${iconName}`);
    }
    return iconName;
  }

  function openIconPicker(field) {
    iconPickerField = field;
    iconPickerSearch = "";
  }

  function closeIconPicker() {
    iconPickerField = null;
    iconPickerSearch = "";
  }

  function selectIcon(name) {
    if (!iconPickerField) return;
    settingsStore.markDirty(iconPickerField.key, name);
    closeIconPicker();
  }

  async function handleJsonFile(field, event) {
    const file = event?.currentTarget?.files?.[0];
    if (!file) return;
    try {
      const text = await file.text();
      settingsStore.markDirty(field.key, text);
    } finally {
      event.currentTarget.value = "";
    }
  }

  function normalizeWebhookPath(path) {
    const normalized = String(path || "").trim();
    if (!normalized) return "";
    return normalized.startsWith("/") ? normalized : `/${normalized}`;
  }

  function webhookUrlForField(field) {
    const explicit = String(field?.webhook_url || "").trim();
    if (explicit) return explicit;
    const path = normalizeWebhookPath(field?.webhook_path);
    if (!path) return "";
    if (field?.webhook_requires_base_url && field?.webhook_base_url_configured === false) {
      return "";
    }
    if (typeof window !== "undefined" && window.location?.origin) {
      return `${window.location.origin}${path}`;
    }
    return path;
  }

  function groupWebhook(fields) {
    const field = (fields || []).find((item) => item.webhook_path || item.webhook_url);
    if (!field) return null;
    const path = normalizeWebhookPath(field.webhook_path);
    const url = webhookUrlForField(field);
    if (!url && !path) return null;
    return {
      key: `${field.provider_id || field.key || "provider"}:${path || url}`,
      path,
      url,
      requiresBaseUrl: Boolean(field.webhook_requires_base_url),
      baseConfigured: field.webhook_base_url_configured !== false,
      hintI18nKey: field.webhook_hint_i18n_key || "",
      hintFallback: field.webhook_hint || "",
    };
  }

  async function copyWebhookUrl(webhook) {
    if (!webhook?.url) return;
    try {
      await navigator.clipboard.writeText(webhook.url);
      copiedWebhookKey = webhook.key;
      if (copiedWebhookTimer && typeof window !== "undefined") {
        window.clearTimeout(copiedWebhookTimer);
      }
      if (typeof window !== "undefined") {
        copiedWebhookTimer = window.setTimeout(() => {
          copiedWebhookKey = "";
          copiedWebhookTimer = null;
        }, 1400);
      }
    } catch {
      copiedWebhookKey = "";
    }
  }

  function groupSectionFields(section) {
    const groups = new Map();
    for (const field of section.fields || []) {
      const key = field.subsection || "_root";
      if (!groups.has(key)) {
        groups.set(key, { fields: [], i18nLabelKey: field.i18n_subsection_key || null });
      }
      const group = groups.get(key);
      group.fields.push(field);
      if (!group.i18nLabelKey && field.i18n_subsection_key) {
        group.i18nLabelKey = field.i18n_subsection_key;
      }
    }
    return Array.from(groups.entries()).map(([id, group]) => ({
      id,
      label: id === "_root" ? null : id,
      i18nLabelKey: group.i18nLabelKey,
      webhook: groupWebhook(group.fields),
      fields: group.fields,
    }));
  }

  function fieldGroupMeta(
    id,
    titleKey,
    titleFallback,
    descriptionKey = "",
    descriptionFallback = ""
  ) {
    return { id, titleKey, titleFallback, descriptionKey, descriptionFallback };
  }

  function plategaSemanticGroup(field) {
    const key = String(field?.key || "");
    if (PLATEGA_SBP_KEYS.has(key) || key.startsWith("PAYMENT_PLATEGA_SBP_")) {
      return fieldGroupMeta(
        "platega_sbp",
        "settings_group_platega_sbp",
        "SBP/card button",
        "settings_group_platega_sbp_hint",
        "Visibility, method ID, and labels for the SBP/card payment button."
      );
    }
    if (PLATEGA_CRYPTO_KEYS.has(key) || key.startsWith("PAYMENT_PLATEGA_CRYPTO_")) {
      return fieldGroupMeta(
        "platega_crypto",
        "settings_group_platega_crypto",
        "Crypto button",
        "settings_group_platega_crypto_hint",
        "Visibility, method ID, and labels for the crypto payment button."
      );
    }
    if (PLATEGA_LEGACY_KEYS.has(key)) {
      return fieldGroupMeta(
        "platega_legacy",
        "settings_group_platega_legacy",
        "Legacy compatibility",
        "settings_group_platega_legacy_hint",
        "Fallback method for old Platega callbacks and deployments."
      );
    }
    return fieldGroupMeta(
      "platega_common",
      "settings_group_platega_common",
      "Common settings",
      "settings_group_platega_common_hint",
      "Shared merchant credentials, redirects, and API endpoint."
    );
  }

  function semanticFieldGroup(section, group, field) {
    if (section?.id === "payments" && group?.id === "Platega") {
      return plategaSemanticGroup(field);
    }
    return null;
  }

  function semanticFieldGroups(section, group) {
    const fields = group?.fields || [];
    const result = new Map();
    for (const field of fields) {
      const meta = semanticFieldGroup(section, group, field) || fieldGroupMeta("_default", "", "");
      if (!result.has(meta.id)) {
        result.set(meta.id, { ...meta, fields: [] });
      }
      result.get(meta.id).fields.push(field);
    }
    return Array.from(result.values()).sort(
      (a, b) =>
        (SEMANTIC_FIELD_GROUP_ORDER[a.id] || 999) - (SEMANTIC_FIELD_GROUP_ORDER[b.id] || 999)
    );
  }

  function fieldGroupTitle(group) {
    return group.titleKey ? at(group.titleKey, {}, group.titleFallback) : "";
  }

  function fieldGroupDescription(group) {
    return group.descriptionKey ? at(group.descriptionKey, {}, group.descriptionFallback) : "";
  }

  function adminLocaleKey(key) {
    const raw = String(key || "");
    return raw.startsWith("admin_") ? raw.slice("admin_".length) : raw;
  }

  function adminText(key, params = {}, fallback = "") {
    return key ? at(adminLocaleKey(key), params, fallback) : fallback;
  }

  function sectionTitle(id) {
    const map = {
      general: "Общие",
      remnawave: "Remnawave Panel",
      appearance: "Внешний вид",
      pricing: "Тарифы и цены",
      payments: "Платёжные системы",
      trial: "Триал",
      referral: "Реферальная программа",
      notifications: "Уведомления",
      backups: "Бэкапы",
      support: "Поддержка",
      devices: "Устройства",
      subscription_guides: "Connection guides",
      system: "Система",
      migrations: "Миграции",
    };
    return adminText(`settings_section_${id}`, {}, map[id] || id);
  }

  function englishFieldLabelFallback(key, originalLabel) {
    if (!key) return originalLabel || "";
    return String(key)
      .toLowerCase()
      .split("_")
      .filter(Boolean)
      .map((part) => {
        if (part === "id") return "ID";
        if (part === "url") return "URL";
        if (part === "api") return "API";
        if (part === "tg") return "TG";
        return part.charAt(0).toUpperCase() + part.slice(1);
      })
      .join(" ");
  }

  function fieldLabelText(field) {
    const isEnglish = String(currentLang || "")
      .toLowerCase()
      .startsWith("en");
    const fallback = isEnglish ? englishFieldLabelFallback(field.key, field.label) : field.label;
    return field.i18n_label_key ? adminText(field.i18n_label_key, {}, fallback) : fallback;
  }

  function fieldDescriptionText(field) {
    if (!field.description) return "";
    return field.i18n_description_key
      ? adminText(field.i18n_description_key, {}, field.description)
      : field.description;
  }

  function fieldPlaceholderText(field) {
    const fallback = field.placeholder || "";
    return field.i18n_placeholder_key
      ? adminText(field.i18n_placeholder_key, {}, fallback)
      : fallback;
  }

  function subsectionTitle(group) {
    if (!group?.label) return "";
    return group.i18nLabelKey ? adminText(group.i18nLabelKey, {}, group.label) : group.label;
  }

  function choiceItems(field) {
    return (field.choices || []).map((choice) => ({
      ...choice,
      label: choice.i18n_label_key
        ? adminText(choice.i18n_label_key, {}, choice.label)
        : choice.label,
    }));
  }

  function setBoolField(field, checked) {
    settingsStore.markDirty(field.key, checked);
    if (checked && field.mutually_exclusive_key) {
      settingsStore.markDirty(field.mutually_exclusive_key, false);
    }
  }
</script>

{#snippet renderWebhookHint(webhook)}
  {@const displayValue = webhook.url || webhook.path}
  <div class="admin-webhook-hint">
    <div class="admin-webhook-hint-meta">
      <strong>{at("settings_provider_webhook_url", {}, "Webhook URL")}</strong>
      <small>
        {webhook.url
          ? at(
              adminLocaleKey(webhook.hintI18nKey || "settings_provider_webhook_url_hint"),
              {},
              webhook.hintFallback || "Use this URL in the provider webhook settings."
            )
          : at(
              "settings_provider_webhook_base_missing",
              { path: webhook.path },
              `Set WEBHOOK_BASE_URL to show the full URL for ${webhook.path}.`
            )}
      </small>
    </div>
    <div class="admin-webhook-value">
      <code title={displayValue}>{displayValue}</code>
      <AdminButton
        class="admin-webhook-copy"
        size="sm"
        variant="ghost"
        disabled={!webhook.url}
        title={at("copy", {}, "Copy")}
        onclick={() => copyWebhookUrl(webhook)}
      >
        {#if copiedWebhookKey === webhook.key}
          <Check size={13} />
          <span>{at("copied", {}, "Copied")}</span>
        {:else}
          <Copy size={13} />
          <span>{at("copy", {}, "Copy")}</span>
        {/if}
      </AdminButton>
    </div>
  </div>
{/snippet}

{#snippet renderGroupedFields(section, group)}
  {@const fieldGroups = semanticFieldGroups(section, group)}
  {#if fieldGroups.length === 1 && !fieldGroups[0].titleKey}
    {#each fieldGroups[0].fields as field}
      {@render renderField(field)}
    {/each}
  {:else}
    <div class="admin-settings-field-groups">
      {#each fieldGroups as fieldGroup}
        <section class="admin-settings-field-group">
          {#if fieldGroup.titleKey}
            <header class="admin-settings-field-group-head">
              <strong>{fieldGroupTitle(fieldGroup)}</strong>
              {#if fieldGroupDescription(fieldGroup)}
                <small>{fieldGroupDescription(fieldGroup)}</small>
              {/if}
            </header>
          {/if}
          <div class="admin-settings-field-group-body">
            {#each fieldGroup.fields as field}
              {@render renderField(field)}
            {/each}
          </div>
        </section>
      {/each}
    </div>
  {/if}
{/snippet}

{#snippet renderField(field)}
  {@const revealed = isSecretRevealed(field.key)}
  <div class="admin-setting" class:is-overridden={isOverridden(field)}>
    <div class="admin-setting-meta">
      <strong>
        {fieldLabelText(field)}
        {#if field.secret}
          <AdminBadge variant="warning">{at("settings_badge_secret", {}, "Secret")}</AdminBadge>
        {/if}
        {#if isOverridden(field)}
          <AdminBadge variant="success">{at("settings_badge_override", {}, "Override")}</AdminBadge>
        {/if}
      </strong>
      <code>{field.key}</code>
      {#if fieldDescriptionText(field)}
        <small>{fieldDescriptionText(field)}</small>
      {/if}
    </div>
    <div class="admin-setting-control">
      {#if field.type === "bool"}
        <div class="admin-setting-switch">
          <Switch.Root
            checked={Boolean(valueFor(field))}
            onCheckedChange={(checked) => setBoolField(field, checked)}
            class="admin-switch-root"
          >
            <Switch.Thumb class="admin-switch-thumb" />
          </Switch.Root>
          <span
            >{valueFor(field)
              ? at("enabled", {}, "Включено")
              : at("disabled", {}, "Выключено")}</span
          >
        </div>
      {:else if field.type === "color"}
        <ColorInput
          class="admin-color"
          value={valueFor(field) || "#00fe7a"}
          ariaLabel={fieldLabelText(field)}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
        <Input
          class="input"
          type="text"
          value={valueFor(field) || ""}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
      {:else if field.type === "icon"}
        {@const selectedIconName = iconValue(field)}
        {@const SelectedIcon = iconComponent(selectedIconName)}
        <AdminButton
          class="admin-icon-picker-trigger"
          variant="ghost"
          onclick={() => openIconPicker(field)}
        >
          {#if SelectedIcon}
            <svelte:component this={SelectedIcon} size={16} />
          {/if}
          <span>{iconLabel(field)}</span>
        </AdminButton>
        {#if !iconIsDefault(field)}
          <AdminButton
            size="sm"
            variant="ghost"
            onclick={() => settingsStore.markDirty(field.key, "")}
          >
            <X size={12} />
            {at("clear", {}, "Clear")}
          </AdminButton>
        {/if}
      {:else if field.choices && field.choices.length > 0}
        <AdminSelect
          class="admin-setting-select"
          value={valueFor(field) || ""}
          items={choiceItems(field)}
          ariaLabel={fieldLabelText(field)}
          placeholder={fieldPlaceholderText(field) || fieldLabelText(field)}
          onValueChange={(value) => settingsStore.markDirty(field.key, value)}
        />
      {:else if field.type === "int" || field.type === "float"}
        <Input
          class="input"
          type="number"
          step={field.type === "float" ? "0.1" : "1"}
          min={field.min ?? undefined}
          max={field.max ?? undefined}
          placeholder={fieldPlaceholderText(field)}
          value={valueFor(field) ?? ""}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
      {:else if field.type === "text"}
        <Textarea
          class="admin-setting-textarea"
          rows="4"
          placeholder={fieldPlaceholderText(field)}
          value={valueFor(field) ?? ""}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
      {:else if field.type === "json"}
        <div class="admin-json-toolbar">
          <FileInput
            id={"json-file-" + field.key}
            class="admin-json-file-input"
            accept="application/json,.json"
            onchange={(event) => handleJsonFile(field, event)}
          />
          <label
            class="admin-btn admin-btn-sm admin-btn-ghost admin-json-upload"
            for={"json-file-" + field.key}
          >
            <FileText size={13} />
            {at("settings_json_upload", {}, "Load .json")}
          </label>
          {#if valueFor(field)}
            <AdminButton
              size="sm"
              variant="ghost"
              onclick={() => settingsStore.markDirty(field.key, "")}
            >
              <X size={12} />
              {at("clear", {}, "Clear")}
            </AdminButton>
          {/if}
        </div>
        <Textarea
          class="admin-setting-textarea admin-setting-json-textarea"
          rows="10"
          spellcheck="false"
          placeholder={fieldPlaceholderText(field)}
          value={valueFor(field) ?? ""}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
      {:else if field.secret}
        <Input
          class="input"
          type={revealed ? "text" : "password"}
          placeholder={secretPlaceholder(field)}
          autocomplete="off"
          value={valueFor(field) ?? ""}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
        <AdminButton
          size="sm"
          variant="ghost"
          aria-label={revealed ? at("hide", {}, "Скрыть") : at("show", {}, "Показать")}
          onclick={() => toggleSecretReveal(field.key)}
        >
          {#if revealed}<EyeOff size={13} />{:else}<Eye size={13} />{/if}
        </AdminButton>
      {:else}
        <Input
          class="input"
          type="text"
          placeholder={fieldPlaceholderText(field)}
          value={valueFor(field) ?? ""}
          oninput={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
        />
      {/if}
      {#if isOverridden(field) || settingsDirty[field.key]}
        <AdminButton size="sm" variant="ghost" onclick={() => settingsStore.resetField(field)}>
          <X size={12} />
          {at("reset", {}, "Сбросить")}
        </AdminButton>
      {/if}
    </div>
  </div>
{/snippet}

{#if settingsLoading || !visibleSettingsSections.length}
  <AdminEmptyState
    >{settingsLoading
      ? at("loading", {}, "Загрузка…")
      : at("no_data", {}, "Нет данных")}</AdminEmptyState
  >
{:else}
  <div
    style="display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;"
  >
    <p class="admin-muted" style="margin:0;">
      {at(
        "settings_hint",
        {},
        "Изменения в админке имеют приоритет над .env. Кнопка «Сбросить» возвращает значение из переменных окружения."
      )}
    </p>
    <div style="display:flex; gap:8px;">
      <AdminButton size="sm" variant="ghost" onclick={toggleAllSections}>
        {settingsAllOpen
          ? at("collapse_all", {}, "Свернуть всё")
          : at("expand_all", {}, "Развернуть всё")}
      </AdminButton>
      {#if Object.keys(settingsDirty).length > 0}
        <AdminButton
          size="sm"
          variant="primary"
          onclick={() => settingsStore.saveSettings(onSettingsSaved)}
          disabled={settingsSaving}
        >
          {settingsSaving ? at("saving", {}, "Сохранение...") : at("save", {}, "Сохранить")}
        </AdminButton>
      {/if}
    </div>
  </div>
  <Accordion.Root type="multiple" bind:value={settingsOpenSections} class="admin-accordion">
    {#each visibleSettingsSections as section}
      {@const dirtyInSection = section.fields.filter((f) => Boolean(settingsDirty[f.key])).length}
      {@const overriddenInSection = section.fields.filter((f) => isOverridden(f)).length}
      <Accordion.Item value={section.id} class="admin-accordion-item admin-card">
        <Accordion.Header class="admin-accordion-header">
          <Accordion.Trigger class="admin-accordion-trigger">
            <span class="admin-accordion-title">{sectionTitle(section.id)}</span>
            <span class="admin-accordion-meta">
              {at(
                "settings_params_count",
                { count: section.fields.length },
                `${section.fields.length} параметров`
              )}{#if overriddenInSection}
                · {at(
                  "settings_overridden_count",
                  { count: overriddenInSection },
                  `${overriddenInSection} override`
                )}{/if}{#if dirtyInSection}
                · {at(
                  "settings_dirty_count",
                  { count: dirtyInSection },
                  `${dirtyInSection} изм.`
                )}{/if}
            </span>
            <ChevronRight size={16} class="admin-accordion-chev" />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class="admin-accordion-content">
          {@const groups = groupSectionFields(section)}
          {@const rootGroup = groups.find((g) => !g.label)}
          {@const labelGroups = groups.filter((g) => g.label)}
          <div class="admin-settings-fields">
            {#if rootGroup}
              {#if rootGroup.webhook}
                {@render renderWebhookHint(rootGroup.webhook)}
              {/if}
              {@render renderGroupedFields(section, rootGroup)}
            {/if}
            {#if labelGroups.length}
              <Accordion.Root
                type="multiple"
                value={settingsOpenSubsections[section.id] || []}
                onValueChange={(v) =>
                  (settingsOpenSubsections = { ...settingsOpenSubsections, [section.id]: v })}
                class="admin-subsection-accordion"
              >
                {#each labelGroups as group}
                  {@const subDirty = group.fields.filter((f) =>
                    Boolean(settingsDirty[f.key])
                  ).length}
                  {@const subOverridden = group.fields.filter((f) => isOverridden(f)).length}
                  <Accordion.Item value={group.id} class="admin-settings-subsection">
                    <Accordion.Header class="admin-accordion-header">
                      <Accordion.Trigger class="admin-settings-subsection-trigger">
                        <strong>{subsectionTitle(group)}</strong>
                        <span class="admin-settings-subsection-meta">
                          {at(
                            "settings_fields_count",
                            { count: group.fields.length },
                            `${group.fields.length} полей`
                          )}{#if subOverridden}
                            · {at(
                              "settings_overridden_count",
                              { count: subOverridden },
                              `${subOverridden} override`
                            )}{/if}{#if subDirty}
                            · {at(
                              "settings_dirty_count",
                              { count: subDirty },
                              `${subDirty} изм.`
                            )}{/if}
                        </span>
                        <ChevronRight size={14} class="admin-accordion-chev" />
                      </Accordion.Trigger>
                    </Accordion.Header>
                    <Accordion.Content class="admin-accordion-content">
                      <div class="admin-settings-subsection-body">
                        {#if group.webhook}
                          {@render renderWebhookHint(group.webhook)}
                        {/if}
                        {@render renderGroupedFields(section, group)}
                      </div>
                    </Accordion.Content>
                  </Accordion.Item>
                {/each}
              </Accordion.Root>
            {/if}
          </div>
        </Accordion.Content>
      </Accordion.Item>
    {/each}
  </Accordion.Root>
{/if}

<Dialog
  open={Boolean(iconPickerField)}
  title={at("settings_icon_picker_title", {}, "Choose icon")}
  description={iconPickerField ? fieldLabelText(iconPickerField) : ""}
  closeLabel={at("close", {}, "Close")}
  onclose={closeIconPicker}
  class="admin-icon-picker-dialog"
>
  <div class="admin-icon-picker-body">
    {#if iconPickerField}
      {@const currentIconName = iconValue(iconPickerField)}
      {@const CurrentIcon = iconComponent(currentIconName)}
      <div class="admin-icon-picker-current">
        <span class="admin-icon-picker-current-preview" aria-hidden="true">
          {#if CurrentIcon}
            <svelte:component this={CurrentIcon} size={24} />
          {/if}
        </span>
        <span class="admin-icon-picker-current-meta">
          <small>{at("settings_icon_current", {}, "Current icon")}</small>
          <strong>{iconLabel(iconPickerField)}</strong>
        </span>
        {#if !iconIsDefault(iconPickerField)}
          <AdminButton
            size="sm"
            variant="ghost"
            onclick={() => settingsStore.markDirty(iconPickerField.key, "")}
          >
            <X size={12} />
            {at("settings_icon_use_default", {}, "Use default")}
          </AdminButton>
        {/if}
      </div>
    {/if}
    <div class="admin-icon-picker-toolbar">
      <label class="admin-icon-picker-search">
        <Search size={15} />
        <Input
          bind:value={iconPickerSearch}
          class="input"
          type="text"
          placeholder={at("search", {}, "Search")}
        />
      </label>
    </div>
    <ScrollArea class="admin-icon-picker-scroll" maxHeight="min(52vh, 460px)">
      <div class="admin-icon-picker-grid">
        {#each filteredIconOptions as iconName}
          {@const Icon = iconComponent(iconName)}
          <button
            class:active={iconPickerField && iconValue(iconPickerField) === iconName}
            class="admin-icon-picker-option"
            type="button"
            onclick={() => selectIcon(iconName)}
          >
            {#if Icon}
              <svelte:component this={Icon} size={18} />
            {/if}
            <span>{iconName}</span>
          </button>
        {/each}
      </div>
    </ScrollArea>
  </div>
</Dialog>
