import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
import { open } from '@tauri-apps/plugin-dialog';
import { Cpu, FileCode2, FolderOpen, Gauge, Link2, ShieldAlert, Trash2, Wand2 } from 'lucide-react';
import {
  applyProfiles,
  fetchSingBoxSubscription,
  forgetSingBoxSubscription,
  generateSingBoxConfig,
  getComponents,
  getProxiFyreSetupStatus,
  getSavedState,
  getSingBoxSetupStatus,
  getSingBoxStatus,
  getStartupSnapshot,
  installProxiFyre,
  installSingBox,
  pingAllSingBoxServers,
  pingProxyTarget,
  pingSingBoxServer,
  restartAsAdmin,
  saveProfile,
  saveSingBoxSubscription,
  saveTarget,
  selectSingBoxServer,
  startProxiFyreService,
  startSingBoxService,
  stopProxiFyreService,
  stopSingBoxService,
  uninstallProxiFyre,
  uninstallSingBox,
  type AdminStatusResponse,
  type ApplyProfilesResponse,
  type LocalSingBoxStatusResponse,
  type PingServerResponse,
  type ProxyProbeResponse,
  type ProxyTargetCheckResponse,
  type ProxiFyreSetupStatus,
  type SingBoxSetupStatus,
} from '../api/tauriCommands';
import type { ComponentStatus, Profile, ProfileItemInput, ProfileItemType, SubscriptionServer, Target } from '../domain/types';
import { BusyRing, Button, DetailsPopover, IconButton, LogDock, ServiceControlRow, Tabs } from '../ui';
import { getApplyReadiness } from './readiness';
import { serviceControlState } from './viewModel';

type DraftItemType = Extract<ProfileItemType, 'process' | 'folder' | 'exe'>;
type ProxiFyreAction = 'start' | 'stop' | 'restart' | 'install' | 'uninstall';
type SingBoxAction = 'start' | 'stop' | 'install' | 'uninstall' | 'fetch' | 'forget' | 'generate' | 'ping';
type RouteMode = 'external' | 'local-singbox';
type ServiceVisualState = 'active' | 'settling' | null;
type PanelId = 'summary' | 'proxifyre' | 'proxy';
type StatusTone = 'ok' | 'warning' | 'error' | 'checking' | 'muted';
type TabTransitionDirection = 'left' | 'right';
type SummaryRouteFlow = 'proxy' | 'direct' | 'idle';

interface DraftItem {
  id: string;
  type: DraftItemType;
  value: string;
}

interface Notice {
  kind: 'success' | 'error' | 'info';
  title: string;
  text: string;
}

interface LogEntry extends Notice {
  id: string;
  at: number;
}

interface ConfigSnapshotItem {
  type: DraftItemType;
  value: string;
}

interface ConfigSnapshot {
  routeMode: RouteMode;
  proxy: string;
  selectedServerTag: string;
  items: ConfigSnapshotItem[];
}

interface ConnectionCheckView {
  tone: StatusTone;
  title: string;
  text: string;
  endpoint: string;
  details: string[];
  probes: ConnectionProbeView[];
  disabledReason?: string;
  loading: boolean;
}

interface ConnectionProbeView {
  id: string;
  label: string;
  value: string;
  tone: StatusTone;
}

interface RouteChainSegment {
  id: string;
  label: string;
  value: string;
  tone: StatusTone;
  details: string[];
}

interface PendingChangeRow {
  id: string;
  label: string;
  before?: string;
  after: string;
  tone?: 'added' | 'removed' | 'changed';
}

interface ConnectionCheckInput {
  routeMode: RouteMode;
  proxyInput: string;
  proxyCheck: ProxyTargetCheckResponse | null;
  singbox: ComponentStatus | undefined;
  singBoxStatus: LocalSingBoxStatusResponse | null;
  selectedServer: SubscriptionServer | null;
  isDetectingComponents: boolean;
  isProxyChecking: boolean;
}

interface RouteChainInput {
  routeMode: RouteMode;
  proxyInput: string;
  proxyfier: ComponentStatus | undefined;
  singbox: ComponentStatus | undefined;
  singBoxStatus: LocalSingBoxStatusResponse | null;
  selectedServer: SubscriptionServer | null;
  appCount: number;
  isDetectingComponents: boolean;
}

const MAIN_TARGET_ID = 'main-proxy';
const MAIN_PROFILE_ID = 'main-profile';
const LOCAL_SINGBOX_TARGET_ID = 'local-singbox';
const LOG_VISIBLE_MS = 6500;
const PANEL_ORDER: PanelId[] = ['proxifyre', 'summary', 'proxy'];
const SHOW_DEV_SUBSCRIPTION_IDENTITY = import.meta.env.DEV;
const proxyWardenToggleOnImage = new URL('../assets/proxywarden-toggle-on.png', import.meta.url).href;
const proxyWardenToggleOffImage = new URL('../assets/proxywarden-toggle-off.png', import.meta.url).href;

const fallbackComponents: ComponentStatus[] = [
  {
    id: 'proxyfier',
    name: 'ProxiFyre',
    state: 'missing',
    installed: false,
    running: false,
    problems: ['ProxiFyre не найден'],
    actions: [],
  },
  {
    id: 'singbox',
    name: 'Локальный sing-box',
    state: 'missing',
    installed: false,
    running: false,
    problems: [],
    actions: [],
  },
];

