import React, { useDeferredValue, useMemo, useRef } from "react";
import { emptyTaskForm, normalizeIntervals, sanitizeTaskForm } from "./appDefaults.js";
import { formatAccountLabel, formatAccountStatus, formatTimestamp, formatCountdown } from "./utils/formatters.js";
import { copyToClipboard } from "./utils/clipboard.js";
import { explainInviteError, explainTdataError } from "./utils/errorHints.js";
import AppOverlays from "./components/AppOverlays.jsx";
import AppSidebar from "./components/AppSidebar.jsx";
import AppMain from "./components/AppMain.jsx";
import useTaskStatusView from "./hooks/useTaskStatusView.js";
import useNotifications from "./hooks/useNotifications.js";
import useLogsView from "./hooks/useLogsView.js";
import useAccountImport from "./hooks/useAccountImport.js";
import useTaskActions from "./hooks/useTaskActions.js";
import useAccessChecks from "./hooks/useAccessChecks.js";
import useAccountManagement from "./hooks/useAccountManagement.js";
import useTaskPresets from "./hooks/useTaskPresets.js";
import useTaskSelection from "./hooks/useTaskSelection.js";
import useInviteImport from "./hooks/useInviteImport.js";
import useSettingsActions from "./hooks/useSettingsActions.js";
import useTaskLoaders from "./hooks/useTaskLoaders.js";
import useTaskFormActions from "./hooks/useTaskFormActions.js";
import useAccountComputed from "./hooks/useAccountComputed.js";
import useAppOrchestration from "./hooks/useAppOrchestration.js";
import useTaskSelectors from "./hooks/useTaskSelectors.js";
import useCriticalEvents from "./hooks/useCriticalEvents.js";
import useUiComputed from "./hooks/useUiComputed.js";
import useMainUiProps from "./hooks/useMainUiProps.js";
import useAppTabGroups from "./hooks/useAppTabGroups.js";
import useAppLoaders from "./hooks/useAppLoaders.js";
import useAppState from "./hooks/useAppState.js";
import useAppTaskDerived from "./hooks/useAppTaskDerived.js";
import useOpenLogsTabListener from "./hooks/useOpenLogsTabListener.js";
import useAppOutsideClicks from "./hooks/useAppOutsideClicks.js";
import { APP_VERSION } from "./constants/consts.js";