export function App() {
  const [activePanel, setActivePanel] = useState<PanelId>('summary');
  const [tabTransitionDirection, setTabTransitionDirection] = useState<TabTransitionDirection>('right');
  const [proxyInput, setProxyInput] = useState('');
  const [routeMode, setRouteMode] = useState<RouteMode>('external');
  const [profileId, setProfileId] = useState(MAIN_PROFILE_ID);
  const [targetId, setTargetId] = useState(MAIN_TARGET_ID);
  const [items, setItems] = useState<DraftItem[]>([]);
  const [appliedSnapshot, setAppliedSnapshot] = useState<ConfigSnapshot | null>(null);
  const [loadedProfiles, setLoadedProfiles] = useState<Profile[]>([]);
  const [isProcessInputOpen, setIsProcessInputOpen] = useState(false);
  const [processInput, setProcessInput] = useState('');
  const [pickerAction, setPickerAction] = useState<'exe' | 'folder' | null>(null);
  const [components, setComponents] = useState<ComponentStatus[]>(fallbackComponents);
  const [setupStatus, setSetupStatus] = useState<ProxiFyreSetupStatus | null>(null);
  const [singBoxStatus, setSingBoxStatus] = useState<LocalSingBoxStatusResponse | null>(null);
  const [singBoxSetupStatus, setSingBoxSetupStatus] = useState<SingBoxSetupStatus | null>(null);
  const [subscriptionInput, setSubscriptionInput] = useState('');
  const [serverPings, setServerPings] = useState<Record<string, PingServerResponse>>({});
  const [proxyCheck, setProxyCheck] = useState<ProxyTargetCheckResponse | null>(null);
  const [adminStatus, setAdminStatus] = useState<AdminStatusResponse | null>(null);
  const [generatedConfigPath, setGeneratedConfigPath] = useState('');
  const [logEntries, setLogEntries] = useState<LogEntry[]>([]);
  const [activeLogId, setActiveLogId] = useState<string | null>(null);
  const [isLogOpen, setIsLogOpen] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [isDetectingComponents, setIsDetectingComponents] = useState(true);
  const [isApplying, setIsApplying] = useState(false);
  const [isRestartingAsAdmin, setIsRestartingAsAdmin] = useState(false);
  const [isProxyChecking, setIsProxyChecking] = useState(false);
  const [serverPingTag, setServerPingTag] = useState<string | null>(null);
  const [serviceAction, setServiceAction] = useState<ProxiFyreAction | null>(null);
  const [singBoxAction, setSingBoxAction] = useState<SingBoxAction | null>(null);
  const [isServiceMenuOpen, setIsServiceMenuOpen] = useState(false);
  const [isSingBoxMenuOpen, setIsSingBoxMenuOpen] = useState(false);
  const [serviceVisualState, setServiceVisualState] = useState<ServiceVisualState>(null);
  const serviceVisualTimerRef = useRef<number | null>(null);

  const proxyfier = useMemo(
    () => components.find((component) => component.id === 'proxyfier'),
    [components],
  );
  const singbox = useMemo(
    () => singBoxStatus?.component ?? components.find((component) => component.id === 'singbox'),
    [components, singBoxStatus],
  );
  const activeLog = useMemo(
    () => logEntries.find((entry) => entry.id === activeLogId) ?? null,
    [activeLogId, logEntries],
  );
  const isSingBoxInstalled = Boolean(singbox?.installed);
  const selectedServerTag = singBoxStatus?.config.selectedServerTag;
  const selectedServer = useMemo(
    () => singBoxStatus?.cache?.servers.find((server) => server.tag === selectedServerTag) ?? null,
    [selectedServerTag, singBoxStatus],
  );
  const currentSnapshot = useMemo(
    () => configSnapshotFromUi(routeMode, proxyInput, items, selectedServerTag),
    [items, proxyInput, routeMode, selectedServerTag],
  );
  const pendingChanges = useMemo(
    () => appliedSnapshot ? configChangeRows(appliedSnapshot, currentSnapshot) : [],
    [appliedSnapshot, currentSnapshot],
  );
  const hasUnappliedChanges = pendingChanges.length > 0;
  const hasAdminPrompt = Boolean(adminStatus?.canRestartElevated);
  const shellStyle = hasUnappliedChanges
    ? ({ '--change-row-count': String(pendingChanges.length) } as CSSProperties)
    : undefined;
  const systemSummary = systemSummaryState({
    isLoading,
    isDetectingComponents,
    proxyfier,
    singbox,
    routeMode: appliedSnapshot?.routeMode ?? routeMode,
    singBoxStatus,
    proxyCheck,
  });

  useEffect(() => {
    void refresh();
  }, []);

  useEffect(() => {
    return () => {
      if (serviceVisualTimerRef.current !== null) {
        window.clearTimeout(serviceVisualTimerRef.current);
      }
    };
  }, []);

  useEffect(() => {
    if (!activeLogId) return undefined;

    const timer = window.setTimeout(() => {
      setActiveLogId((current) => (current === activeLogId ? null : current));
    }, LOG_VISIBLE_MS);

    return () => window.clearTimeout(timer);
  }, [activeLogId]);

  async function refresh() {
    setIsLoading(true);
    setIsDetectingComponents(true);
    try {
      const snapshot = await getStartupSnapshot();
      setAdminStatus(snapshot.adminStatus);
      setComponents(snapshot.components);
      setSetupStatus(snapshot.proxifyreSetupStatus);
      setSingBoxStatus(snapshot.singboxStatus);
      setSingBoxSetupStatus(snapshot.singboxSetupStatus);
      applySavedState(
        snapshot.savedState.profiles,
        snapshot.savedState.targets,
        snapshot.savedState.generatedConfigPath,
        snapshot.singboxStatus,
      );
    } catch {
      showNotice({
        kind: 'info',
        title: 'Режим предпросмотра',
        text: 'Запусти приложение через Tauri, чтобы увидеть найденный ProxiFyre и применить конфиг.',
      });
    } finally {
      setIsLoading(false);
      setIsDetectingComponents(false);
    }
  }

  async function restartApplicationAsAdmin() {
    setIsRestartingAsAdmin(true);
    try {
      await restartAsAdmin();
      setIsRestartingAsAdmin(false);
    } catch (error) {
      setIsRestartingAsAdmin(false);
      showNotice({
        kind: 'error',
        title: 'Перезапуск отменен',
        text: errorMessage(error),
      });
    }
  }

  function applySavedState(
    profiles: Profile[],
    targets: Target[],
    generatedPath: string,
    singBoxStatusForSnapshot = singBoxStatus,
  ) {
    const activeProfiles = profiles.filter((profile) => profile.enabled);
    const mainProfile = profiles.find((profile) => profile.id === MAIN_PROFILE_ID);
    const activeProfile = mainProfile ?? activeProfiles[0];
    const activeTarget = targetForUi(targets, activeProfile);
    const externalTarget = targetForExternalProxy(targets);
    const editableProfiles = mainProfile ? [mainProfile] : activeProfiles;
    const savedProxyInput = externalTarget ? formatProxy(externalTarget) : '';
    const savedItems = itemsForProfiles(editableProfiles);
    const savedRouteMode =
      activeTarget?.id === LOCAL_SINGBOX_TARGET_ID || activeProfile?.targetId === LOCAL_SINGBOX_TARGET_ID
        ? 'local-singbox'
        : 'external';

    setProxyInput(savedProxyInput);
    setProxyCheck(null);
    setItems(savedItems);
    setLoadedProfiles(profiles);
    setProfileId(mainProfile?.id ?? MAIN_PROFILE_ID);
    setTargetId(externalTarget?.id ?? MAIN_TARGET_ID);
    setRouteMode(savedRouteMode);
    setGeneratedConfigPath(generatedPath);
    setAppliedSnapshot(configSnapshotFromUi(
      savedRouteMode,
      savedProxyInput,
      savedItems,
      singBoxStatusForSnapshot?.config.selectedServerTag,
    ));
  }

  function addItem(type: DraftItemType, rawValue: string) {
    const value = normalizeItemValue(rawValue, type);
    if (!value) {
      showNotice({
        kind: 'error',
        title: 'Нечего добавить',
        text: emptyItemMessage(type),
      });
      return false;
    }

    if (items.some((item) => item.type === type && sameValue(item.value, value))) {
      showNotice({
        kind: 'info',
        title: 'Уже добавлено',
        text: value,
      });
      return false;
    }

    setItems((current) => [
      ...current,
      {
        id: `${type}-${Date.now()}`,
        type,
        value,
      },
    ]);
    return true;
  }

  function addProcess() {
    if (addItem('process', processInput)) {
      setProcessInput('');
      setIsProcessInputOpen(false);
    }
  }

  function removeItem(id: string) {
    setItems((current) => current.filter((item) => item.id !== id));
  }

  function changeRouteMode(nextMode: RouteMode) {
    setRouteMode(nextMode);
    setProxyCheck(null);
  }

  function changeProxyInput(nextValue: string) {
    setProxyInput(nextValue);
    setProxyCheck(null);
  }

  function switchPanel(nextPanel: PanelId) {
    if (activePanel === nextPanel) return;

    const currentIndex = PANEL_ORDER.indexOf(activePanel);
    const nextIndex = PANEL_ORDER.indexOf(nextPanel);
    setTabTransitionDirection(nextIndex > currentIndex ? 'right' : 'left');
    setActivePanel(nextPanel);
  }

  async function pickAndAddItem(type: Extract<DraftItemType, 'exe' | 'folder'>) {
    setPickerAction(type);
    try {
      const selectedPath = await pickPath(type);
      if (selectedPath) {
        addItem(type, selectedPath);
      }
    } catch (error) {
      showNotice({
        kind: 'error',
        title: type === 'exe' ? 'EXE не выбран' : 'Папка не выбрана',
        text: errorMessage(error),
      });
    } finally {
      setPickerAction(null);
    }
  }

  async function updateConfig() {
    let parsedProxy: ParsedProxy | null = null;
    try {
      if (!items.length) throw new Error('Добавь хотя бы один процесс, EXE-файл или папку.');
      if (routeMode === 'external') {
        parsedProxy = parseProxy(proxyInput);
      } else if (!isSingBoxInstalled) {
        throw new Error('Сначала установи Local sing-box.');
      } else if (!singBoxStatus?.config.selectedServerTag) {
        throw new Error('Выбери сервер Local sing-box.');
      }
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Проверь данные',
        text: errorMessage(error),
      });
      return;
    }

    setIsApplying(true);
    try {
      let singBoxGeneratedPath = '';
      if (routeMode === 'external') {
        if (!parsedProxy) throw new Error('Прокси не разобран.');
        await saveTarget({
          id: targetId,
          name: 'Основной прокси',
          kind: 'external',
          protocol: parsedProxy.protocol,
          host: parsedProxy.host,
          port: parsedProxy.port,
        });
      } else {
        const singBoxResult = await generateSingBoxConfig();
        singBoxGeneratedPath = singBoxResult.generatedConfigPath;
        await ensureSingBoxRunningForApply();
      }

      await saveProfile({
        id: profileId,
        name: 'Приложения через прокси',
        enabled: true,
        targetId: routeMode === 'local-singbox' ? LOCAL_SINGBOX_TARGET_ID : targetId,
        protocols: ['TCP', 'UDP'],
        items: items.map(profileItemInput),
      });
      await Promise.all(
        loadedProfiles
          .filter((profile) => profile.enabled && profile.id !== profileId)
          .map((profile) => saveProfile(profileInputFromProfile(profile, false))),
      );

      const result = await applyProfiles();
      await restartProxiFyreAfterApply();
      const [saved, detectedComponents, detectedSetupStatus, detectedSingBoxStatus, detectedSingBoxSetupStatus] = await Promise.all([
        getSavedState(),
        getComponents(),
        getProxiFyreSetupStatus(),
        getSingBoxStatus(),
        getSingBoxSetupStatus(),
      ]);

      applySavedState(saved.profiles, saved.targets, result.generatedConfigPath);
      setComponents(detectedComponents);
      setSetupStatus(detectedSetupStatus);
      setSingBoxStatus(detectedSingBoxStatus);
      setSingBoxSetupStatus(detectedSingBoxSetupStatus);
      showNotice(routeMode === 'local-singbox' ? noticeFromLocalApply(result, singBoxGeneratedPath) : noticeFromApply(result));
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Конфиг не обновлен',
        text: errorMessage(error),
      });
    } finally {
      setIsApplying(false);
    }
  }

  async function ensureSingBoxRunningForApply() {
    if (routeMode !== 'local-singbox' || !singbox?.installed) return;

    setIsSingBoxMenuOpen(false);
    try {
      await nextFrame();
      if (singbox.running) {
        setSingBoxAction('stop');
        const stopped = await stopSingBoxService();
        setComponents((current) => upsertComponent(current, stopped));
      }

      setSingBoxAction('start');
      const component = await startSingBoxService();
      setComponents((current) => upsertComponent(current, component));
      const status = await refreshSingBoxState();
      if (!status.component.running) {
        throw new Error('Local sing-box установлен, но служба не запустилась.');
      }
    } finally {
      setSingBoxAction(null);
    }
  }

  async function setProxiFyreServiceRunning(shouldRun: boolean) {
    const action = shouldRun ? 'start' : 'stop';
    setServiceAction(action);
    setIsServiceMenuOpen(false);
    startServiceVisual();
    try {
      await nextFrame();
      const component = shouldRun
        ? await startProxiFyreService()
        : await stopProxiFyreService();

      setComponents((current) => upsertComponent(current, component));
      showNotice({
        kind: 'success',
        title: shouldRun ? 'Служба запущена' : 'Служба остановлена',
        text: proxyfierDetails(component, false),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: shouldRun ? 'Служба не запущена' : 'Служба не остановлена',
        text: errorMessage(error),
      });
    } finally {
      setServiceAction(null);
      settleServiceVisual();
    }
  }

  async function installProxiFyrePackage() {
    setServiceAction('install');
    setIsServiceMenuOpen(false);
    startServiceVisual();
    try {
      await nextFrame();
      const component = await installProxiFyre();
      const detectedSetupStatus = await getProxiFyreSetupStatus();
      setComponents((current) => upsertComponent(current, component));
      setSetupStatus(detectedSetupStatus);
      showNotice({
        kind: 'success',
        title: 'ProxiFyre установлен',
        text: proxyfierDetails(component, false),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'ProxiFyre не установлен',
        text: errorMessage(error),
      });
    } finally {
      setServiceAction(null);
      settleServiceVisual();
    }
  }

  async function uninstallProxiFyrePackage() {
    const confirmed = window.confirm(
      'Удалить ProxiFyre с компьютера? Будет удалена служба и папка установки ProxiFyre.',
    );
    if (!confirmed) return;

    setServiceAction('uninstall');
    setIsServiceMenuOpen(false);
    startServiceVisual();
    try {
      await nextFrame();
      const component = await uninstallProxiFyre();
      const detectedSetupStatus = await getProxiFyreSetupStatus();
      setComponents((current) => upsertComponent(current, component));
      setSetupStatus(detectedSetupStatus);
      showNotice({
        kind: 'success',
        title: 'ProxiFyre удален',
        text: 'Служба и папка установки ProxiFyre удалены.',
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'ProxiFyre не удален',
        text: errorMessage(error),
      });
    } finally {
      setServiceAction(null);
      settleServiceVisual();
    }
  }

  async function refreshSingBoxState() {
    const [detectedSingBoxStatus, detectedSingBoxSetupStatus, detectedComponents] = await Promise.all([
      getSingBoxStatus(),
      getSingBoxSetupStatus(),
      getComponents(),
    ]);
    setSingBoxStatus(detectedSingBoxStatus);
    setSingBoxSetupStatus(detectedSingBoxSetupStatus);
    setComponents(detectedComponents);
    return detectedSingBoxStatus;
  }

  async function setSingBoxServiceRunning(shouldRun: boolean) {
    const action: SingBoxAction = shouldRun ? 'start' : 'stop';
    setSingBoxAction(action);
    setIsSingBoxMenuOpen(false);
    try {
      await nextFrame();
      const component = shouldRun ? await startSingBoxService() : await stopSingBoxService();
      setComponents((current) => upsertComponent(current, component));
      await refreshSingBoxState();
      setProxyCheck(null);
      showNotice({
        kind: 'success',
        title: shouldRun ? 'sing-box запущен' : 'sing-box остановлен',
        text: componentDetails(component, false),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: shouldRun ? 'sing-box не запущен' : 'sing-box не остановлен',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  async function installSingBoxPackage() {
    setSingBoxAction('install');
    setIsSingBoxMenuOpen(false);
    try {
      await nextFrame();
      const component = await installSingBox();
      setComponents((current) => upsertComponent(current, component));
      await refreshSingBoxState();
      setProxyCheck(null);
      showNotice({
        kind: 'success',
        title: 'Local sing-box установлен',
        text: componentDetails(component, false),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Local sing-box не установлен',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  async function uninstallSingBoxPackage() {
    const confirmed = window.confirm(
      'Удалить Local sing-box с компьютера? Будет удалена служба и папка установки sing-box.',
    );
    if (!confirmed) return;

    setSingBoxAction('uninstall');
    setIsSingBoxMenuOpen(false);
    try {
      await nextFrame();
      const component = await uninstallSingBox();
      setComponents((current) => upsertComponent(current, component));
      await refreshSingBoxState();
      setProxyCheck(null);
      showNotice({
        kind: 'success',
        title: 'Local sing-box удален',
        text: 'Служба и папка установки Local sing-box удалены.',
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Local sing-box не удален',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  async function syncSingBoxSubscription() {
    const subscriptionUrl = subscriptionInput.trim();
    if (!subscriptionUrl && !singBoxStatus?.config.hasSubscription) {
      showNotice({
        kind: 'error',
        title: 'Ссылка не указана',
        text: 'Вставь ссылку подписки Local sing-box.',
      });
      return;
    }

    setSingBoxAction('fetch');
    try {
      if (subscriptionUrl) {
        await saveSingBoxSubscription(subscriptionUrl);
      }
      const status = await fetchSingBoxSubscription();
      setSingBoxStatus(status);
      setComponents((current) => upsertComponent(current, status.component));
      setSubscriptionInput('');
      setServerPings({});
      setProxyCheck(null);
      showNotice({
        kind: 'success',
        title: 'Подписка обновлена',
        text: `Серверов: ${status.cache?.servers.length ?? 0}`,
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Подписка не обновлена',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  async function forgetSingBoxSubscriptionData() {
    setSingBoxAction('forget');
    setIsSingBoxMenuOpen(false);
    try {
      const status = await forgetSingBoxSubscription();
      setSingBoxStatus(status);
      setComponents((current) => upsertComponent(current, status.component));
      setServerPings({});
      setProxyCheck(null);
      showNotice({
        kind: 'info',
        title: 'Подписка очищена',
        text: 'Ссылка, cache и выбранный сервер Local sing-box удалены.',
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Подписка не очищена',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  async function chooseSingBoxServer(server: SubscriptionServer) {
    try {
      const status = await selectSingBoxServer(server);
      setSingBoxStatus(status);
      setComponents((current) => upsertComponent(current, status.component));
      setProxyCheck(null);
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Сервер не выбран',
        text: errorMessage(error),
      });
    }
  }

  async function pingSingBoxServers() {
    setSingBoxAction('ping');
    try {
      const results = await pingAllSingBoxServers();
      setServerPings(Object.fromEntries(results.map((result) => [result.tag, result])));
      showNotice({
        kind: 'info',
        title: 'Ping завершен',
        text: pingSummary(results),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Ping не выполнен',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  async function pingSingleSingBoxServer(server: SubscriptionServer) {
    setServerPingTag(server.tag);
    try {
      const result = await pingSingBoxServer(server.tag);
      setServerPings((current) => ({
        ...current,
        [result.tag]: result,
      }));
      showNotice({
        kind: result.ok ? 'success' : 'error',
        title: result.ok ? 'Сервер отвечает' : 'Сервер не ответил',
        text: serverTooltip(server, result),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Ping не выполнен',
        text: errorMessage(error),
      });
    } finally {
      setServerPingTag(null);
    }
  }

  async function checkRouteProxy() {
    let target: ParsedProxy;
    try {
      target = routeProxyCheckTarget(routeMode, proxyInput, singBoxStatus);
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Маршрут не проверен',
        text: errorMessage(error),
      });
      return;
    }

    setIsProxyChecking(true);
    try {
      const result = await pingProxyTarget(target.host, target.port);
      setProxyCheck(result);
      showNotice({
        kind: proxyCheckNoticeKind(result),
        title: proxyCheckNoticeTitle(result),
        text: proxyCheckText(result),
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Маршрут не проверен',
        text: errorMessage(error),
      });
    } finally {
      setIsProxyChecking(false);
    }
  }

  async function restartProxiFyreAfterApply() {
    if (!proxyfier?.installed || !proxyfier.running) return;

    setServiceAction('restart');
    setIsServiceMenuOpen(false);
    startServiceVisual();
    try {
      await nextFrame();
      const stopped = await stopProxiFyreService();
      setComponents((current) => upsertComponent(current, stopped));
      const started = await startProxiFyreService();
      setComponents((current) => upsertComponent(current, started));
    } finally {
      setServiceAction(null);
      settleServiceVisual();
    }
  }

  async function generateSingBoxNow() {
    setSingBoxAction('generate');
    try {
      const result = await generateSingBoxConfig();
      await refreshSingBoxState();
      showNotice({
        kind: 'success',
        title: 'Конфиг sing-box создан',
        text: result.generatedConfigPath,
      });
    } catch (error) {
      showNotice({
        kind: 'error',
        title: 'Конфиг sing-box не создан',
        text: errorMessage(error),
      });
    } finally {
      setSingBoxAction(null);
    }
  }

  function startServiceVisual() {
    if (serviceVisualTimerRef.current !== null) {
      window.clearTimeout(serviceVisualTimerRef.current);
      serviceVisualTimerRef.current = null;
    }

    setServiceVisualState('active');
  }

  function settleServiceVisual() {
    if (serviceVisualTimerRef.current !== null) {
      window.clearTimeout(serviceVisualTimerRef.current);
    }

    setServiceVisualState('settling');
    serviceVisualTimerRef.current = window.setTimeout(() => {
      setServiceVisualState(null);
      serviceVisualTimerRef.current = null;
    }, 700);
  }

  function showNotice(notice: Notice) {
    const entry: LogEntry = {
      ...notice,
      id: `log-${Date.now()}-${Math.random().toString(36).slice(2)}`,
      at: Date.now(),
    };

    setLogEntries((current) => [entry, ...current].slice(0, 40));
    setActiveLogId(entry.id);
  }

  function renderAdminPrompt() {
    if (!hasAdminPrompt) return null;

    return (
      <aside className="admin-prompt" aria-label="Права администратора">
        <span className="admin-prompt-icon" aria-hidden="true">
          <ShieldAlert size={15} strokeWidth={1.9} />
        </span>
        <div className="admin-prompt-copy">
          <strong>Права администратора</strong>
          <span>{adminStatus?.message}</span>
        </div>
        <Button
          type="button"
          variant="primary"
          size="sm"
          className="admin-prompt-action"
          onClick={() => void restartApplicationAsAdmin()}
          loading={isRestartingAsAdmin}
          loadingLabel="Открываю UAC"
        >
          Перезапустить с правами
        </Button>
      </aside>
    );
  }

  function renderTabs() {
    const tabs: Array<{ id: PanelId; label: string }> = [
      { id: 'proxifyre', label: 'ProxiFyre' },
      { id: 'summary', label: 'ProxyWarden' },
      { id: 'proxy', label: 'VPN / Прокси' },
    ];

    return <Tabs items={tabs} activeId={activePanel} onChange={switchPanel} ariaLabel="Разделы ProxyWarden" />;
  }

  function renderSummaryPanel() {
    return (
      <section
        className="tab-panel summary-panel"
        role="tabpanel"
        id="panel-summary"
        aria-labelledby="tab-summary"
      >
        <h1 className="summary-brand-title">ProxyWarden</h1>
        <div className="summary-main">
          {renderSummaryStatusControl()}
          {renderRouteChain('vertical')}
        </div>
      </section>
    );
  }

  function renderProxiFyreCard() {
    const state = serviceControlState(proxyfier, isDetectingComponents);
    const visualState = serviceVisualState === 'active' ? 'working' : serviceVisualState === 'settling' ? 'settling' : null;
    const primaryAction = proxyfier?.installed
      ? {
          label: proxyfier.running ? 'Остановить' : 'Запустить',
          onClick: () => void setProxiFyreServiceRunning(!proxyfier.running),
          variant: proxyfier.running ? 'danger' as const : 'neutral' as const,
          loading: serviceAction === 'start' || serviceAction === 'stop' || serviceAction === 'restart',
          loadingLabel: serviceAction === 'restart'
            ? 'Перезапускаю'
            : serviceAction === 'start' ? 'Запускаю' : 'Останавливаю',
          disabled: isDetectingComponents || Boolean(serviceAction),
        }
      : {
          label: 'Установить',
          onClick: () => void installProxiFyrePackage(),
          variant: 'primary' as const,
          loading: serviceAction === 'install',
          loadingLabel: 'Устанавливаю',
          disabled: isDetectingComponents || Boolean(serviceAction),
        };

    return (
      <ServiceControlRow
        state={state}
        visualState={visualState}
        className="proxifyre-card"
        title={proxyfierTitle(proxyfier, isDetectingComponents)}
        detail={proxyfierDetails(proxyfier, isDetectingComponents)}
        primaryAction={primaryAction}
        menu={proxyfier?.installed ? {
          label: 'Дополнительные действия ProxiFyre',
          open: isServiceMenuOpen,
          onOpenChange: setIsServiceMenuOpen,
          disabled: isDetectingComponents || Boolean(serviceAction),
          items: [{
            label: serviceAction === 'uninstall' ? 'Удаляю...' : 'Удалить ProxiFyre',
            danger: true,
            disabled: Boolean(serviceAction),
            onClick: () => void uninstallProxiFyrePackage(),
          }],
        } : undefined}
      />
    );
  }

  function renderProxiFyreSetupStrip() {
    const stripItems = setupStatus?.items ?? proxifyreSetupPlaceholders();

    return (
      <div className={`setup-strip ${setupStatus?.ready ? 'ready' : 'attention'}`} aria-label="Состав ProxiFyre">
        <span className="setup-strip-title">Состав</span>
        <div className="setup-strip-items">
          {stripItems.map((item) => (
            <div className={`setup-strip-item ${item.installed ? 'installed' : 'missing'}`} key={item.id}>
              <span className="setup-strip-dot" aria-hidden="true" />
              <strong>{setupItemUserName(item.id, item.name)}</strong>
              <span>{setupItemShortStatus(item)}</span>
            </div>
          ))}
        </div>
      </div>
    );
  }

  function renderAppsSection() {
    return (
      <section className="apps-section">
        <div className="apps-header">
          <div className="section-head apps-title">
            <h2>Приложения</h2>
            <span>{items.length}</span>
          </div>
        </div>

        {isProcessInputOpen ? (
          <div className="process-add-line">
            <input
              value={processInput}
              onChange={(event) => setProcessInput(event.target.value)}
              onKeyDown={(event) => {
                if (event.key === 'Enter') addProcess();
                if (event.key === 'Escape') {
                  setProcessInput('');
                  setIsProcessInputOpen(false);
                }
              }}
              placeholder="Discord"
              spellCheck={false}
              autoFocus
            />
            <Button type="button" variant="primary" onClick={addProcess}>
              OK
            </Button>
            <Button
              type="button"
              variant="neutral"
              onClick={() => {
                setProcessInput('');
                setIsProcessInputOpen(false);
              }}
            >
              Отмена
            </Button>
          </div>
        ) : (
          <div className="app-add-skeleton">
            <div className="app-add-main">
              <span className="item-icon app-add-icon" aria-hidden="true">
                <Cpu size={18} strokeWidth={1.9} />
              </span>
              <div>
                <strong>Новое приложение</strong>
                <span>Добавь процесс, EXE-файл или папку</span>
              </div>
            </div>
            <div className="add-toolbar" aria-label="Добавить приложение">
              <IconButton
                type="button"
                variant="add"
                onClick={() => setIsProcessInputOpen(true)}
                label="Добавить процесс"
                icon={<Cpu size={20} strokeWidth={1.8} />}
              />
              <IconButton
                type="button"
                variant="add"
                onClick={() => void pickAndAddItem('exe')}
                disabled={Boolean(pickerAction)}
                loading={pickerAction === 'exe'}
                label="Добавить EXE-файл"
                icon={<FileCode2 size={20} strokeWidth={1.8} />}
              />
              <IconButton
                type="button"
                variant="add"
                onClick={() => void pickAndAddItem('folder')}
                disabled={Boolean(pickerAction)}
                loading={pickerAction === 'folder'}
                label="Добавить папку"
                icon={<FolderOpen size={20} strokeWidth={1.8} />}
              />
            </div>
          </div>
        )}

        <div className="app-list">
          {isLoading ? (
            <div className="list-skeleton" aria-label="Загрузка приложений">
              <span />
              <span />
            </div>
          ) : items.length ? (
            items.map((item) => (
              <div className="app-row" key={item.id}>
                <div className="app-row-main">
                  <span className="item-icon" aria-hidden="true">
                    {itemIcon(item.type)}
                  </span>
                  <div>
                    <strong>{item.value}</strong>
                    <span>{itemTypeLabel(item.type)}</span>
                  </div>
                </div>
                <Button type="button" variant="danger" onClick={() => removeItem(item.id)} aria-label={`Удалить ${item.value}`}>
                  Удалить
                </Button>
              </div>
            ))
          ) : (
            <div className="empty-state">Список пуст. Добавь первое приложение сверху.</div>
          )}
        </div>
      </section>
    );
  }

  function renderSummaryStatusControl() {
    const installed = Boolean(proxyfier?.installed);
    const running = Boolean(proxyfier?.running);
    const working = serviceAction === 'start' || serviceAction === 'stop' || serviceAction === 'restart';
    const stateLabel = working || systemSummary.tone === 'checking'
      ? 'Проверяю'
      : systemSummary.tone === 'ok' ? 'Работает' : 'Не работает';
    const buttonAriaLabel = !installed
      ? 'ProxiFyre не установлен'
      : running ? 'Отключить ProxyWarden' : 'Включить ProxyWarden';
    const imageSrc = running ? proxyWardenToggleOnImage : proxyWardenToggleOffImage;

    return (
      <div className={`summary-status-control ${systemSummary.tone} ${running ? 'on' : 'off'}`}>
        <button
          type="button"
          className="summary-toggle-button"
          onClick={() => void setProxiFyreServiceRunning(!running)}
          disabled={!installed || isDetectingComponents || Boolean(serviceAction)}
          aria-label={buttonAriaLabel}
          aria-pressed={installed ? running : undefined}
        >
          {systemSummary.tone === 'checking' || working ? <BusyRing /> : null}
          <img src={imageSrc} alt="" draggable={false} />
        </button>
        <strong className={`summary-state-label ${systemSummary.tone}`}>{stateLabel}</strong>
      </div>
    );
  }

  function renderChangesDock() {
    if (!hasUnappliedChanges || !appliedSnapshot) return null;

    const externalProxyError = routeMode === 'external' ? safeProxyError(proxyInput) : null;
    const readiness = getApplyReadiness({
      routeMode,
      appCount: items.length,
      proxiFyreInstalled: Boolean(proxyfier?.installed),
      singBoxInstalled: isSingBoxInstalled,
      selectedServerTag: singBoxStatus?.config.selectedServerTag,
      externalProxyValue: proxyInput,
      externalProxyError,
      busy: isApplying || Boolean(serviceAction) || Boolean(singBoxAction),
    });
    const applyButtonText = isLoading || isDetectingComponents
      ? 'Проверяю готовность'
      : readiness.ready
        ? changesApplyButtonLabel(isApplying, singBoxAction, serviceAction)
        : readiness.title ?? 'Применение недоступно';

    return (
      <aside className="changes-dock" aria-label="Черновик конфигурации">
        <div className="changes-dock-main">
          <ul className="changes-list">
            {pendingChanges.map((change) => (
              <li className={`change-row ${change.tone ?? 'changed'}`} key={change.id}>
                <span className="change-label">{change.label}</span>
                <span className="change-values">
                  {change.before ? (
                    <>
                      <span>{change.before}</span>
                      <span className="change-arrow" aria-hidden="true">-&gt;</span>
                    </>
                  ) : null}
                  <strong>{change.after}</strong>
                </span>
              </li>
            ))}
          </ul>
        </div>
        <div className="changes-actions">
          <Button
            type="button"
            variant={readiness.ready ? 'primary' : 'neutral'}
            size="md"
            onClick={updateConfig}
            disabled={!readiness.ready}
            loading={isApplying}
            loadingLabel={changesApplyButtonLabel(true, singBoxAction, serviceAction)}
          >
            {applyButtonText}
          </Button>
        </div>
      </aside>
    );
  }

  function renderProxiFyrePanel() {
    return (
      <section
        className="tab-panel"
        role="tabpanel"
        id="panel-proxifyre"
        aria-labelledby="tab-proxifyre"
      >
        {renderProxiFyreCard()}
        {renderProxiFyreSetupStrip()}
        {renderAppsSection()}
      </section>
    );
  }

  function renderExternalProxyControls() {
    const proxyValidation = proxyInput.trim() ? safeProxyError(proxyInput) : null;

    return (
      <div className="external-proxy-card">
        <label className="simple-field route-proxy-field">
          <span>Внешний SOCKS5 прокси</span>
          <input
            value={proxyInput}
            onChange={(event) => changeProxyInput(event.target.value)}
            placeholder="socks5://127.0.0.1:1080"
            spellCheck={false}
          />
          {proxyValidation ? <span className="field-error">{proxyValidation}</span> : null}
        </label>
      </div>
    );
  }

  function renderConnectionCheck(check: ConnectionCheckView) {
    const buttonDisabled = Boolean(check.disabledReason);

    return (
      <div className="connection-check" aria-label="Проверка соединения">
        <div className="connection-check-status">
          <span>Проверка маршрута</span>
          <strong>{check.title}</strong>
          <p>{check.text}</p>
        </div>
        {check.probes.length ? (
          <div className="connection-probes" aria-label="Контрольные точки">
            {check.probes.map((probe) => (
              <span className={`connection-probe ${probe.tone}`} key={probe.id}>
                <strong>{probe.label}</strong>
                <span>{probe.value}</span>
              </span>
            ))}
          </div>
        ) : null}
        <DetailsPopover
          className="connection-endpoint"
          details={check.details}
          popoverLabel="Проверка маршрута"
          align="end"
          aria-label={`${check.endpoint}. ${check.details.join('. ')}`}
        >
          <strong>{check.endpoint}</strong>
        </DetailsPopover>
        <Button
          type="button"
          variant="neutral"
          size="md"
          onClick={() => void checkRouteProxy()}
          disabled={buttonDisabled}
          loading={check.loading}
          loadingLabel="Проверяю"
        >
          Проверить
        </Button>
      </div>
    );
  }

  function renderSingBoxCard() {
    const state = serviceControlState(singbox, isDetectingComponents);
    const setupSummary = singBoxSetupStatus
      ? singBoxSetupStatus.ready
        ? 'состав готов'
        : `не хватает: ${singBoxSetupStatus.missingCount}`
      : 'состав не проверен';
    const primaryAction = singbox?.installed
      ? {
          label: singbox.running ? 'Остановить' : 'Запустить',
          onClick: () => void setSingBoxServiceRunning(!singbox.running),
          variant: singbox.running ? 'danger' as const : 'neutral' as const,
          loading: singBoxAction === 'start' || singBoxAction === 'stop',
          loadingLabel: singBoxAction === 'start' ? 'Запускаю' : 'Останавливаю',
          disabled: isDetectingComponents || Boolean(singBoxAction),
        }
      : {
          label: 'Установить',
          onClick: () => void installSingBoxPackage(),
          variant: 'primary' as const,
          loading: singBoxAction === 'install',
          loadingLabel: 'Устанавливаю',
          disabled: isDetectingComponents || Boolean(singBoxAction),
        };

    return (
      <ServiceControlRow
        state={state}
        visualState={singBoxAction ? 'working' : null}
        className="singbox-card"
        title="Local sing-box"
        detail={singBoxDetails(singbox, singBoxStatus, isDetectingComponents)}
        primaryAction={primaryAction}
        menu={singbox?.installed ? {
          label: 'Дополнительные действия Local sing-box',
          open: isSingBoxMenuOpen,
          onOpenChange: setIsSingBoxMenuOpen,
          disabled: isDetectingComponents || Boolean(singBoxAction),
          items: [{
            label: singBoxAction === 'uninstall' ? 'Удаляю...' : 'Удалить Local sing-box',
            danger: true,
            disabled: Boolean(singBoxAction),
            onClick: () => void uninstallSingBoxPackage(),
          }],
        } : undefined}
        inlineActions={(
          <DetailsPopover
            className="setup-summary"
            details={singBoxDetailLines(
              singbox,
              singBoxStatus,
              singBoxSetupStatus,
              selectedServerTag,
              SHOW_DEV_SUBSCRIPTION_IDENTITY,
            )}
            popoverLabel="Состав Local sing-box"
            aria-label={`Подробности Local sing-box: ${setupSummary}`}
          >
            <span className="setup-summary-glyph" aria-hidden="true">i</span>
          </DetailsPopover>
        )}
      >
        {isSingBoxInstalled ? renderSingBoxWorkspace() : null}
      </ServiceControlRow>
    );
  }

  function renderSingBoxWorkspace() {
    return (
      <div className="singbox-workspace">
        <div className="subscription-line">
          <span className="subscription-icon" aria-hidden="true">
            <Link2 size={18} strokeWidth={1.9} />
          </span>
          <input
            value={subscriptionInput}
            onChange={(event) => setSubscriptionInput(event.target.value)}
            placeholder={singBoxStatus?.config.subscriptionDisplayUrl ?? 'https://example.com/sub'}
            spellCheck={false}
          />
          <Button
            type="button"
            onClick={() => void syncSingBoxSubscription()}
            loading={singBoxAction === 'fetch'}
            loadingLabel="Загружаю"
            variant="neutral"
            disabled={Boolean(singBoxAction)}
          >
            {subscriptionInput.trim() || !singBoxStatus?.config.hasSubscription ? 'Загрузить' : 'Обновить'}
          </Button>
          <IconButton
            type="button"
            variant="danger"
            onClick={() => void forgetSingBoxSubscriptionData()}
            disabled={Boolean(singBoxAction) || !singBoxStatus?.config.hasSubscription}
            label="Очистить подписку Local sing-box"
            tooltip="Очистить"
            icon={<Trash2 size={18} strokeWidth={1.9} />}
          />
        </div>

        <div className="singbox-workspace-head">
          <span>Серверы подписки</span>
          <div className="singbox-workspace-actions">
            <IconButton
              type="button"
              onClick={() => void pingSingBoxServers()}
              disabled={Boolean(singBoxAction) || !singBoxStatus?.cache?.servers.length}
              loading={singBoxAction === 'ping'}
              label="Проверить все серверы подписки"
              tooltip="Проверить все серверы"
              icon={<Gauge size={16} strokeWidth={1.9} />}
            />
            <IconButton
              type="button"
              onClick={() => void generateSingBoxNow()}
              disabled={Boolean(singBoxAction) || !selectedServerTag}
              loading={singBoxAction === 'generate'}
              label="Создать конфиг Local sing-box"
              tooltip="Создать конфиг"
              icon={<Wand2 size={16} strokeWidth={1.9} />}
            />
          </div>
        </div>

        {singBoxStatus?.cache?.servers.length ? (
          <div className="server-list">
            {singBoxStatus.cache.servers.map((server) => {
              const ping = serverPings[server.tag];
              const selected = server.tag === selectedServerTag;
              return (
                <div className={`server-row ${selected ? 'selected' : ''} ${ping ? pingTone(ping) : ''}`.trim()} key={server.tag}>
                  <button
                    type="button"
                    className="server-select-button"
                    onClick={() => void chooseSingBoxServer(server)}
                    data-tooltip={serverTooltip(server, ping)}
                    aria-label={`Выбрать ${displayServerTag(server.tag)}. ${serverTooltip(server, ping)}`}
                  >
                    <span className="server-select-dot" aria-hidden="true" />
                    <strong>{displayServerTag(server.tag)}</strong>
                    {ping ? <span className="server-ping-badge">{ping.ok ? `${ping.latency ?? 0} ms` : 'fail'}</span> : null}
                  </button>
                  <IconButton
                    type="button"
                    onClick={() => void pingSingleSingBoxServer(server)}
                    disabled={Boolean(serverPingTag)}
                    loading={serverPingTag === server.tag}
                    label={`Проверить ${displayServerTag(server.tag)}`}
                    tooltip="Ping"
                    icon={<Gauge size={14} strokeWidth={1.9} />}
                  />
                </div>
              );
            })}
          </div>
        ) : (
          <div className="empty-state">Подписка Local sing-box еще не загружена.</div>
        )}
      </div>
    );
  }

  function renderProxyOverview() {
    const check = connectionCheckView({
      routeMode,
      proxyInput,
      proxyCheck,
      singbox,
      singBoxStatus,
      selectedServer,
      isDetectingComponents,
      isProxyChecking,
    });

    return (
      <section className={`proxy-overview ${check.tone}`} aria-label="Состояние прокси">
        {check.tone === 'checking' ? <BusyRing /> : null}
        {renderConnectionCheck(check)}
      </section>
    );
  }

  function renderProxyPanel() {
    return (
      <section
        className="tab-panel"
        role="tabpanel"
        id="panel-proxy"
        aria-labelledby="tab-proxy"
      >
        {renderProxyOverview()}

        <section className="route-panel" aria-label="Маршрут приложений">
          <div className="route-switch">
            <Button
              type="button"
              variant={routeMode === 'external' ? 'primary' : 'neutral'}
              onClick={() => changeRouteMode('external')}
              aria-pressed={routeMode === 'external'}
            >
              Внешний прокси
            </Button>
            <Button
              type="button"
              variant={routeMode === 'local-singbox' ? 'primary' : 'neutral'}
              onClick={() => changeRouteMode('local-singbox')}
              aria-pressed={routeMode === 'local-singbox'}
            >
              Локальный прокси
            </Button>
          </div>

          {routeMode === 'external' ? renderExternalProxyControls() : renderSingBoxCard()}
        </section>

      </section>
    );
  }

  function renderRouteChain(variant: 'horizontal' | 'vertical' = 'horizontal') {
    const input: RouteChainInput = {
      routeMode,
      proxyInput,
      proxyfier,
      singbox,
      singBoxStatus,
      selectedServer,
      appCount: items.length,
      isDetectingComponents,
    };
    const flow = variant === 'vertical' ? summaryRouteFlow(input) : 'proxy';
    const segments = variant === 'vertical'
      ? summaryRouteChainSegments(input, flow)
      : routeChainSegments(input);
    const isVertical = variant === 'vertical';

    return (
      <section className={`route-chain route-chain--${variant} route-chain--${flow}`} aria-label="Текущий маршрут">
        {isVertical ? (
          <span className="route-chain-rail" aria-hidden="true">
            {flow === 'idle' ? null : (
              <>
                <span className="route-chain-packet route-chain-packet--one" />
                <span className="route-chain-packet route-chain-packet--two" />
                <span className="route-chain-packet route-chain-packet--three" />
              </>
            )}
          </span>
        ) : null}
        {segments.map((segment, index) => (
          <DetailsPopover
            className={`route-chain-segment ${segment.tone}`}
            details={segment.details}
            popoverLabel={segment.label}
            align={isVertical || index >= segments.length - 2 ? 'end' : 'start'}
            aria-label={`${segment.label}: ${segment.value}. ${segment.details.join('. ')}`}
            key={segment.id}
          >
            <span className="route-chain-dot" aria-hidden="true" />
            <span>{segment.label}</span>
            <strong>{segment.value}</strong>
            {index < segments.length - 1 ? (
              <span className="route-chain-arrow" aria-hidden="true">{isVertical ? '↓' : '->'}</span>
            ) : null}
          </DetailsPopover>
        ))}
      </section>
    );
  }

  function renderActivePanel() {
    if (activePanel === 'proxifyre') return renderProxiFyrePanel();
    if (activePanel === 'proxy') return renderProxyPanel();
    return renderSummaryPanel();
  }

  return (
    <main
      className={[
        'simple-shell',
        hasUnappliedChanges ? 'has-change-dock' : '',
        hasAdminPrompt ? 'has-admin-prompt' : '',
      ].filter(Boolean).join(' ')}
      style={shellStyle}
    >
      <section className="simple-panel">
        {renderTabs()}
        <div className={`tab-panel-frame swipe-${tabTransitionDirection}`} key={activePanel}>
          {renderActivePanel()}
        </div>
      </section>

      {renderChangesDock()}

      <LogDock
        entries={logEntries}
        activeEntry={activeLog}
        open={isLogOpen}
        onToggle={() => setIsLogOpen((current) => !current)}
        formatTime={formatLogTime}
      />
      {renderAdminPrompt()}
    </main>
  );
}

interface ParsedProxy {
  protocol: 'socks5';
  host: string;
  port: number;
}

interface SummaryStateInput {
  isLoading: boolean;
  isDetectingComponents: boolean;
  proxyfier: ComponentStatus | undefined;
  singbox: ComponentStatus | undefined;
  routeMode: RouteMode;
  singBoxStatus: LocalSingBoxStatusResponse | null;
  proxyCheck: ProxyTargetCheckResponse | null;
}

interface SummaryState {
  tone: StatusTone;
  title: string;
  text: string;
}

function configSnapshotFromUi(
  routeMode: RouteMode,
  proxyInput: string,
  items: Array<Pick<DraftItem, 'type' | 'value'>>,
  selectedServerTag?: string,
): ConfigSnapshot {
  return {
    routeMode,
    proxy: routeMode === 'external' ? normalizeProxySnapshot(proxyInput) : '',
    selectedServerTag: routeMode === 'local-singbox' ? normalizeServerTag(selectedServerTag) : '',
    items: normalizeSnapshotItems(items),
  };
}

function normalizeProxySnapshot(value: string) {
  try {
    const parsed = parseProxy(value);
    return `${parsed.protocol}://${parsed.host.trim().toLowerCase()}:${parsed.port}`;
  } catch {
    return value.trim().toLowerCase();
  }
}

function normalizeServerTag(tag: string | undefined) {
  return tag?.trim() ?? '';
}

function normalizeSnapshotItems(items: Array<Pick<DraftItem, 'type' | 'value'>>): ConfigSnapshotItem[] {
  return items
    .map((item) => ({
      type: item.type,
      value: normalizeItemValue(item.value, item.type).toLowerCase(),
    }))
    .filter((item) => item.value)
    .sort((left, right) => `${left.type}:${left.value}`.localeCompare(`${right.type}:${right.value}`));
}

function configChangeRows(applied: ConfigSnapshot, current: ConfigSnapshot): PendingChangeRow[] {
  if (sameConfigSnapshot(applied, current)) return [];

  const rows: PendingChangeRow[] = [];

  if (applied.routeMode !== current.routeMode) {
    rows.push({
      id: 'route-mode',
      label: 'Маршрут',
      before: routeModeLabel(applied.routeMode),
      after: routeModeLabel(current.routeMode),
    });
  }

  if (
    applied.proxy !== current.proxy
    && (applied.routeMode === 'external' || current.routeMode === 'external')
  ) {
    rows.push({
      id: 'external-proxy',
      label: 'SOCKS5',
      before: snapshotProxyChangeText(applied),
      after: snapshotProxyChangeText(current),
    });
  }

  if (
    applied.selectedServerTag !== current.selectedServerTag
    && (applied.routeMode === 'local-singbox' || current.routeMode === 'local-singbox')
  ) {
    rows.push({
      id: 'vpn-server',
      label: 'VPN сервер',
      before: snapshotServerChangeText(applied),
      after: snapshotServerChangeText(current),
    });
  }

  const itemRows = snapshotItemChangeRows(applied.items, current.items);
  if (itemRows.length) {
    rows.push(...itemRows);
  }

  return rows;
}

function sameConfigSnapshot(left: ConfigSnapshot, right: ConfigSnapshot) {
  if (left.routeMode !== right.routeMode) return false;
  if (left.proxy !== right.proxy) return false;
  if (left.selectedServerTag !== right.selectedServerTag) return false;
  if (left.items.length !== right.items.length) return false;

  return left.items.every((item, index) => {
    const other = right.items[index];
    return item.type === other.type && item.value === other.value;
  });
}

function routeModeLabel(routeMode: RouteMode) {
  return routeMode === 'local-singbox' ? 'Локальный прокси' : 'Внешний прокси';
}

function snapshotRouteLine(snapshot: ConfigSnapshot | null, status: LocalSingBoxStatusResponse | null) {
  if (!snapshot) return 'Сохраненное состояние еще не загружено.';

  if (snapshot.routeMode === 'local-singbox') {
    const server = snapshot.selectedServerTag ? displayServerTag(snapshot.selectedServerTag) : 'сервер не выбран';
    return `Выбранные приложения -> ProxiFyre -> Local sing-box ${localSingBoxAddress(status)} -> ${server}`;
  }

  return `Выбранные приложения -> ProxiFyre -> внешний прокси ${displaySnapshotProxy(snapshot.proxy)}`;
}

function snapshotServerText(snapshot: ConfigSnapshot | null) {
  if (!snapshot) return 'Сохраненное состояние еще не загружено.';
  if (snapshot.routeMode === 'external') return 'Не используется: применен внешний прокси.';
  return snapshot.selectedServerTag ? displayServerTag(snapshot.selectedServerTag) : 'Сервер не выбран.';
}

function snapshotItemsText(snapshot: ConfigSnapshot | null) {
  if (!snapshot) return 'Сохраненное состояние еще не загружено.';
  return snapshot.items.length ? appCountText(snapshot.items.length) : 'Приложения не настроены.';
}

function displaySnapshotProxy(proxy: string) {
  return proxy.replace(/^socks5:\/\//, '') || 'не указан';
}

function snapshotProxyChangeText(snapshot: ConfigSnapshot) {
  return snapshot.routeMode === 'external' ? displaySnapshotProxy(snapshot.proxy) : 'не используется';
}

function snapshotServerChangeText(snapshot: ConfigSnapshot) {
  if (snapshot.routeMode !== 'local-singbox') return 'не используется';
  return snapshot.selectedServerTag ? displayServerTag(snapshot.selectedServerTag) : 'сервер не выбран';
}

function snapshotItemChangeRows(
  appliedItems: ConfigSnapshotItem[],
  currentItems: ConfigSnapshotItem[],
): PendingChangeRow[] {
  const appliedKeys = new Set(appliedItems.map(snapshotItemKey));
  const currentKeys = new Set(currentItems.map(snapshotItemKey));
  const added = currentItems.filter((item) => !appliedKeys.has(snapshotItemKey(item)));
  const removed = appliedItems.filter((item) => !currentKeys.has(snapshotItemKey(item)));

  return [
    ...added.map((item) => ({
      id: `app-add-${snapshotItemKey(item)}`,
      label: 'Добавлено',
      after: `+ ${formatSnapshotItem(item)}`,
      tone: 'added' as const,
    })),
    ...removed.map((item) => ({
      id: `app-remove-${snapshotItemKey(item)}`,
      label: 'Удалено',
      after: `- ${formatSnapshotItem(item)}`,
      tone: 'removed' as const,
    })),
  ];
}

function snapshotItemKey(item: ConfigSnapshotItem) {
  return `${item.type}:${item.value}`;
}

function formatSnapshotItem(item: ConfigSnapshotItem) {
  return `${itemTypeLabel(item.type)} ${item.value}`;
}

function systemSummaryState(input: SummaryStateInput): SummaryState {
  if (input.isLoading || input.isDetectingComponents) {
    return {
      tone: 'checking',
      title: 'Проверяю',
      text: 'Обновляю сохраненный маршрут, компоненты и локальный proxy path.',
    };
  }

  if (!input.proxyfier?.installed) {
    return {
      tone: 'error',
      title: 'Не настроено',
      text: 'ProxiFyre не найден. Перейди в настройки ProxiFyre и установи компонент.',
    };
  }

  if (!input.proxyfier.running) {
    return {
      tone: 'warning',
      title: 'Требует внимания',
      text: 'ProxiFyre установлен, но служба сейчас остановлена.',
    };
  }

  if (input.routeMode === 'local-singbox') {
    if (!input.singbox?.installed) {
      return {
        tone: 'warning',
        title: 'Локальный прокси не готов',
        text: 'Для локального маршрута нужно установить Local sing-box.',
      };
    }
    if (!input.singbox.running) {
      return {
        tone: 'warning',
        title: 'Локальный прокси остановлен',
        text: 'Local sing-box установлен, но служба не запущена.',
      };
    }
    if (!input.singBoxStatus?.config.selectedServerTag) {
      return {
        tone: 'warning',
        title: 'Сервер не выбран',
        text: 'Выбери сервер Local sing-box на вкладке VPN / Прокси.',
      };
    }
  }

  if (input.proxyCheck && !input.proxyCheck.ok) {
    return {
      tone: 'warning',
      title: 'Маршрут не прошел проверку',
      text: proxyCheckText(input.proxyCheck),
    };
  }

  return {
    tone: 'ok',
    title: 'Работает',
    text: 'Сохраненная конфигурация выглядит готовой к маршрутизации выбранных приложений.',
  };
}

function summaryControlText(
  snapshot: ConfigSnapshot | null,
  proxyfier: ComponentStatus | undefined,
  checking: boolean,
) {
  if (checking) return 'Проверка компонентов.';
  if (!proxyfier?.installed) return 'ProxiFyre не найден.';
  if (!proxyfier.running) return 'Отключено: трафик идет напрямую.';
  if (!snapshot) return 'Маршрут загружается.';

  return `${routeModeLabel(snapshot.routeMode)} · ${appCountCompact(snapshot.items.length)}`;
}

function routeEndpointLabel(
  routeMode: RouteMode,
  proxyInput: string,
  status: LocalSingBoxStatusResponse | null,
) {
  if (routeMode === 'local-singbox') {
    const lan = lanSingBoxAddress(status);
    return lan ? `${localSingBoxAddress(status)} · LAN: ${lan}` : localSingBoxAddress(status);
  }

  try {
    const parsed = parseProxy(proxyInput);
    return formatHostPort(parsed.host, parsed.port);
  } catch {
    return proxyInput.trim() || 'не указан';
  }
}

function connectionCheckView(input: ConnectionCheckInput): ConnectionCheckView {
  if (input.routeMode === 'external') {
    const proxyValidation = input.proxyInput.trim() ? safeProxyError(input.proxyInput) : 'Введи SOCKS5 endpoint.';
    const endpoint = routeEndpointLabel('external', input.proxyInput, input.singBoxStatus);
    const details = [
      `Endpoint: ${endpoint}`,
      'Проверка: TCP доступность SOCKS5 и HTTP-запросы через него.',
      'Контрольные точки: Cloudflare Trace, Cloudflare Speed, ipify.',
      'Local sing-box для этого маршрута не требуется.',
    ];

    if (proxyValidation) {
      return {
        tone: 'warning',
        title: 'Нужен endpoint',
        text: proxyValidation,
        endpoint,
        details,
        probes: [],
        disabledReason: proxyValidation,
        loading: input.isProxyChecking,
      };
    }

    if (input.proxyCheck) {
      return {
        tone: proxyCheckTone(input.proxyCheck),
        title: proxyCheckTitle(input.proxyCheck),
        text: proxyCheckText(input.proxyCheck),
        endpoint,
        details: [...details, ...proxyCheckDetails(input.proxyCheck)],
        probes: proxyCheckProbes(input.proxyCheck),
        loading: input.isProxyChecking,
      };
    }

    return {
      tone: 'muted',
      title: 'Готово к проверке',
      text: 'Проверит TCP, внешний IP и задержку через тестовые endpoints.',
      endpoint,
      details,
      probes: [],
      loading: input.isProxyChecking,
    };
  }

  const localAddress = localSingBoxAddress(input.singBoxStatus);
  const selectedEndpoint = input.selectedServer
    ? `${displayServerTag(input.selectedServer.tag)} · ${formatHostPort(input.selectedServer.server, input.selectedServer.serverPort)}`
    : 'сервер не выбран';
  const details = [
    `Endpoint: ${localAddress}`,
    `LAN: ${lanSingBoxAddress(input.singBoxStatus) ?? 'недоступен'}`,
    `Сервер: ${selectedEndpoint}`,
    'Проверка: HTTP-запросы через local SOCKS listener.',
    'Контрольные точки: Cloudflare Trace, Cloudflare Speed, ipify.',
  ];

  if (input.isDetectingComponents) {
    return {
      tone: 'checking',
      title: 'Проверяю компоненты',
      text: 'Обновляю состояние.',
      endpoint: localAddress,
      details,
      probes: [],
      disabledReason: 'Дождись завершения проверки компонентов.',
      loading: input.isProxyChecking,
    };
  }

  if (!input.singbox?.installed) {
    return {
      tone: 'warning',
      title: 'sing-box не готов',
      text: 'Установи sing-box и выбери сервер.',
      endpoint: localAddress,
      details,
      probes: [],
      disabledReason: 'Local sing-box не установлен.',
      loading: false,
    };
  }

  if (!input.singbox.running) {
    return {
      tone: 'warning',
      title: 'Служба остановлена',
      text: 'Запусти sing-box.',
      endpoint: localAddress,
      details,
      probes: [],
      disabledReason: 'Local sing-box остановлен.',
      loading: false,
    };
  }

  if (!input.selectedServer) {
    return {
      tone: 'warning',
      title: 'Сервер не выбран',
      text: 'Выбери сервер подписки.',
      endpoint: selectedEndpoint,
      details,
      probes: [],
      disabledReason: 'Сервер Local sing-box не выбран.',
      loading: false,
    };
  }

  if (input.proxyCheck) {
    return {
      tone: proxyCheckTone(input.proxyCheck),
      title: proxyCheckTitle(input.proxyCheck),
      text: proxyCheckText(input.proxyCheck),
      endpoint: localAddress,
      details: [...details, ...proxyCheckDetails(input.proxyCheck)],
      probes: proxyCheckProbes(input.proxyCheck),
      loading: input.isProxyChecking,
    };
  }

  return {
    tone: 'muted',
    title: 'Готово к проверке',
    text: 'Проверит внешний IP и задержку через local SOCKS listener.',
    endpoint: localAddress,
    details,
    probes: [],
    loading: input.isProxyChecking,
  };
}

function summaryRouteFlow(input: RouteChainInput): SummaryRouteFlow {
  if (input.isDetectingComponents || input.appCount <= 0) return 'idle';
  if (!input.proxyfier?.running) return 'direct';
  return 'proxy';
}

function summaryRouteChainSegments(input: RouteChainInput, flow: SummaryRouteFlow): RouteChainSegment[] {
  if (flow === 'proxy') return routeChainSegments(input);

  const appSegment: RouteChainSegment = {
    id: 'apps',
    label: 'Приложения',
    value: appCountCompact(input.appCount),
    tone: input.appCount > 0 ? 'ok' : 'warning',
    details: [
      input.appCount > 0 ? appCountText(input.appCount) : 'Добавь хотя бы одно приложение на вкладке ProxiFyre.',
    ],
  };

  if (flow === 'idle') {
    return [
      appSegment,
      {
        id: 'idle',
        label: 'Трафик',
        value: input.isDetectingComponents ? 'проверяю' : 'ожидает приложения',
        tone: input.isDetectingComponents ? 'checking' : 'warning',
        details: input.isDetectingComponents
          ? ['Проверяю ProxiFyre и текущий маршрут.']
          : ['Пока нет выбранных приложений, ProxyWarden ничего не маршрутизирует.'],
      },
    ];
  }

  return [
    appSegment,
    {
      id: 'direct',
      label: 'Интернет',
      value: 'напрямую',
      tone: 'warning',
      details: [
        input.proxyfier?.installed
          ? 'ProxiFyre остановлен, поэтому выбранные приложения не перехватываются.'
          : 'ProxiFyre не найден, поэтому выбранные приложения не перехватываются.',
        'Пакеты идут обычным системным маршрутом без SOCKS5.',
      ],
    },
  ];
}

function routeChainSegments(input: RouteChainInput): RouteChainSegment[] {
  const proxyValidation = input.routeMode === 'external' && input.proxyInput.trim()
    ? safeProxyError(input.proxyInput)
    : null;
  const localServer = input.singBoxStatus?.config.selectedServerTag
    ? displayServerTag(input.singBoxStatus.config.selectedServerTag)
    : 'сервер не выбран';
  const endpoint = input.routeMode === 'local-singbox'
    ? localSingBoxAddress(input.singBoxStatus)
    : routeEndpointLabel('external', input.proxyInput, input.singBoxStatus);

  const endpointTone: StatusTone = input.routeMode === 'external'
    ? proxyValidation || !input.proxyInput.trim() ? 'warning' : 'ok'
    : input.singbox?.running ? 'ok' : input.singbox?.installed ? 'warning' : 'warning';
  const exitTone: StatusTone = input.routeMode === 'external'
    ? proxyValidation || !input.proxyInput.trim() ? 'warning' : 'ok'
    : input.singbox?.running && input.selectedServer ? 'ok' : 'warning';

  return [
    {
      id: 'apps',
      label: 'Приложения',
      value: appCountCompact(input.appCount),
      tone: input.appCount > 0 ? 'ok' : 'warning',
      details: [
        input.appCount > 0 ? appCountText(input.appCount) : 'Добавь хотя бы одно приложение на вкладке ProxiFyre.',
      ],
    },
    {
      id: 'proxifyre',
      label: 'ProxiFyre',
      value: input.isDetectingComponents ? 'проверяю' : input.proxyfier?.running ? 'запущен' : input.proxyfier?.installed ? 'остановлен' : 'не найден',
      tone: componentChainTone(input.proxyfier, input.isDetectingComponents),
      details: [
        proxyfierTitle(input.proxyfier, input.isDetectingComponents),
        proxyfierDetails(input.proxyfier, input.isDetectingComponents),
      ],
    },
    {
      id: 'endpoint',
      label: input.routeMode === 'local-singbox' ? 'Local endpoint' : 'SOCKS5 endpoint',
      value: endpoint,
      tone: endpointTone,
      details: input.routeMode === 'local-singbox'
        ? singBoxDetailLines(input.singbox, input.singBoxStatus, null, input.singBoxStatus?.config.selectedServerTag)
        : [
            `Endpoint: ${endpoint}`,
            proxyValidation ?? 'Формат внешнего SOCKS5 корректен.',
            'Local sing-box не участвует во внешнем маршруте.',
          ],
    },
    {
      id: 'exit',
      label: input.routeMode === 'local-singbox' ? 'VPN сервер' : 'Выход',
      value: input.routeMode === 'local-singbox' ? localServer : 'внешний SOCKS5',
      tone: exitTone,
      details: input.routeMode === 'local-singbox'
        ? [
            input.selectedServer ? serverLabel(input.selectedServer) : 'Сервер Local sing-box не выбран.',
            'Применение создаст sing-box config и обновит ProxiFyre.',
          ]
        : [
            'Выбранные приложения идут через внешний SOCKS5.',
            'Local sing-box не нужен для этого маршрута.',
          ],
    },
  ];
}

function componentChainTone(component: ComponentStatus | undefined, checking: boolean): StatusTone {
  if (checking) return 'checking';
  if (!component?.installed) return 'warning';
  if (!component.running) return 'warning';
  return 'ok';
}

function appCountCompact(count: number) {
  if (count === 1) return '1 приложение';
  if (count > 1 && count < 5) return `${count} приложения`;
  return `${count} приложений`;
}

function appCountText(count: number) {
  if (count === 1) return '1 приложение маршрутизируется через профиль.';
  if (count > 1 && count < 5) return `${count} приложения маршрутизируются через профиль.`;
  return `${count} приложений маршрутизируются через профиль.`;
}

function safeProxyError(value: string) {
  try {
    parseProxy(value);
    return null;
  } catch (error) {
    return errorMessage(error);
  }
}

function pingTone(ping: PingServerResponse): StatusTone {
  return ping.ok ? 'ok' : 'error';
}

function pingResultTitle(ping: PingServerResponse) {
  return ping.ok ? 'Доступен' : 'Недоступен';
}

function pingResultText(ping: PingServerResponse) {
  if (ping.ok) return `${ping.server}:${ping.serverPort} ответил за ${ping.latency ?? 0} ms.`;
  return `${ping.server}:${ping.serverPort}: ${ping.error ?? 'нет ответа'}`;
}

function proxyCheckTone(check: ProxyTargetCheckResponse): StatusTone {
  if (!check.ok) return 'error';
  if (check.probes.some((probe) => !probe.ok)) return 'warning';
  return 'ok';
}

function proxyCheckNoticeKind(check: ProxyTargetCheckResponse): Notice['kind'] {
  if (!check.ok) return 'error';
  return check.probes.some((probe) => !probe.ok) ? 'info' : 'success';
}

function proxyCheckTitle(check: ProxyTargetCheckResponse) {
  if (!check.ok) return 'Маршрут не прошел';
  const okCount = check.probes.filter((probe) => probe.ok).length;
  if (check.probes.length && okCount < check.probes.length) return 'Маршрут частично отвечает';
  return 'Маршрут отвечает';
}

function proxyCheckNoticeTitle(check: ProxyTargetCheckResponse) {
  if (!check.ok) return 'Проверка маршрута не прошла';
  const okCount = check.probes.filter((probe) => probe.ok).length;
  if (check.probes.length && okCount < check.probes.length) return 'Проверка частично прошла';
  return 'Проверка маршрута прошла';
}

function proxyCheckText(check: ProxyTargetCheckResponse) {
  const okProbes = check.probes.filter((probe) => probe.ok);
  if (!check.ok) return check.error ?? proxyProbeErrorText(check);
  if (!check.probes.length) return `${formatHostPort(check.server, check.serverPort)} доступен за ${check.latency ?? 0} ms.`;

  const fastest = okProbes.reduce<ProxyProbeResponse | null>((current, probe) => {
    if (!current) return probe;
    if ((probe.latency ?? Number.MAX_SAFE_INTEGER) < (current.latency ?? Number.MAX_SAFE_INTEGER)) return probe;
    return current;
  }, null);
  const ips = uniqueProbeIps(okProbes);
  const ipText = ips.length ? `IP ${ips.join(', ')}` : 'IP не распознан';
  const speedText = fastest ? `быстрее ${fastest.name}: ${fastest.latency ?? 0} ms` : `TCP ${check.latency ?? 0} ms`;

  return `Ответили ${okProbes.length}/${check.probes.length}; ${ipText}; ${speedText}.`;
}

function proxyProbeErrorText(check: ProxyTargetCheckResponse) {
  const failed = check.probes.find((probe) => !probe.ok);
  return failed?.error ?? `${formatHostPort(check.server, check.serverPort)}: нет ответа`;
}

function proxyCheckDetails(check: ProxyTargetCheckResponse) {
  const details = [
    `SOCKS5: ${formatHostPort(check.server, check.serverPort)}${check.latency != null ? ` · ${check.latency} ms` : ''}`,
  ];

  for (const probe of check.probes) {
    if (probe.ok) {
      const ip = probe.ip ? ` · IP ${probe.ip}` : '';
      const status = probe.status ? ` · HTTP ${probe.status}` : '';
      const latency = probe.latency != null ? ` · ${probe.latency} ms` : '';
      details.push(`${probe.name}:${ip}${status}${latency}`);
    } else {
      details.push(`${probe.name}: ${probe.error ?? 'нет ответа'}`);
    }
  }

  return details;
}

function proxyCheckProbes(check: ProxyTargetCheckResponse): ConnectionProbeView[] {
  return [
    {
      id: 'tcp',
      label: 'SOCKS',
      value: check.latency != null ? `${check.latency} ms` : check.ok ? 'ok' : 'fail',
      tone: check.error && !check.ok ? 'error' : 'ok',
    },
    ...check.probes.map((probe) => ({
      id: probe.id,
      label: shortProbeName(probe.name),
      value: probe.ok ? probeValue(probe) : 'fail',
      tone: probe.ok ? 'ok' as const : 'error' as const,
    })),
  ];
}

function probeValue(probe: ProxyProbeResponse) {
  const latency = probe.latency != null ? `${probe.latency} ms` : 'ok';
  return probe.ip ? `${compactIp(probe.ip)} · ${latency}` : latency;
}

function uniqueProbeIps(probes: ProxyProbeResponse[]) {
  return [...new Set(probes.map((probe) => probe.ip).filter((ip): ip is string => Boolean(ip)))];
}

function compactIp(ip: string) {
  return ip.length > 20 ? `${ip.slice(0, 10)}...${ip.slice(-6)}` : ip;
}

function shortProbeName(name: string) {
  return name
    .replace('Cloudflare ', 'CF ')
    .replace('Trace', 'trace')
    .replace('Speed', 'speed');
}

function changesApplyButtonLabel(
  isApplying: boolean,
  singBoxAction: SingBoxAction | null,
  serviceAction: ProxiFyreAction | null,
) {
  if (isApplying) {
    if (singBoxAction === 'start') return 'Запускаю sing-box...';
    if (singBoxAction === 'stop') return 'Перезапускаю sing-box...';
    if (serviceAction === 'restart') return 'Перезапускаю ProxiFyre...';
    return 'Применяю...';
  }
  return 'Применить изменения';
}

function parseProxy(rawValue: string): ParsedProxy {
  const value = rawValue.trim();
  if (!value) throw new Error('Введи адрес прокси.');

  const withProtocol = /^[a-z][a-z0-9+.-]*:\/\//i.test(value) ? value : `socks5://${value}`;
  let parsed: URL;
  try {
    parsed = new URL(withProtocol);
  } catch {
    throw new Error('Формат: socks5://host:port или host:port.');
  }

  const protocol = parsed.protocol.replace(':', '').toLowerCase();
  if (protocol !== 'socks5') {
    throw new Error('Сейчас поддерживается только SOCKS5.');
  }
  if (parsed.username || parsed.password) {
    throw new Error('Прокси с логином и паролем пока не поддерживаются.');
  }

  const host = parsed.hostname.replace(/^\[|\]$/g, '');
  const port = Number(parsed.port);
  if (!host || !Number.isInteger(port) || port < 1 || port > 65535) {
    throw new Error('Укажи хост и порт прокси.');
  }

  return { protocol: 'socks5', host, port };
}

function routeProxyCheckTarget(
  routeMode: RouteMode,
  proxyInput: string,
  status: LocalSingBoxStatusResponse | null,
): ParsedProxy {
  if (routeMode === 'external') return parseProxy(proxyInput);
  if (!status) throw new Error('Состояние Local sing-box не загружено.');

  return {
    protocol: 'socks5',
    host: localSingBoxProbeHost(status),
    port: status.config.listenPort,
  };
}

function targetForUi(targets: Target[], profile: Profile | undefined) {
  if (profile) return targets.find((target) => target.id === profile.targetId);
  return targets.find((target) => target.id === MAIN_TARGET_ID) ?? targets.find((target) => target.kind === 'external');
}

function targetForExternalProxy(targets: Target[]) {
  return targets.find((target) => target.id === MAIN_TARGET_ID)
    ?? targets.find((target) => target.kind === 'external' && target.id !== LOCAL_SINGBOX_TARGET_ID);
}

function itemsForProfiles(profiles: Profile[]): DraftItem[] {
  const seen = new Set<string>();
  const items: DraftItem[] = [];

  for (const profile of profiles) {
    for (const item of profile.items) {
      if (item.type !== 'process' && item.type !== 'folder' && item.type !== 'exe') continue;

      const key = `${item.type}:${item.value.trim().toLowerCase()}`;
      if (seen.has(key)) continue;
      seen.add(key);
      items.push({
        id: `${item.type}-${items.length}-${item.value}`,
        type: item.type,
        value: item.value,
      });
    }
  }

  return items;
}

function formatProxy(target: Target) {
  return target.protocol === 'socks5'
    ? `${target.host}:${target.port}`
    : `${target.protocol}://${target.host}:${target.port}`;
}

function normalizeItemValue(value: string, type: DraftItemType) {
  const clean = value.trim().replace(/^"|"$/g, '');
  if (!clean) return '';
  if (type === 'folder' || type === 'exe') return clean;

  return clean
    .split(/[\\/]/)
    .pop()
    ?.replace(/\.exe$/i, '')
    .trim() ?? '';
}

async function pickPath(type: Extract<DraftItemType, 'exe' | 'folder'>) {
  const selected = await open(
    type === 'folder'
      ? {
          title: 'Выбери папку',
          directory: true,
          multiple: false,
        }
      : {
          title: 'Выбери EXE-файл',
          directory: false,
          multiple: false,
          filters: [{ name: 'EXE-файлы', extensions: ['exe'] }],
        },
  );

  if (Array.isArray(selected)) return selected[0] ?? null;
  return selected;
}

function nextFrame() {
  return new Promise<void>((resolve) => {
    window.requestAnimationFrame(() => resolve());
  });
}

function profileItemInput(item: DraftItem): ProfileItemInput {
  return {
    type: item.type,
    value: item.value,
    recursive: item.type === 'folder',
  };
}

function emptyItemMessage(type: DraftItemType) {
  if (type === 'process') return 'Введи имя процесса.';
  if (type === 'folder') return 'Введи путь к папке.';
  return 'Введи путь к EXE-файлу.';
}

function itemTypeLabel(type: DraftItemType) {
  if (type === 'process') return 'процесс';
  if (type === 'folder') return 'папка';
  return 'EXE-файл';
}

function itemIcon(type: DraftItemType) {
  if (type === 'process') return <Cpu size={18} strokeWidth={1.9} />;
  if (type === 'folder') return <FolderOpen size={18} strokeWidth={1.9} />;
  return <FileCode2 size={18} strokeWidth={1.9} />;
}

function proxifyreSetupPlaceholders(): ProxiFyreSetupStatus['items'] {
  return [
    { id: 'vc-runtime', name: 'Среда запуска', installed: false, details: 'Проверяю' },
    { id: 'packet-filter', name: 'Сетевой драйвер', installed: false, details: 'Проверяю' },
    { id: 'proxifyre', name: 'Клиент ProxiFyre', installed: false, details: 'Проверяю' },
  ];
}

function setupItemUserName(id: string, fallbackName: string) {
  if (id === 'vc-runtime') return 'Среда запуска';
  if (id === 'packet-filter') return 'Сетевой драйвер';
  if (id === 'proxifyre') return 'Клиент ProxiFyre';
  return fallbackName;
}

function setupItemShortStatus(item: ProxiFyreSetupStatus['items'][number]) {
  if (item.details === 'Проверяю') return 'проверяю';
  if (!item.installed) return 'нужно установить';
  if (item.id === 'proxifyre') return item.version?.includes('не запущена') ? 'остановлен' : 'запущен';
  return 'готово';
}

function profileInputFromProfile(profile: Profile, enabled: boolean) {
  return {
    id: profile.id,
    name: profile.name,
    enabled,
    targetId: profile.targetId,
    protocols: profile.protocols,
    items: profile.items.map((item) => ({
      type: item.type,
      value: item.value,
      recursive: item.recursive,
    })),
  };
}

function proxyfierTitle(component: ComponentStatus | undefined, checking: boolean) {
  if (checking) return 'Проверяю ProxiFyre';
  if (!component) return 'ProxiFyre не проверен';
  if (component.running) return 'ProxiFyre найден и запущен';
  if (component.installed) return 'ProxiFyre найден';
  return 'ProxiFyre не найден';
}

function proxyfierDetails(component: ComponentStatus | undefined, checking: boolean) {
  if (checking) return 'Ищу установленный клиент и состояние службы.';
  if (!component) return 'Нажми «Обновить», чтобы проверить компьютер.';
  if (component.path) return component.path;
  return component.problems[0] ?? 'Путь установки не найден.';
}

function singBoxDetails(
  component: ComponentStatus | undefined,
  status: LocalSingBoxStatusResponse | null,
  checking: boolean,
) {
  if (checking) return 'Ищу sing-box, wrapper и службу.';
  if (component?.running && status) {
    const lanAddress = lanSingBoxAddress(status);
    return lanAddress
      ? `Доступен локально: ${localSingBoxAddress(status)} · LAN: ${lanAddress}`
      : `Доступен локально: ${localSingBoxAddress(status)}`;
  }
  if (component?.installed) return 'Служба остановлена. Запусти Local sing-box перед применением маршрута.';
  if (status?.config.hasSubscription) {
    return status.config.subscriptionDisplayUrl ?? 'Подписка сохранена.';
  }
  return component?.problems[0] ?? 'Установи компонент для подписки и локального маршрута.';
}

function singBoxDetailLines(
  component: ComponentStatus | undefined,
  status: LocalSingBoxStatusResponse | null,
  setupStatus: SingBoxSetupStatus | null,
  selectedServerTag: string | undefined,
  showDevSubscriptionIdentity = false,
) {
  const setupDetails = setupStatus
    ? setupStatus.items
        .map((item) => `${item.name}: ${setupItemShortStatus(item)}`)
        .join('; ')
    : 'состав не проверен';

  const details = [
    `Локально: ${localSingBoxAddress(status)}`,
    `LAN: ${lanSingBoxAddress(status) ?? 'недоступен'}`,
    `Сервер: ${selectedServerTag ? displayServerTag(selectedServerTag) : 'не выбран'}`,
    `Файл: ${component?.path ?? 'не найден'}`,
    `Конфиг: ${status?.generatedConfigPath ?? 'не создан'}`,
    `Подписка: ${status?.config.subscriptionDisplayUrl ?? (status?.config.hasSubscription ? 'сохранена' : 'не загружена')}`,
    `Состав: ${setupDetails}`,
  ];

  if (showDevSubscriptionIdentity) {
    details.push(...subscriptionIdentityDetailLines(status));
  }

  return details;
}

function subscriptionIdentityDetailLines(status: LocalSingBoxStatusResponse | null) {
  const headers = status?.subscriptionIdentity?.headers
    ?.filter((header) => header.name.trim().toLowerCase() !== 'x-hwid') ?? [];

  if (!headers.length) return ['Dev headers подписки: недоступны'];

  const details = [
    'Dev headers подписки (без HWID):',
    ...headers.map((header) => `${header.name}: ${header.value || 'пусто'}`),
  ];
  const sendsLegacyOsVersion = headers.some((header) => header.name.trim().toLowerCase() === 'x-ver-os');
  if (!sendsLegacyOsVersion) {
    details.push('X-Ver-OS: не отправится, версия ОС не определена');
  }

  return details;
}

function componentDetails(component: ComponentStatus | undefined, checking: boolean) {
  if (checking) return 'Проверяю состояние службы.';
  if (!component) return 'Компонент не проверен.';
  if (component.path) return component.path;
  return component.problems[0] ?? 'Путь установки не найден.';
}

function noticeFromApply(result: ApplyProfilesResponse): Notice {
  return {
    kind: result.success ? 'success' : 'error',
    title: result.success ? 'Конфиг обновлен' : 'Конфиг создан, но не применен',
    text: result.message,
  };
}

function noticeFromLocalApply(result: ApplyProfilesResponse, singBoxGeneratedPath: string): Notice {
  return {
    kind: result.success ? 'success' : 'error',
    title: result.success ? 'Local sing-box применен' : 'Конфиг создан, но не применен',
    text: singBoxGeneratedPath ? `${result.message} sing-box: ${singBoxGeneratedPath}` : result.message,
  };
}

function upsertComponent(components: ComponentStatus[], component: ComponentStatus) {
  const index = components.findIndex((current) => current.id === component.id);
  if (index === -1) return [...components, component];

  return [
    ...components.slice(0, index),
    component,
    ...components.slice(index + 1),
  ];
}

function sameValue(left: string, right: string) {
  return left.trim().toLowerCase() === right.trim().toLowerCase();
}

function formatLogTime(timestamp: number) {
  return new Date(timestamp).toLocaleTimeString('ru-RU', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
  });
}

function serverLabel(server: SubscriptionServer) {
  return `${server.type} · ${server.server}:${server.serverPort}`;
}

function serverTooltip(server: SubscriptionServer, ping: PingServerResponse | undefined) {
  const details = serverLabel(server);
  if (!ping) return details;
  return ping.ok ? `${details} · ping ${ping.latency ?? 0} ms` : `${details} · ping fail`;
}

function displayServerTag(tag: string) {
  const withoutFlags = tag
    .replace(/[\u{1f1e6}-\u{1f1ff}]/gu, '')
    .replace(/\s*->\s*/g, ' -> ')
    .replace(/\s*->\s*$/g, '')
    .replace(/^\s*->\s*/g, '')
    .replace(/\s{2,}/g, ' ')
    .trim();

  return withoutFlags || tag;
}

function pingSummary(results: PingServerResponse[]) {
  if (!results.length) return 'Серверов для проверки нет.';
  const ok = results.filter((result) => result.ok);
  if (!ok.length) return `Не ответил ни один сервер из ${results.length}.`;
  const best = ok.reduce((current, result) => {
    if ((result.latency ?? Number.MAX_SAFE_INTEGER) < (current.latency ?? Number.MAX_SAFE_INTEGER)) return result;
    return current;
  });
  return `Ответили ${ok.length}/${results.length}; быстрее ${best.tag}: ${best.latency ?? 0} ms.`;
}

function localSingBoxAddress(status: LocalSingBoxStatusResponse | null) {
  if (!status) return 'не загружен';
  return formatHostPort(localSingBoxProbeHost(status), status.config.listenPort);
}

function localSingBoxProbeHost(status: LocalSingBoxStatusResponse) {
  const host = status.config.listenHost.trim();
  return host === '0.0.0.0' || host === '::' || !host ? '127.0.0.1' : host;
}

function lanSingBoxAddress(status: LocalSingBoxStatusResponse | null) {
  if (!status) return null;
  const host = status.config.listenHost.trim().toLowerCase();
  if (host === '127.0.0.1' || host === 'localhost' || host === '::1') return null;
  const lanHost = host === '0.0.0.0' || host === '::' || !host
    ? status.lanListenHost
    : status.config.listenHost;
  if (!lanHost) return null;
  return formatHostPort(lanHost, status.config.listenPort);
}

function formatHostPort(host: string, port: number) {
  return host.includes(':') && !host.startsWith('[') ? `[${host}]:${port}` : `${host}:${port}`;
}

function errorMessage(error: unknown) {
  if (error instanceof Error) return error.message;
  if (typeof error === 'string') return error;
  if (error && typeof error === 'object' && 'message' in error) {
    return String((error as { message: unknown }).message);
  }
  return 'Неизвестная ошибка.';
}