export default function App() {
  const {
    settings,
    setSettings,
    accounts,
    setAccounts,
    accountStats,
    setAccountStats,
    accountAssignments,
    setAccountAssignments,
    globalStatus,
    setGlobalStatus,
    logs,
    setLogs,
    invites,
    setInvites,
    fallbackList,
    setFallbackList,
    confirmQueue,
    setConfirmQueue,
    tasks,
    setTasks,
    selectedTaskId,
    setSelectedTaskId,
    taskForm,
    setTaskForm,
    competitorText,
    setCompetitorText,
    selectedAccountIds,
    setSelectedAccountIds,
    taskAccountRoles,
    setTaskAccountRoles,
    activePreset,
    setActivePreset,
    presetSignatureRef,
    taskStatus,
    setTaskStatus,
    taskStatusMap,
    setTaskStatusMap,
    membershipStatus,
    setMembershipStatus,
    groupVisibility,
    setGroupVisibility,
    accessStatus,
    setAccessStatus,
    inviteAccessStatus,
    setInviteAccessStatus,
    inviteAccessCheckedAt,
    setInviteAccessCheckedAt,
    confirmAccessStatus,
    setConfirmAccessStatus,
    confirmAccessCheckedAt,
    setConfirmAccessCheckedAt,
    accountEvents,
    setAccountEvents,
    apiTraceLogs,
    setApiTraceLogs,
    taskAudit,
    setTaskAudit,
    testRun,
    setTestRun,
    queueItems,
    setQueueItems,
    queueStats,
    setQueueStats,
    fileImportForm,
    setFileImportForm,
    fileImportResult,
    setFileImportResult,
    taskActionLoading,
    setTaskActionLoading,
    taskNotice,
    setTaskNotice,
    autosaveNote,
    setAutosaveNote,
    settingsNotice,
    setSettingsNotice,
    tdataNotice,
    setTdataNotice,
    notificationsOpen,
    setNotificationsOpen,
    notificationsModalRef,
    importModalOpen,
    setImportModalOpen,
    nowExpanded,
    setNowExpanded,
    moreActionsOpen,
    setMoreActionsOpen,
    moreActionsRef,
    checklistOpen,
    setChecklistOpen,
    manualLoginOpen,
    setManualLoginOpen,
    taskSearch,
    setTaskSearch,
    taskFilter,
    setTaskFilter,
    infoOpen,
    setInfoOpen,
    infoTab,
    setInfoTab,
    activeTab,
    setActiveTab,
    logsTab,
    setLogsTab,
    taskSort,
    setTaskSort,
    expandedInviteId,
    setExpandedInviteId,
    liveConfirmOpen,
    setLiveConfirmOpen,
    liveConfirmContext,
    setLiveConfirmContext,
    now,
    setNow,
    isVisible,
    setIsVisible,
    bellRef,
    settingsAutosaveReady,
    taskAutosaveReady,
    taskAutosaveTimer,
    autosaveNoteTimer,
    tasksPollInFlight,
    accountsPollInFlight,
    logsPollInFlight,
    eventsPollInFlight
  } = useAppState();
  const liveConfirmResolver = useRef(null);
  const {
    competitorGroups,
    hasSelectedTask,
    selectedTask,
    selectedTaskName,
    canSaveTask
  } = useAppTaskDerived({
    tasks,
    selectedTaskId,
    taskForm,
    competitorText
  });
  const appendTestEvent = async (action, details) => {
    if (!window.api) return;
    try {
      await window.api.addAccountEvent({
        accountId: 0,
        action,
        details
      });
    } catch (error) {
      // ignore logging errors
    }
  };
  const runTest = async (mode) => {
    const startedAt = new Date().toISOString();
    setTestRun({
      status: "running",
      mode,
      steps: [],
      startedAt: formatTimestamp(startedAt),
      finishedAt: "",
      summary: ""
    });
    await appendTestEvent("test_run_start", `${selectedTaskName} · режим: ${mode}`);
    const steps = [];
    const pushStep = (title, status, details) => {
      const step = { title, status, details };
      steps.push(step);
      setTestRun((prev) => ({ ...prev, steps: [...steps] }));
      appendTestEvent("test_run_step", `${title} · ${status}${details ? ` · ${details}` : ""}`);
    };
    if (!hasSelectedTask) {
      pushStep("Выбрана задача", "error", "Задача не выбрана");
    } else {
      pushStep("Выбрана задача", "ok", selectedTaskName);
    }
    const missing = [];
    if (!taskForm.name.trim()) missing.push("название");
    if (!taskForm.ourGroup.trim()) missing.push("наша группа");
    if (competitorGroups.length === 0) missing.push("группы конкурентов");
    if (missing.length) {
      pushStep("Заполнены поля задачи", "error", `Не заполнено: ${missing.join(", ")}`);
    } else {
      pushStep("Заполнены поля задачи", "ok", "Минимально достаточно");
    }
    if (selectedAccountIds.length === 0) {
      pushStep("Аккаунты в задаче", "error", "Не выбраны аккаунты");
    } else {
      pushStep("Аккаунты в задаче", "ok", `Выбрано: ${selectedAccountIds.length}`);
    }
    const inviteCount = roleSummary.invite.length;
    const monitorCount = roleSummary.monitor.length;
    const confirmCount = roleSummary.confirm.length;
    const roleDetails = `мониторинг: ${monitorCount}, инвайт: ${inviteCount}, подтверждение: ${confirmCount}`;
    if (inviteCount === 0) {
      pushStep("Роли аккаунтов", "error", `Нет роли инвайта (${roleDetails})`);
    } else {
      pushStep("Роли аккаунтов", "ok", roleDetails);
    }
    const membershipIds = Object.keys(membershipStatus || {});
    const inOurGroup = membershipIds.filter((id) => membershipStatus[id]?.ourGroupMember).length;
    pushStep("Участие в нашей группе", inOurGroup > 0 ? "ok" : "warn", `В нашей группе: ${inOurGroup}`);
    if (!inviteAccessStatus || inviteAccessStatus.length === 0) {
      pushStep("Проверка прав инвайта", "warn", "Права не проверялись");
    } else {
      const okCount = inviteAccessStatus.filter((item) => item && item.ok).length;
      pushStep("Проверка прав инвайта", okCount > 0 ? "ok" : "warn", `OK: ${okCount} / ${inviteAccessStatus.length}`);
    }
    pushStep("Очередь", queueStats.total > 0 ? "ok" : "warn", `В очереди: ${queueStats.total}`);
    const minInterval = Number(taskForm.minIntervalMinutes || 0);
    const maxInterval = Number(taskForm.maxIntervalMinutes || 0);
    const intervalOk = minInterval > 0 && maxInterval > 0 && minInterval <= maxInterval;
    pushStep("Интервалы и лимиты", intervalOk ? "ok" : "warn", `Мин: ${minInterval || "—"} · Макс: ${maxInterval || "—"}`);
    if (taskForm.inviteViaAdmins) {
      pushStep("Инвайт через админов", taskForm.inviteAdminMasterId ? "ok" : "warn", taskForm.inviteAdminMasterId ? "Мастер-админ выбран" : "Не выбран мастер-админ");
    } else {
      pushStep("Инвайт через админов", "ok", "Отключено");
    }

    if (mode === "live") {
      pushStep("Live: проверка прав групп", "ok", "Запущена");
      await checkAccess();
      pushStep("Live: проверка прав инвайта", "ok", "Запущена");
      await checkInviteAccess();
      pushStep("Live: проверка видимости конкурентов", "ok", "Запущена");
      const visibilityResult = await window.api.groupVisibilityByTask(selectedTaskId);
      if (visibilityResult && visibilityResult.ok && Array.isArray(visibilityResult.result)) {
        const items = visibilityResult.result;
        const openCount = items.filter((item) => item && item.status === "open").length;
        const closedCount = items.filter((item) => item && item.status === "closed").length;
        const unknownCount = items.filter((item) => !item || !item.status || item.status === "unknown").length;
        pushStep("Live: видимость конкурентов", "ok", `Открытые: ${openCount} · Закрытые: ${closedCount} · Неизвестные: ${unknownCount}`);
        const closedLinks = items
          .filter((item) => item && item.status === "closed")
          .map((item) => item.link || item.source || "")
          .filter(Boolean);
        if (closedLinks.length) {
          const preview = closedLinks.slice(0, 5).join(", ");
          const suffix = closedLinks.length > 5 ? ` и ещё ${closedLinks.length - 5}` : "";
          pushStep("Live: закрытые конкуренты", "warn", `${preview}${suffix}`);
        }
      } else {
        pushStep("Live: видимость конкурентов", "warn", "Не удалось определить видимость");
      }
      pushStep("Live: обновление участия", "ok", "Запущено");
      await refreshMembership("test");
      if (queueStats.total > 0) {
        const confirmed = await new Promise((resolve) => {
          liveConfirmResolver.current = resolve;
          const item = (queueItems || [])[0] || null;
          setLiveConfirmContext(item ? {
            userId: item.user_id,
            username: item.username,
            sourceChat: item.source_chat
          } : null);
          setLiveConfirmOpen(true);
        });
        if (confirmed) {
          pushStep("Live: тестовый инвайт", "ok", "Запуск");
          const liveResult = await window.api.testInviteOnce({ taskId: selectedTaskId });
          if (liveResult && liveResult.ok) {
            const confirmLabel = liveResult.confirmed === true
              ? "подтверждено"
              : liveResult.confirmed === false
                ? `не подтверждено${liveResult.confirmError ? ` (${liveResult.confirmError})` : ""}`
                : "не проверено";
            pushStep("Live: результат инвайта", "ok", `Успешно · ${confirmLabel}`);
          } else {
            pushStep("Live: результат инвайта", "warn", liveResult && liveResult.error ? liveResult.error : "Ошибка инвайта");
          }
        } else {
          pushStep("Live: тестовый инвайт", "warn", "Отменено пользователем");
        }
      } else {
        pushStep("Live: тестовый инвайт", "warn", "Очередь пуста");
      }
    }

    const hasError = steps.some((step) => step.status === "error");
    const hasWarn = steps.some((step) => step.status === "warn");
    const finishedAt = new Date().toISOString();
    const status = hasError ? "error" : hasWarn ? "warn" : "ok";
    const summary = hasError ? "Есть ошибки" : hasWarn ? "Есть предупреждения" : "Всё готово";
    setTestRun((prev) => ({
      ...prev,
      status,
      finishedAt: formatTimestamp(finishedAt),
      summary
    }));
    await appendTestEvent("test_run_finish", `${status} · ${summary}`);
  };
  const {
    toasts,
    notifications,
    setNotifications,
    notificationFilter,
    setNotificationFilter,
    filteredNotifications,
    showNotification,
    dismissToast
  } = useNotifications();
  const {
    logSearch,
    setLogSearch,
    inviteSearch,
    setInviteSearch,
    fallbackSearch,
    setFallbackSearch,
    auditSearch,
    setAuditSearch,
    confirmSearch,
    setConfirmSearch,
    logPage,
    setLogPage,
    invitePage,
    setInvitePage,
    fallbackPage,
    setFallbackPage,
    auditPage,
    setAuditPage,
    confirmPage,
    setConfirmPage,
    queueSearch,
    setQueueSearch,
    queuePage,
    setQueuePage,
    inviteFilter,
    setInviteFilter,
    logPageCount,
    invitePageCount,
    fallbackPageCount,
    auditPageCount,
    confirmPageCount,
    queuePageCount,
    pagedLogs,
    pagedInvites,
    pagedFallback,
    pagedAudit,
    pagedConfirmQueue,
    pagedQueue,
    inviteStats,
    mutualContactDiagnostics
  } = useLogsView({
    logs,
    invites,
    fallbackList,
    taskAudit,
    confirmQueue,
    queueItems
  });
  const confirmStats = useMemo(() => {
    const stats = {
      total: confirmQueue.length,
      pending: 0,
      confirmed: 0,
      failed: 0
    };
    (confirmQueue || []).forEach((item) => {
      if (!item) return;
      if (item.status === "confirmed") stats.confirmed += 1;
      else if (item.status === "failed") stats.failed += 1;
      else stats.pending += 1;
    });
    return stats;
  }, [confirmQueue]);
  const { checkAccess, checkInviteAccess, checkConfirmAccess } = useAccessChecks({
    selectedTaskId,
    setAccessStatus,
    setInviteAccessStatus,
    setInviteAccessCheckedAt,
    setConfirmAccessStatus,
    setConfirmAccessCheckedAt,
    setTaskNotice,
    showNotification
  });
  const {
    loadTasks,
    loadAccountAssignments,
    loadTaskStatuses,
    loadBase
  } = useAppLoaders({
    selectedTaskId,
    setTasks,
    setSelectedTaskId,
    setAccountAssignments,
    setTaskStatusMap,
    setSettings,
    setAccounts,
    setAccountEvents,
    setAccountStats,
    setGlobalStatus
  });
  const deferredTaskSearch = useDeferredValue(taskSearch);
  const { onSettingsChange, saveSettings } = useSettingsActions({
    settings,
    setSettings,
    setSettingsNotice,
    showNotification
  });
  const { createTask, selectTask } = useTaskSelection({
    taskAutosaveReady,
    selectedTaskId,
    setSelectedTaskId,
    setTaskForm,
    setCompetitorText,
    setSelectedAccountIds,
    setTaskAccountRoles,
    setAccessStatus,
    setMembershipStatus
  });
  const { importInviteFile } = useInviteImport({
    fileImportForm,
    setFileImportForm,
    setFileImportResult,
    hasSelectedTask,
    selectedTaskId,
    showNotification,
    setInvites,
    setFallbackList,
    loadTaskStatuses
  });
  const { loadSelectedTask, refreshMembership } = useTaskLoaders({
    taskAutosaveReady,
    setTaskForm,
    setCompetitorText,
    setSelectedAccountIds,
    setTaskAccountRoles,
    setLogs,
    setInvites,
    setFallbackList,
    setConfirmQueue,
    setGroupVisibility,
    setTaskStatus,
    setMembershipStatus,
    showNotification,
    selectedTaskId
  });

  const {
    saveTask,
    deleteTask,
    startTask,
    stopTask,
    startAllTasks,
    stopAllTasks,
    parseHistory,
    checkAll,
    joinGroupsForTask,
    clearLogs,
    clearInvites,
    clearAccountEvents,
    exportLogs,
    exportTaskBundle,
    exportInvites,
    exportProblemInvites,
    exportFallback,
    updateFallbackStatus,
    clearFallback,
    clearConfirmQueue,
    clearQueue,
    clearAllTaskLogsAndQueue,
    clearDatabase,
    resetSessions
  } = useTaskActions({
    taskForm,
    setTaskForm,
    sanitizeTaskForm,
    taskAccountRoles,
    setTaskAccountRoles,
    selectedAccountIds,
    setSelectedAccountIds,
    accounts,
    selectedTaskId,
    selectedTaskName,
    competitorGroups,
    hasSelectedTask,
    setTaskNotice,
    showNotification,
    setAutosaveNote,
    autosaveNoteTimer,
    loadTasks,
    loadAccountAssignments,
    loadTaskStatuses,
    refreshMembership,
    checkInviteAccess,
    checkAccess,
    setLogs,
    setInvites,
    setTaskStatus,
    setTaskAudit,
    setSelectedTaskId,
    resetTaskForm: () => setTaskForm(emptyTaskForm),
    setCompetitorText,
    resetSelectedAccountIds: setSelectedAccountIds,
    resetTaskAccountRoles: setTaskAccountRoles,
    setFallbackList,
    setConfirmQueue,
    setAccountEvents,
    setTaskActionLoading,
    taskActionLoading,
    loadBase,
    createTask,
    setActiveTab,
    checkConfirmAccess
  });
  const {
    accountById,
    accountStatsMap,
    roleSummary,
    roleIntersectionCount,
    assignedAccountCount,
    assignedAccountMap,
    filterFreeAccounts,
    accountBuckets,
    perAccountInviteSum
  } = useAccountComputed({
    accounts,
    accountStats,
    taskAccountRoles,
    accountAssignments,
    selectedTaskId,
    tasks
  });

  const {
    persistAccountRoles,
    computeAdminConfirmConfigRisk,
    fixAdminConfirmConfigRisk,
    computeConfirmAccessRisk,
    fixConfirmAccessRisk,
    resetCooldown,
    deleteAccount,
    refreshIdentity,
    updateAccountRole,
    updateAccountInviteLimit,
    setInviteLimitForAllInviters,
    setAccountRolesAll,
    applyRolePreset,
    computeWatcherInviteRisk,
    fixWatcherInviteRisk,
    assignAccountsToTask,
    moveAccountToTask,
    removeAccountFromTask
  } = useAccountManagement({
    selectedTaskId,
    taskAccountRoles,
    setTaskAccountRoles,
    setTaskForm,
    selectedAccountIds,
    setSelectedAccountIds,
    accounts,
    accountBuckets,
    taskForm,
    hasSelectedTask,
    loadAccountAssignments,
    showNotification,
    setTaskNotice,
    setAccounts,
    membershipStatus,
    refreshMembership,
    confirmAccessStatus
  });
  const { applyTaskPreset } = useTaskPresets({
    hasSelectedTask,
    accounts,
    selectedAccountIds,
    taskForm,
    setTaskForm,
    setTaskAccountRoles,
    setSelectedAccountIds,
    persistAccountRoles,
    showNotification,
    setTaskNotice,
    setActivePreset,
    setActiveTab,
    selectedTaskId,
    presetSignatureRef
  });

  const {
    loginForm,
    setLoginForm,
    tdataForm,
    setTdataForm,
    loginStatus,
    tdataResult,
    tdataLoading,
    startLogin,
    completeLogin,
    importTdata
  } = useAccountImport({
    selectedTaskId,
    hasSelectedTask,
    assignAccountsToTask,
    setAccounts,
    showNotification,
    explainTdataError,
    setTdataNotice
  });

  

  const { taskSummary, filteredTasks } = useTaskSelectors({
    tasks,
    taskStatusMap,
    deferredTaskSearch,
    taskFilter,
    taskSort
  });

  useAppOutsideClicks({
    notificationsOpen,
    notificationsModalRef,
    bellRef,
    setNotificationsOpen,
    moreActionsOpen,
    moreActionsRef,
    setMoreActionsOpen
  });



  const formatCountdownWithNowLocal = (value) => formatCountdown(value, now);
  const { criticalErrorAccounts } = useCriticalEvents({
    accountEvents,
    accounts
  });
  const {
    monitorLabels,
    inviteLabels,
    nowLine,
    primaryIssue,
    openFixTab,
    checklistItems,
    checklistStats,
    inviteAccessChecked,
    inviteAccessOk,
    inviteAccessWarn,
    lastEvents
  } = useTaskStatusView({
    taskStatus,
    taskAccountRoles,
    accountById,
    formatAccountLabel,
    setActiveTab,
    checkInviteAccess,
    checkConfirmAccess,
    parseHistory,
    refreshMembership,
    assignedAccountCount,
    roleSummary,
    accountEvents,
    formatCountdownWithNow: formatCountdownWithNowLocal,
    inviteAccessStatus,
    confirmAccessStatus,
    membershipStatus,
    selectedTask,
    computeWatcherInviteRisk,
    fixWatcherInviteRisk
  });

  const {
    pauseReason,
    hasPerAccountInviteLimits,
    formatCountdownWithNow
  } = useUiComputed({
    taskStatus,
    assignedAccountCount,
    roleSummary,
    inviteAccessWarn,
    taskAccountRoles,
    taskStatusMap,
    tasks,
    formatCountdown,
    now
  });
  const refreshQueue = React.useCallback(async () => {
    if (!window.api || selectedTaskId == null) return;
    try {
      const queueData = await window.api.listQueue({ limit: 200, taskId: selectedTaskId });
      if (queueData && Array.isArray(queueData.items)) setQueueItems(queueData.items);
      if (queueData && queueData.stats) setQueueStats(queueData.stats);
    } catch (_error) {
      // noop: queue refresh errors are non-blocking for UI actions
    }
  }, [selectedTaskId, setQueueItems, setQueueStats]);
  useAppOrchestration({
    activeTab,
    selectedTaskId,
    isVisible,
    setIsVisible,
    setNow,
    tasksPollInFlight,
    accountsPollInFlight,
    logsPollInFlight,
    eventsPollInFlight,
    setTasks,
    loadTaskStatuses,
    setTaskStatus,
    setAccounts,
    setAccountAssignments,
    setAccountStats,
    setGlobalStatus,
    setLogs,
    setInvites,
    setFallbackList,
    setConfirmQueue,
    setTaskAudit,
    setAccountEvents,
    setApiTraceLogs,
    setQueueItems,
    setQueueStats,
    loadBase,
    loadSelectedTask,
    setAccessStatus,
    setInviteAccessStatus,
    setMembershipStatus,
    setTaskNotice,
    setActivePreset,
    checkAccess,
    checkInviteAccess,
    activePreset,
    taskForm,
    taskAccountRoles,
    presetSignatureRef,
    taskStatus,
    setTaskStatusMap,
    checklistStats,
    setChecklistOpen,
    hasSelectedTask,
    roleIntersectionCount,
    roleSummary,
    setTaskForm,
    sanitizeTaskForm,
    taskNotice,
    settingsNotice,
    tdataNotice,
    setSettingsNotice,
    setTdataNotice,
    showNotification,
    settings,
    settingsAutosaveReady,
    taskAutosaveReady,
    taskAutosaveTimer,
    competitorText,
    selectedAccountIds,
    canSaveTask,
    saveTask,
    setSettings
  });

  const clearApiTrace = async () => {
    if (!window.api) return;
    try {
      await window.api.clearApiTrace(selectedTaskId || 0);
      setApiTraceLogs(await window.api.listApiTrace({ limit: 300, taskId: selectedTaskId || 0 }));
      showNotification("API трассировка очищена.", "success");
    } catch (error) {
      showNotification(error.message || String(error), "error");
    }
  };

  const toggleApiTrace = async () => {
    if (!window.api) return;
    try {
      const next = !Boolean(settings && settings.apiTraceEnabled);
      const updated = await window.api.saveSettings({ ...settings, apiTraceEnabled: next });
      setSettings(updated);
      showNotification(`Трассировка API ${next ? "включена" : "выключена"}.`, next ? "success" : "info");
    } catch (error) {
      showNotification(error.message || String(error), "error");
    }
  };

  const exportApiTraceJson = async () => {
    if (!window.api) return;
    try {
      const result = await window.api.exportApiTraceJson(selectedTaskId || 0);
      if (result && result.ok) {
        showNotification(`API трассировка выгружена в JSON: ${result.filePath}`, "success");
      }
    } catch (error) {
      showNotification(error.message || String(error), "error");
    }
  };

  const exportApiTraceCsv = async () => {
    if (!window.api) return;
    try {
      const result = await window.api.exportApiTraceCsv(selectedTaskId || 0);
      if (result && result.ok) {
        showNotification(`API трассировка выгружена в CSV: ${result.filePath}`, "success");
      }
    } catch (error) {
      showNotification(error.message || String(error), "error");
    }
  };

  const { applyRoleMode } = useTaskFormActions({
    taskForm,
    setTaskForm
  });
  const { quickActions, nowStatus, checklist, tabs } = useMainUiProps({
    selectedTaskName,
    autosaveNote,
    taskStatus,
    hasSelectedTask,
    canSaveTask,
    taskActionLoading,
    saveTask,
    parseHistory,
    joinGroupsForTask,
    checkAll,
    startTask,
    stopTask,
    moreActionsOpen,
    setMoreActionsOpen,
    moreActionsRef,
    clearQueue,
    clearAllTaskLogsAndQueue,
    startAllTasks,
    stopAllTasks,
    clearDatabase,
    resetSessions,
    pauseReason,
    setActiveTab,
    tasksLength: tasks.length,
    runTestSafe: () => runTest("safe"),
    exportTaskBundle,
    refreshMembership,
    refreshIdentity,
    apiTraceEnabled: Boolean(settings && settings.apiTraceEnabled),
    toggleApiTrace,
    setInfoOpen,
    setInfoTab,
    nowLine,
    nowExpanded,
    setNowExpanded,
    primaryIssue,
    openFixTab,
    monitorLabels,
    inviteLabels,
    roleSummary,
    groupVisibility,
    lastEvents,
    formatTimestamp,
    checklistOpen,
    setChecklistOpen,
    checklistStats,
    checklistItems,
    activeTab,
    logsTab,
    setLogsTab,
    confirmStats
  });
  const { taskSettings, accountsTab, logsTab: logsTabGroup, queueTab: queueTabGroup, apiTraceTab, eventsTab, settingsTab } = useAppTabGroups({
    selectedTaskId,
    refreshQueue,
    selectedTaskName,
    taskForm,
    setTaskForm,
    activePreset,
    setActivePreset,
    applyTaskPreset,
    formatAccountLabel,
    accountById,
    competitorText,
    setCompetitorText,
    applyRoleMode,
    normalizeIntervals,
    taskStatus,
    perAccountInviteSum,
    hasSelectedTask,
    inviteAccessStatus,
    inviteAccessCheckedAt,
    confirmAccessStatus,
    confirmAccessCheckedAt,
    formatTimestamp,
    checkInviteAccess,
    checkConfirmAccess,
    accounts,
    showNotification,
    copyToClipboard,
    sanitizeTaskForm,
    hasPerAccountInviteLimits,
    fileImportResult,
    importInviteFile,
    fileImportForm,
    setFileImportForm,
    criticalErrorAccounts,
    accountStatsMap,
    settings,
    membershipStatus,
    assignedAccountMap,
    accountBuckets,
    filterFreeAccounts,
    selectedAccountIds,
    taskAccountRoles,
    inviteAdminMasterId: taskForm.inviteAdminMasterId,
    refreshMembership,
    refreshIdentity,
    formatAccountStatus,
    resetCooldown,
    deleteAccount,
    updateAccountRole,
    updateAccountInviteLimit,
    setInviteLimitForAllInviters,
    setAccountRolesAll,
    applyRolePreset,
    computeWatcherInviteRisk,
    fixWatcherInviteRisk,
    removeAccountFromTask,
    moveAccountToTask,
    logsTab,
    setLogsTab,
    exportLogs,
    clearLogs,
    exportInvites,
    exportProblemInvites,
    exportFallback,
    updateFallbackStatus,
    clearFallback,
    clearInvites,
    logSearch,
    setLogSearch,
    logPage,
    setLogPage,
    logPageCount,
    pagedLogs,
    inviteSearch,
    setInviteSearch,
    invitePage,
    setInvitePage,
    invitePageCount,
    inviteFilter,
    setInviteFilter,
    pagedInvites,
    fallbackSearch,
    setFallbackSearch,
    fallbackPage,
    setFallbackPage,
    fallbackPageCount,
    pagedFallback,
    confirmQueue,
    confirmSearch,
    setConfirmSearch,
    confirmPage,
    setConfirmPage,
    confirmPageCount,
    pagedConfirmQueue,
    confirmStats,
    queueItems,
    queueStats,
    queueSearch,
    setQueueSearch,
    queuePage,
    setQueuePage,
    queuePageCount,
    pagedQueue,
    clearConfirmQueue,
    auditSearch,
    setAuditSearch,
    auditPage,
    setAuditPage,
    auditPageCount,
    pagedAudit,
    explainInviteError,
    expandedInviteId,
    setExpandedInviteId,
    inviteStats,
    invites,
    selectedTask,
    accessStatus,
    roleSummary,
    mutualContactDiagnostics,
    apiTraceLogs,
    clearApiTrace,
    exportApiTraceJson,
    exportApiTraceCsv,
    accountEvents,
    clearAccountEvents,
    onSettingsChange,
    saveSettings
  });

  useOpenLogsTabListener(setActiveTab);


  return (
    <div className="app">
      <AppOverlays
        importModalOpen={importModalOpen}
        setImportModalOpen={setImportModalOpen}
        manualLoginOpen={manualLoginOpen}
        setManualLoginOpen={setManualLoginOpen}
        hasSelectedTask={hasSelectedTask}
        loginForm={loginForm}
        setLoginForm={setLoginForm}
        startLogin={startLogin}
        completeLogin={completeLogin}
        loginStatus={loginStatus}
        tdataForm={tdataForm}
        setTdataForm={setTdataForm}
        importTdata={importTdata}
        tdataLoading={tdataLoading}
        tdataResult={tdataResult}
        explainTdataError={explainTdataError}
        notificationsOpen={notificationsOpen}
        setNotificationsOpen={setNotificationsOpen}
        notificationsModalRef={notificationsModalRef}
        notifications={notifications}
        filteredNotifications={filteredNotifications}
        notificationFilter={notificationFilter}
        setNotificationFilter={setNotificationFilter}
        setNotifications={setNotifications}
        infoOpen={infoOpen}
        setInfoOpen={setInfoOpen}
        infoTab={infoTab}
        setInfoTab={setInfoTab}
        liveConfirmOpen={liveConfirmOpen}
        setLiveConfirmOpen={setLiveConfirmOpen}
        liveConfirmContext={liveConfirmContext}
        onConfirmLiveInvite={() => {
          setLiveConfirmOpen(false);
          setLiveConfirmContext(null);
          if (liveConfirmResolver.current) {
            liveConfirmResolver.current(true);
            liveConfirmResolver.current = null;
          }
        }}
        onCancelLiveInvite={() => {
          setLiveConfirmOpen(false);
          setLiveConfirmContext(null);
          if (liveConfirmResolver.current) {
            liveConfirmResolver.current(false);
            liveConfirmResolver.current = null;
          }
        }}
        toasts={toasts}
        dismissToast={dismissToast}
      />
      <div className="layout">
        <AppSidebar
          taskSummary={taskSummary}
          globalStatus={globalStatus}
          selectedTaskName={selectedTaskName}
          competitorGroups={competitorGroups}
          assignedAccountCount={assignedAccountCount}
          taskStatus={taskStatus}
          notificationsOpen={notificationsOpen}
          setNotificationsOpen={setNotificationsOpen}
          infoOpen={infoOpen}
          setInfoOpen={setInfoOpen}
          bellRef={bellRef}
          notificationsCount={notifications.length}
          onOpenImport={() => setImportModalOpen(true)}
          createTask={createTask}
          taskSearch={taskSearch}
          setTaskSearch={setTaskSearch}
          taskFilter={taskFilter}
          setTaskFilter={setTaskFilter}
          taskSort={taskSort}
          setTaskSort={setTaskSort}
          filteredTasks={filteredTasks}
          taskStatusMap={taskStatusMap}
          selectedTaskId={selectedTaskId}
          selectTask={selectTask}
          deleteTask={deleteTask}
          hasSelectedTask={hasSelectedTask}
          formatCountdown={formatCountdownWithNow}
          formatTimestamp={formatTimestamp}
          accountById={accountById}
          formatAccountLabel={formatAccountLabel}
        />
        <AppMain
          quickActions={quickActions}
          nowStatus={nowStatus}
          testRun={testRun}
          runTestLive={() => runTest("live")}
          checklist={checklist}
          tabs={tabs}
          taskSettings={taskSettings}
          accountsTab={accountsTab}
          logsTab={logsTabGroup}
          queueTab={queueTabGroup}
          apiTraceTab={apiTraceTab}
          eventsTab={eventsTab}
          settingsTab={settingsTab}
        />
      </div>
      <div className="app-footer">Версия: {APP_VERSION}</div>
    </div>
  );
}
