﻿<template>
  <div class="settings-page">
    <h1>Настройки</h1>
    <p class="settings-intro">
      Каналы оповещений SAC. Значения в БД (<code>source=db</code>) или из <code>sac-api.env</code> (<code>env</code>),
      пока запись не создана через UI.
    </p>

    <p v-if="error" class="error">{{ error }}</p>
    <p v-if="loading">Загрузка…</p>

    <template v-if="!loading">
      <p v-if="success" class="success">{{ success }}</p>

      <div v-if="lastCreatedCode" class="settings-enrollment-banner" role="status">
        <p class="settings-enrollment-banner-title">Код регистрации Seaca</p>
        <p class="settings-enrollment-banner-hint">
          Скопируйте и передайте оператору вместе с URL сервера. Полный код показывается только сейчас.
        </p>
        <div class="settings-enrollment-code-row">
          <code class="settings-enrollment-code-value">{{ lastCreatedCode }}</code>
          <button type="button" @click="copyEnrollmentCode">Копировать</button>
          <button type="button" class="secondary" @click="dismissEnrollmentCode">Скрыть</button>
        </div>
      </div>
      <section class="card settings-card settings-policy">
        <h2>Интерфейс</h2>
        <p class="settings-hint settings-hint-top">
          Блок «Сервер» в левой панели: CPU, RAM, диск, задержка БД и счётчики SAC. Обновляется каждые 30 секунд.
        </p>
        <form class="settings-form" @submit.prevent="saveUiSettings">
          <label class="settings-field settings-inline">
            <input v-model="uiForm.show_sidebar_system_stats" type="checkbox" />
            Показывать блок системной информации в боковой панели
          </label>
          <p v-if="uiLoaded" class="settings-meta">
            Источник: <code>{{ uiLoaded.source }}</code>
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingUi">
              {{ savingUi ? "Сохранение…" : "Сохранить интерфейс" }}
            </button>
          </div>
        </form>
      </section>

      <section class="card settings-card settings-policy">
        <h2>Windows (qwinsta / logoff)</h2>
        <p class="settings-hint settings-hint-top">
          Доменный admin для удалённых команд на Windows-хостах (qwinsta, logoff через агент).
          Формат логина: <code>ДОМЕН\пользователь</code> (например <code>B26\papatramp</code>).
          Пока запись не создана в UI — используются <code>SAC_WIN_ADMIN_*</code> из <code>sac-api.env</code>.
        </p>
        <form class="settings-form" @submit.prevent="saveWinAdminSettings">
          <label class="settings-field">
            Доменный admin
            <input
              v-model="winAdminForm.user"
              type="text"
              autocomplete="off"
              placeholder="B26\papatramp"
            />
          </label>
          <label class="settings-field">
            Пароль
            <input
              v-model="winAdminForm.password"
              type="password"
              autocomplete="new-password"
              :placeholder="winAdminPasswordPlaceholder"
            />
          </label>
          <p v-if="winAdminLoaded" class="settings-meta">
            Настроен: {{ winAdminLoaded.configured ? "да" : "нет" }}
            · источник: <code>{{ winAdminLoaded.source }}</code>
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingWinAdmin">
              {{ savingWinAdmin ? "Сохранение…" : "Сохранить Windows admin" }}
            </button>
          </div>
        </form>
      </section>

      <section class="card settings-card settings-policy">
        <h2>Linux (SSH / обновление ssh-monitor)</h2>
        <p class="settings-hint settings-hint-top">
          SSH-учётка для удалённого запуска <code>/opt/scripts/update_ssh_monitor.sh</code> с карточки Linux-хоста.
          Рекомендуется <code>root</code> или пользователь с <code>sudo NOPASSWD</code>.
          Пока запись не создана в UI — используются <code>SAC_LINUX_ADMIN_*</code> из <code>sac-api.env</code>.
        </p>
        <form class="settings-form" @submit.prevent="saveLinuxAdminSettings">
          <label class="settings-field">
            SSH login
            <input
              v-model="linuxAdminForm.user"
              type="text"
              autocomplete="off"
              placeholder="root"
            />
          </label>
          <label class="settings-field">
            SSH password
            <input
              v-model="linuxAdminForm.password"
              type="password"
              autocomplete="new-password"
              :placeholder="linuxAdminPasswordPlaceholder"
            />
          </label>
          <p v-if="linuxAdminLoaded" class="settings-meta">
            Настроен: {{ linuxAdminLoaded.configured ? "да" : "нет" }}
            · источник: <code>{{ linuxAdminLoaded.source }}</code>
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingLinuxAdmin">
              {{ savingLinuxAdmin ? "Сохранение…" : "Сохранить Linux admin" }}
            </button>
          </div>
        </form>
      </section>

      <section class="card settings-card settings-policy">
        <h2>Обновления агентов</h2>
        <p class="settings-hint settings-hint-top">
          Режим <code>gpo</code> — обновления вручную/GPO; SAC только показывает устаревшие версии.
          Режим <code>sac</code> — «Запросить обновление» ставит флаг в poll; агент self-update;
          через {{ agentUpdateForm.fallback_after_minutes }} мин без ответа — fallback SSH/WinRM с SAC.
        </p>
        <form class="settings-form" @submit.prevent="saveAgentUpdateSettingsForm">
          <label class="settings-field">
            Режим
            <select v-model="agentUpdateForm.mode">
              <option value="gpo">GPO / ручной</option>
              <option value="sac">SAC (poll + fallback)</option>
            </select>
          </label>
          <label class="settings-field settings-inline">
            <input v-model="agentUpdateForm.fallback_enabled" type="checkbox" />
            Авто-fallback SSH/WinRM после таймаута
          </label>
          <label class="settings-field">
            Fallback через (мин)
            <input v-model.number="agentUpdateForm.fallback_after_minutes" type="number" min="1" max="1440" />
          </label>
          <label class="settings-field">
            Recommended RDP-login-monitor
            <input v-model="agentUpdateForm.recommended_rdp_version" type="text" placeholder="2.1.0-SAC" />
          </label>
          <label class="settings-field">
            Recommended ssh-monitor
            <input v-model="agentUpdateForm.recommended_ssh_version" type="text" placeholder="2.1.0-SAC" />
          </label>
          <label class="settings-field">
            WinRM: путь к Deploy-LoginMonitor.ps1 (fallback)
            <input
              v-model="agentUpdateForm.win_agent_update_script"
              type="text"
              placeholder="C:\ProgramData\LoginMonitor\Deploy-LoginMonitor.ps1"
            />
          </label>
          <p v-if="agentUpdateLoaded" class="settings-meta">
            Источник: <code>{{ agentUpdateLoaded.source }}</code>
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingAgentUpdate">
              {{ savingAgentUpdate ? "Сохранение…" : "Сохранить обновления агентов" }}
            </button>
          </div>
        </form>
      </section>

      <section class="card settings-card settings-policy">
        <h2>Правило оповещений</h2>
        <p class="settings-hint settings-hint-top">
          События и problems с severity <strong>≥</strong> порога уходят в выбранные каналы (если канал включён и
          настроен).
        </p>
        <form class="settings-form" @submit.prevent="savePolicy">
          <label class="settings-field">
            Мин. severity
            <select v-model="policyForm.min_severity">
              <option value="info">info</option>
              <option value="warning">warning</option>
              <option value="high">high</option>
              <option value="critical">critical</option>
            </select>
          </label>
          <fieldset class="settings-channels">
            <legend>Каналы</legend>
            <label class="settings-inline">
              <input v-model="policyForm.use_telegram" type="checkbox" />
              Telegram
            </label>
            <label class="settings-inline">
              <input v-model="policyForm.use_webhook" type="checkbox" />
              Webhook
            </label>
            <label class="settings-inline">
              <input v-model="policyForm.use_email" type="checkbox" />
              Email
            </label>
            <label class="settings-inline">
              <input v-model="policyForm.use_mobile" type="checkbox" />
              Seaca (push)
            </label>
          </fieldset>
          <p v-if="policyLoaded" class="settings-meta">
            Источник: <code>{{ policyLoaded.source }}</code>
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingPolicy">
              {{ savingPolicy ? "Сохранение…" : "Сохранить правило" }}
            </button>
          </div>
        </form>
      </section>

      <section class="card settings-card settings-policy">
        <h2>Мобильные устройства (Seaca)</h2>
        <p class="settings-hint settings-hint-top">
          Подключение Android-клиента по коду регистрации. Управление пользователями SAC — в разделе «Пользователи».
        </p>
        <form class="settings-form" @submit.prevent="saveMobileSettings">
          <label class="settings-field settings-inline">
            <input v-model="mobileForm.devices_allowed" type="checkbox" />
            Разрешать мобильным устройствам подключаться
          </label>
          <label class="settings-field">
            Макс. устройств на пользователя
            <input v-model.number="mobileForm.max_devices_per_user" type="number" min="1" max="50" />
          </label>
          <label class="settings-field">
            Мин. версия приложения (необязательно)
            <input v-model="mobileForm.min_app_version" type="text" placeholder="0.1.0" />
          </label>
          <p v-if="mobileLoaded" class="settings-meta">
            FCM: {{ mobileLoaded.fcm_configured ? "настроен" : "не настроен" }}
            · источник: <code>{{ mobileLoaded.source }}</code>
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingMobile">
              {{ savingMobile ? "Сохранение…" : "Сохранить мобильные" }}
            </button>
          </div>
        </form>

        <h3>Коды регистрации</h3>
        <form class="settings-form settings-mobile-code-form" @submit.prevent="createEnrollmentCode">
          <label class="settings-field">
            Метка
            <input v-model="codeForm.label" type="text" placeholder="Телефон Иванова" />
          </label>
          <label class="settings-field">
            Пользователь (необязательно)
            <select v-model="codeForm.target_user_id">
              <option :value="null">— любой с паролем —</option>
              <option v-for="u in sacUsers" :key="u.id" :value="u.id">{{ u.username }} ({{ u.role }})</option>
            </select>
          </label>
          <label class="settings-field">
            Способ входа
            <select v-model="codeForm.login_mode">
              <option value="password">Логин и пароль SAC + код</option>
              <option value="code_only">Только код (пользователь привязан)</option>
            </select>
          </label>
          <label class="settings-field">
            Срок (часов)
            <input v-model.number="codeForm.expires_in_hours" type="number" min="1" max="720" />
          </label>
          <div class="settings-actions">
            <button type="submit" :disabled="creatingCode">
              {{ creatingCode ? "Создание…" : "Выдать код" }}
            </button>
          </div>
        </form>
        <div class="settings-mobile-table-wrap">
          <table class="settings-severity-table">
            <thead>
              <tr>
                <th>Метка</th>
                <th>Префикс</th>
                <th>Пользователь</th>
                <th>Вход</th>
                <th>Использований</th>
                <th>Действия</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="code in enrollmentCodes" :key="code.id">
                <td>{{ code.label || "—" }}</td>
                <td><code>{{ code.code_prefix }}…</code></td>
                <td>{{ code.target_username || "—" }}</td>
                <td>{{ code.login_mode }}</td>
                <td>{{ code.use_count }}/{{ code.max_uses }}</td>
                <td>
                  <button
                    v-if="code.is_active"
                    type="button"
                    class="secondary"
                    @click="revokeCode(code.id)"
                  >
                    Отозвать
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <h3>Подключённые устройства</h3>
        <div class="settings-mobile-table-wrap">
          <table class="settings-severity-table">
            <thead>
              <tr>
                <th>Имя</th>
                <th>Пользователь</th>
                <th>Статус</th>
                <th>FCM</th>
                <th>Последняя активность</th>
                <th>Действия</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="device in mobileDevices" :key="device.id">
                <td>{{ device.display_name }}</td>
                <td>{{ device.username }}</td>
                <td>{{ device.is_active ? "активно" : "отключено" }}</td>
                <td>{{ device.has_fcm_token ? "да" : "нет" }}</td>
                <td>{{ device.last_seen_at || device.enrolled_at }}</td>
                <td class="settings-mobile-actions">
                  <button
                    v-if="device.is_active && device.has_fcm_token"
                    type="button"
                    class="secondary"
                    :disabled="testingPushDeviceId === device.id"
                    @click="testDevicePush(device.id)"
                  >
                    {{ testingPushDeviceId === device.id ? "Отправка…" : "Тест push" }}
                  </button>
                  <button
                    v-if="device.is_active"
                    type="button"
                    class="secondary"
                    @click="revokeDevice(device.id)"
                  >
                    Отключить
                  </button>
                  <button
                    type="button"
                    class="danger"
                    @click="deleteDevice(device.id)"
                  >
                    Удалить
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <section class="card settings-card">
        <h2>Severity по типу события</h2>
        <p class="settings-hint settings-hint-top">
          Переопределение значимости для новых событий после ingest. Исходное severity агента сохраняется в
          <code>details.severity_agent</code>. Пустое значение — как прислал агент (см. «По умолчанию»).
        </p>
        <div class="settings-severity-toolbar">
          <input v-model="severityFilter" type="search" placeholder="Фильтр по type…" />
          <button type="button" :disabled="savingSeverity" @click="saveSeverityOverrides">
            {{ savingSeverity ? "Сохранение…" : "Сохранить severity" }}
          </button>
        </div>
        <div class="settings-severity-table-wrap">
          <table class="settings-severity-table">
            <thead>
              <tr>
                <th>Тип события</th>
                <th>По умолчанию</th>
                <th>Effective</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="row in filteredSeverityRows" :key="row.event_type">
                <td><code>{{ row.event_type }}</code></td>
                <td>{{ row.default_severity }}</td>
                <td>
                  <select v-model="row.selected">
                    <option value="">по умолчанию агента</option>
                    <option value="info">info</option>
                    <option value="warning">warning</option>
                    <option value="high">high</option>
                    <option value="critical">critical</option>
                  </select>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <p class="settings-meta">Записей: {{ filteredSeverityRows.length }} / {{ severityRows.length }}</p>
      </section>

      <section class="card settings-card">
        <h2>Telegram</h2>
        <form class="settings-form" @submit.prevent="saveTelegram">
          <label class="settings-field">
            <input v-model="telegramForm.enabled" type="checkbox" />
            Включить Telegram
          </label>
          <label class="settings-field">
            Bot token
            <input
              v-model="telegramForm.bot_token"
              type="password"
              autocomplete="off"
              :placeholder="tokenPlaceholder"
            />
          </label>
          <label class="settings-field">
            Chat ID
            <input
              v-model="telegramForm.chat_id"
              type="text"
              autocomplete="off"
              :placeholder="chatPlaceholder"
            />
          </label>
          <p v-if="telegramLoaded" class="settings-meta">
            Источник: <code>{{ telegramLoaded.source }}</code>
            · настроен: {{ telegramLoaded.configured ? "да" : "нет" }}
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingTelegram">
              {{ savingTelegram ? "Сохранение…" : "Сохранить" }}
            </button>
            <button type="button" class="secondary" :disabled="testingTelegram" @click="testTelegram">
              {{ testingTelegram ? "Отправка…" : "Проверить Telegram" }}
            </button>
          </div>
        </form>
        <p class="settings-hint">
          Для <strong>UseSAC=exclusive</strong> рекомендуется <code>warning</code> — failed login и problems, без
          успешных RDP (<code>info</code>).
        </p>
      </section>

      <section class="card settings-card">
        <h2>Webhook</h2>
        <form class="settings-form" @submit.prevent="saveWebhook">
          <label class="settings-field">
            <input v-model="webhookForm.enabled" type="checkbox" />
            Включить webhook
          </label>
          <label class="settings-field">
            URL
            <input
              v-model="webhookForm.url"
              type="url"
              autocomplete="off"
              :placeholder="urlPlaceholder"
            />
          </label>
          <label class="settings-field">
            Secret header (необязательно)
            <input v-model="webhookForm.secret_header" type="text" autocomplete="off" placeholder="X-SAC-Token" />
          </label>
          <label class="settings-field">
            Secret value
            <input
              v-model="webhookForm.secret"
              type="password"
              autocomplete="off"
              :placeholder="webhookSecretPlaceholder"
            />
          </label>
          <p v-if="webhookLoaded" class="settings-meta">
            Источник: <code>{{ webhookLoaded.source }}</code>
            · настроен: {{ webhookLoaded.configured ? "да" : "нет" }}
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingWebhook">
              {{ savingWebhook ? "Сохранение…" : "Сохранить" }}
            </button>
            <button type="button" class="secondary" :disabled="testingWebhook" @click="testWebhook">
              {{ testingWebhook ? "Отправка…" : "Проверить webhook" }}
            </button>
          </div>
        </form>
        <p class="settings-hint">JSON POST: <code>kind</code> = event | problem | test; те же пороги severity, что у Telegram.</p>
      </section>

      <section class="card settings-card">
        <h2>Email (SMTP)</h2>
        <form class="settings-form" @submit.prevent="saveEmail">
          <label class="settings-field">
            <input v-model="emailForm.enabled" type="checkbox" />
            Включить email
          </label>
          <label class="settings-field">
            SMTP host
            <input v-model="emailForm.smtp_host" type="text" autocomplete="off" :placeholder="smtpHostPlaceholder" />
          </label>
          <label class="settings-field">
            SMTP port
            <input v-model.number="emailForm.smtp_port" type="number" min="1" max="65535" />
          </label>
          <label class="settings-field">
            SMTP user
            <input v-model="emailForm.smtp_user" type="text" autocomplete="off" />
          </label>
          <label class="settings-field">
            SMTP password
            <input
              v-model="emailForm.smtp_password"
              type="password"
              autocomplete="off"
              :placeholder="smtpPasswordPlaceholder"
            />
          </label>
          <label class="settings-field">
            From
            <input v-model="emailForm.mail_from" type="email" autocomplete="off" />
          </label>
          <label class="settings-field">
            To (через запятую или ;)
            <input v-model="emailForm.mail_to" type="text" autocomplete="off" :placeholder="mailToPlaceholder" />
          </label>
          <label class="settings-field settings-inline">
            <input v-model="emailForm.smtp_starttls" type="checkbox" />
            STARTTLS
          </label>
          <label class="settings-field settings-inline">
            <input v-model="emailForm.smtp_ssl" type="checkbox" />
            SSL (порт 465)
          </label>
          <p v-if="emailLoaded" class="settings-meta">
            Источник: <code>{{ emailLoaded.source }}</code>
            · настроен: {{ emailLoaded.configured ? "да" : "нет" }}
          </p>
          <div class="settings-actions">
            <button type="submit" :disabled="savingEmail">
              {{ savingEmail ? "Сохранение…" : "Сохранить" }}
            </button>
            <button type="button" class="secondary" :disabled="testingEmail" @click="testEmail">
              {{ testingEmail ? "Отправка…" : "Проверить email" }}
            </button>
          </div>
        </form>
      </section>
    </template>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import {
  apiFetch,
  createMobileEnrollmentCode,
  fetchMobileDevices,
  fetchMobileEnrollmentCodes,
  fetchMobileSettings,
  fetchUsers,
  fetchWinAdminSettings,
  fetchLinuxAdminSettings,
  fetchAgentUpdateSettings,
  saveAgentUpdateSettings,
  revokeMobileDevice,
  deleteMobileDevice,
  revokeMobileEnrollmentCode,
  testMobileDevicePush,
  updateMobileSettings,
  updateWinAdminSettings,
  updateLinuxAdminSettings,
  type MobileDevice,
  type MobileEnrollmentCode,
  type MobileSettings,
  type AgentUpdateSettings,
  type UserSummary,
} from "../api";

interface TelegramSettings {
  enabled: boolean;
  configured: boolean;
  chat_id_hint: string | null;
  bot_token_hint: string | null;
  min_severity: string;
  source: string;
}

interface WebhookSettings {
  enabled: boolean;
  configured: boolean;
  url_hint: string | null;
  secret_header: string | null;
  secret_hint: string | null;
  min_severity: string;
  source: string;
}

interface EmailSettings {
  enabled: boolean;
  configured: boolean;
  smtp_host_hint: string | null;
  smtp_port: number;
  smtp_user: string | null;
  smtp_password_hint: string | null;
  mail_from: string | null;
  mail_to_hint: string | null;
  smtp_starttls: boolean;
  smtp_ssl: boolean;
  min_severity: string;
  source: string;
}

interface NotificationPolicy {
  min_severity: string;
  use_telegram: boolean;
  use_webhook: boolean;
  use_email: boolean;
  use_mobile: boolean;
  source: string;
}

interface UiSettings {
  show_sidebar_system_stats: boolean;
  source: string;
}

interface WinAdminSettings {
  configured: boolean;
  user: string | null;
  password_hint: string | null;
  source: string;
}

interface LinuxAdminSettings {
  configured: boolean;
  user: string | null;
  password_hint: string | null;
  source: string;
}

interface SeverityOverrideRowApi {
  event_type: string;
  default_severity: string;
  override_severity: string | null;
}

interface SeverityOverrideRowUi extends SeverityOverrideRowApi {
  selected: string;
}

const loading = ref(true);
const savingPolicy = ref(false);
const savingUi = ref(false);
const savingWinAdmin = ref(false);
const savingLinuxAdmin = ref(false);
const savingAgentUpdate = ref(false);
const savingTelegram = ref(false);
const savingWebhook = ref(false);
const savingEmail = ref(false);
const savingSeverity = ref(false);
const savingMobile = ref(false);
const creatingCode = ref(false);
const testingTelegram = ref(false);
const testingWebhook = ref(false);
const testingEmail = ref(false);
const testingPushDeviceId = ref<number | null>(null);
const error = ref("");
const success = ref("");
const telegramLoaded = ref<TelegramSettings | null>(null);
const webhookLoaded = ref<WebhookSettings | null>(null);
const emailLoaded = ref<EmailSettings | null>(null);
const policyLoaded = ref<NotificationPolicy | null>(null);
const uiLoaded = ref<UiSettings | null>(null);
const winAdminLoaded = ref<WinAdminSettings | null>(null);
const linuxAdminLoaded = ref<LinuxAdminSettings | null>(null);
const agentUpdateLoaded = ref<AgentUpdateSettings | null>(null);
const severityRows = ref<SeverityOverrideRowUi[]>([]);
const severityFilter = ref("");
const mobileLoaded = ref<MobileSettings | null>(null);
const enrollmentCodes = ref<MobileEnrollmentCode[]>([]);
const mobileDevices = ref<MobileDevice[]>([]);
const sacUsers = ref<UserSummary[]>([]);
const lastCreatedCode = ref("");
const ENROLLMENT_CODE_STORAGE_KEY = "sac_mobile_last_enrollment_code";

function restoreLastCreatedCode() {
  const stored = sessionStorage.getItem(ENROLLMENT_CODE_STORAGE_KEY);
  if (stored) {
    lastCreatedCode.value = stored;
  }
}

function persistLastCreatedCode(code: string) {
  sessionStorage.setItem(ENROLLMENT_CODE_STORAGE_KEY, code);
}

function dismissEnrollmentCode() {
  lastCreatedCode.value = "";
  sessionStorage.removeItem(ENROLLMENT_CODE_STORAGE_KEY);
}

async function copyEnrollmentCode() {
  if (!lastCreatedCode.value) return;
  error.value = "";
  try {
    await navigator.clipboard.writeText(lastCreatedCode.value);
    success.value = "Код скопирован в буфер обмена.";
  } catch {
    error.value = "Не удалось скопировать автоматически — выделите код вручную.";
  }
}

const policyForm = reactive({
  min_severity: "warning",
  use_telegram: true,
  use_webhook: false,
  use_email: false,
  use_mobile: false,
});

const mobileForm = reactive({
  devices_allowed: false,
  max_devices_per_user: 3,
  min_app_version: "",
});

const codeForm = reactive({
  label: "",
  target_user_id: null as number | null,
  login_mode: "password",
  expires_in_hours: 24,
  max_uses: 1,
});

const uiForm = reactive({
  show_sidebar_system_stats: true,
});

const winAdminForm = reactive({
  user: "",
  password: "",
});

const linuxAdminForm = reactive({
  user: "",
  password: "",
});

const agentUpdateForm = reactive({
  mode: "gpo" as "gpo" | "sac",
  fallback_enabled: true,
  fallback_after_minutes: 15,
  recommended_rdp_version: "",
  recommended_ssh_version: "",
  win_agent_update_script: "",
});

const telegramForm = reactive({
  enabled: false,
  bot_token: "",
  chat_id: "",
});

const webhookForm = reactive({
  enabled: false,
  url: "",
  secret_header: "",
  secret: "",
});

const emailForm = reactive({
  enabled: false,
  smtp_host: "",
  smtp_port: 587,
  smtp_user: "",
  smtp_password: "",
  mail_from: "",
  mail_to: "",
  smtp_starttls: true,
  smtp_ssl: false,
});

const tokenPlaceholder = computed(() =>
  telegramLoaded.value?.bot_token_hint
    ? `оставить ${telegramLoaded.value.bot_token_hint}`
    : "обязателен при первой настройке",
);
const chatPlaceholder = computed(() =>
  telegramLoaded.value?.chat_id_hint
    ? `оставить ${telegramLoaded.value.chat_id_hint}`
    : "обязателен при первой настройке",
);
const urlPlaceholder = computed(() =>
  webhookLoaded.value?.url_hint ? `оставить ${webhookLoaded.value.url_hint}` : "https://…",
);
const webhookSecretPlaceholder = computed(() =>
  webhookLoaded.value?.secret_hint ? `оставить ${webhookLoaded.value.secret_hint}` : "необязательно",
);
const smtpHostPlaceholder = computed(() =>
  emailLoaded.value?.smtp_host_hint ? `оставить ${emailLoaded.value.smtp_host_hint}` : "smtp.example.com",
);
const smtpPasswordPlaceholder = computed(() =>
  emailLoaded.value?.smtp_password_hint ? `оставить ${emailLoaded.value.smtp_password_hint}` : "необязательно",
);
const mailToPlaceholder = computed(() =>
  emailLoaded.value?.mail_to_hint ? `оставить ${emailLoaded.value.mail_to_hint}` : "admin@example.com",
);
const winAdminPasswordPlaceholder = computed(() =>
  winAdminLoaded.value?.password_hint
    ? `оставить ${winAdminLoaded.value.password_hint}`
    : "обязателен при первой настройке",
);
const linuxAdminPasswordPlaceholder = computed(() =>
  linuxAdminLoaded.value?.password_hint
    ? `оставить ${linuxAdminLoaded.value.password_hint}`
    : "обязателен при первой настройке",
);

const filteredSeverityRows = computed(() => {
  const q = severityFilter.value.trim().toLowerCase();
  if (!q) return severityRows.value;
  return severityRows.value.filter((row) => row.event_type.toLowerCase().includes(q));
});

function mapSeverityRows(items: SeverityOverrideRowApi[]): SeverityOverrideRowUi[] {
  return items.map((row) => ({
    ...row,
    selected: row.override_severity ?? "",
  }));
}

async function loadSeverityOverrides() {
  const data = await apiFetch<{ items: SeverityOverrideRowApi[] }>(
    "/api/v1/settings/notifications/severity-overrides",
  );
  severityRows.value = mapSeverityRows(data.items);
}

async function saveSeverityOverrides() {
  savingSeverity.value = true;
  error.value = "";
  success.value = "";
  try {
    const overrides: Record<string, string | null> = {};
    for (const row of severityRows.value) {
      const prev = row.override_severity ?? "";
      const next = row.selected.trim();
      if (next === prev) continue;
      overrides[row.event_type] = next || null;
    }
    if (Object.keys(overrides).length === 0) {
      error.value = "Нет изменений severity для сохранения";
      return;
    }
    const data = await apiFetch<{ items: SeverityOverrideRowApi[] }>(
      "/api/v1/settings/notifications/severity-overrides",
      {
        method: "PUT",
        body: JSON.stringify({ overrides }),
      },
    );
    severityRows.value = mapSeverityRows(data.items);
    success.value = "Severity по типам событий сохранён.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения severity";
  } finally {
    savingSeverity.value = false;
  }
}

async function loadUiSettings() {
  const data = await apiFetch<UiSettings>("/api/v1/settings/ui");
  uiLoaded.value = data;
  uiForm.show_sidebar_system_stats = data.show_sidebar_system_stats;
}

async function saveUiSettings() {
  savingUi.value = true;
  error.value = "";
  success.value = "";
  try {
    uiLoaded.value = await apiFetch<UiSettings>("/api/v1/settings/ui", {
      method: "PUT",
      body: JSON.stringify({
        show_sidebar_system_stats: uiForm.show_sidebar_system_stats,
      }),
    });
    success.value = "Настройки интерфейса сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения";
  } finally {
    savingUi.value = false;
  }
}

async function loadWinAdminSettings() {
  const data = await fetchWinAdminSettings();
  winAdminLoaded.value = data;
  winAdminForm.user = data.user ?? "";
  winAdminForm.password = "";
}

async function saveWinAdminSettings() {
  savingWinAdmin.value = true;
  error.value = "";
  success.value = "";
  try {
    const payload: { user?: string; password?: string } = {};
    if (winAdminForm.user.trim()) {
      payload.user = winAdminForm.user.trim();
    }
    if (winAdminForm.password.trim()) {
      payload.password = winAdminForm.password;
    }
    if (!payload.user && !payload.password) {
      error.value = "Укажите логин и/или новый пароль Windows admin";
      return;
    }
    if (!winAdminLoaded.value?.configured && (!payload.user || !payload.password)) {
      error.value = "Для первой настройки укажите логин и пароль";
      return;
    }
    winAdminLoaded.value = await updateWinAdminSettings(payload);
    winAdminForm.user = winAdminLoaded.value.user ?? winAdminForm.user;
    winAdminForm.password = "";
    success.value = "Учётные данные Windows admin сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения Windows admin";
  } finally {
    savingWinAdmin.value = false;
  }
}

async function loadLinuxAdminSettings() {
  const data = await fetchLinuxAdminSettings();
  linuxAdminLoaded.value = data;
  linuxAdminForm.user = data.user ?? "";
  linuxAdminForm.password = "";
}

async function saveLinuxAdminSettings() {
  savingLinuxAdmin.value = true;
  error.value = "";
  success.value = "";
  try {
    const payload: { user?: string; password?: string } = {};
    if (linuxAdminForm.user.trim()) {
      payload.user = linuxAdminForm.user.trim();
    }
    if (linuxAdminForm.password.trim()) {
      payload.password = linuxAdminForm.password;
    }
    if (!payload.user && !payload.password) {
      error.value = "Укажите логин и/или новый пароль Linux admin";
      return;
    }
    if (!linuxAdminLoaded.value?.configured && (!payload.user || !payload.password)) {
      error.value = "Для первой настройки укажите логин и пароль";
      return;
    }
    linuxAdminLoaded.value = await updateLinuxAdminSettings(payload);
    linuxAdminForm.user = linuxAdminLoaded.value.user ?? linuxAdminForm.user;
    linuxAdminForm.password = "";
    success.value = "Учётные данные Linux admin сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения Linux admin";
  } finally {
    savingLinuxAdmin.value = false;
  }
}

async function loadAgentUpdateSettings() {
  const data = await fetchAgentUpdateSettings();
  agentUpdateLoaded.value = data;
  agentUpdateForm.mode = data.mode;
  agentUpdateForm.fallback_enabled = data.fallback_enabled;
  agentUpdateForm.fallback_after_minutes = data.fallback_after_minutes;
  agentUpdateForm.recommended_rdp_version = data.recommended_rdp_version ?? "";
  agentUpdateForm.recommended_ssh_version = data.recommended_ssh_version ?? "";
  agentUpdateForm.win_agent_update_script = data.win_agent_update_script ?? "";
}

async function saveAgentUpdateSettingsForm() {
  savingAgentUpdate.value = true;
  error.value = "";
  success.value = "";
  try {
    agentUpdateLoaded.value = await saveAgentUpdateSettings({
      mode: agentUpdateForm.mode,
      fallback_enabled: agentUpdateForm.fallback_enabled,
      fallback_after_minutes: agentUpdateForm.fallback_after_minutes,
      recommended_rdp_version: agentUpdateForm.recommended_rdp_version.trim() || null,
      recommended_ssh_version: agentUpdateForm.recommended_ssh_version.trim() || null,
      win_agent_update_script: agentUpdateForm.win_agent_update_script.trim() || null,
    });
    success.value = "Настройки обновления агентов сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения agent-updates";
  } finally {
    savingAgentUpdate.value = false;
  }
}

async function loadMobileSection() {
  mobileLoaded.value = await fetchMobileSettings();
  mobileForm.devices_allowed = mobileLoaded.value.devices_allowed;
  mobileForm.max_devices_per_user = mobileLoaded.value.max_devices_per_user;
  mobileForm.min_app_version = mobileLoaded.value.min_app_version ?? "";
  enrollmentCodes.value = await fetchMobileEnrollmentCodes();
  mobileDevices.value = await fetchMobileDevices();
  const users = await fetchUsers();
  sacUsers.value = users.items;
}

async function saveMobileSettings() {
  savingMobile.value = true;
  error.value = "";
  success.value = "";
  try {
    mobileLoaded.value = await updateMobileSettings({
      devices_allowed: mobileForm.devices_allowed,
      max_devices_per_user: mobileForm.max_devices_per_user,
      min_app_version: mobileForm.min_app_version.trim() || null,
    });
    success.value = "Настройки мобильных устройств сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения";
  } finally {
    savingMobile.value = false;
  }
}

async function createEnrollmentCode() {
  creatingCode.value = true;
  error.value = "";
  success.value = "";
  try {
    const created = await createMobileEnrollmentCode({
      label: codeForm.label,
      target_user_id: codeForm.target_user_id,
      login_mode: codeForm.login_mode,
      max_uses: codeForm.max_uses,
      expires_in_hours: codeForm.expires_in_hours,
    });
    lastCreatedCode.value = created.enrollment_code;
    persistLastCreatedCode(created.enrollment_code);
    enrollmentCodes.value = await fetchMobileEnrollmentCodes();
    codeForm.label = "";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка создания кода";
  } finally {
    creatingCode.value = false;
  }
}

async function revokeCode(codeId: number) {
  error.value = "";
  success.value = "";
  try {
    await revokeMobileEnrollmentCode(codeId);
    enrollmentCodes.value = await fetchMobileEnrollmentCodes();
    success.value = "Код отозван.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка";
  }
}

async function revokeDevice(deviceId: number) {
  error.value = "";
  success.value = "";
  try {
    await revokeMobileDevice(deviceId);
    mobileDevices.value = await fetchMobileDevices();
    success.value = "Устройство отключено.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка";
  }
}

async function deleteDevice(deviceId: number) {
  const device = mobileDevices.value.find((item) => item.id === deviceId);
  const label = device?.display_name || `#${deviceId}`;
  if (!window.confirm(`Удалить запись устройства «${label}» из базы? Повторная привязка потребует новый код enroll.`)) {
    return;
  }
  error.value = "";
  success.value = "";
  try {
    await deleteMobileDevice(deviceId);
    mobileDevices.value = await fetchMobileDevices();
    success.value = "Запись устройства удалена.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка";
  }
}

async function testDevicePush(deviceId: number) {
  error.value = "";
  success.value = "";
  testingPushDeviceId.value = deviceId;
  try {
    const res = await testMobileDevicePush(deviceId);
    success.value = res.message;
    window.scrollTo({ top: 0, behavior: "smooth" });
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка push";
    window.scrollTo({ top: 0, behavior: "smooth" });
  } finally {
    testingPushDeviceId.value = null;
  }
}

async function load() {
  loading.value = true;
  error.value = "";
  try {
    const data = await apiFetch<{
      policy: NotificationPolicy;
      telegram: TelegramSettings;
      webhook: WebhookSettings;
      email: EmailSettings;
    }>("/api/v1/settings/notifications");
    policyLoaded.value = data.policy;
    policyForm.min_severity = data.policy.min_severity;
    policyForm.use_telegram = data.policy.use_telegram;
    policyForm.use_webhook = data.policy.use_webhook;
    policyForm.use_email = data.policy.use_email;
    policyForm.use_mobile = data.policy.use_mobile ?? false;
    telegramLoaded.value = data.telegram;
    webhookLoaded.value = data.webhook;
    emailLoaded.value = data.email;
    telegramForm.enabled = data.telegram.enabled;
    telegramForm.bot_token = "";
    telegramForm.chat_id = "";
    webhookForm.enabled = data.webhook.enabled;
    webhookForm.url = "";
    webhookForm.secret_header = data.webhook.secret_header ?? "";
    webhookForm.secret = "";
    emailForm.enabled = data.email.enabled;
    emailForm.smtp_host = "";
    emailForm.smtp_port = data.email.smtp_port || 587;
    emailForm.smtp_user = data.email.smtp_user ?? "";
    emailForm.smtp_password = "";
    emailForm.mail_from = data.email.mail_from ?? "";
    emailForm.mail_to = "";
    emailForm.smtp_starttls = data.email.smtp_starttls;
    emailForm.smtp_ssl = data.email.smtp_ssl;
    await loadUiSettings();
    await loadWinAdminSettings();
    await loadLinuxAdminSettings();
    await loadAgentUpdateSettings();
    await loadSeverityOverrides();
    await loadMobileSection();
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка загрузки";
  } finally {
    loading.value = false;
  }
}

async function savePolicy() {
  savingPolicy.value = true;
  error.value = "";
  success.value = "";
  try {
    policyLoaded.value = await apiFetch<NotificationPolicy>("/api/v1/settings/notifications/policy", {
      method: "PUT",
      body: JSON.stringify({
        min_severity: policyForm.min_severity,
        use_telegram: policyForm.use_telegram,
        use_webhook: policyForm.use_webhook,
        use_email: policyForm.use_email,
        use_mobile: policyForm.use_mobile,
      }),
    });
    success.value = "Правило оповещений сохранено.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения";
  } finally {
    savingPolicy.value = false;
  }
}

async function saveTelegram() {
  savingTelegram.value = true;
  error.value = "";
  success.value = "";
  try {
    const body: Record<string, unknown> = {
      enabled: telegramForm.enabled,
    };
    if (telegramForm.bot_token.trim()) body.bot_token = telegramForm.bot_token.trim();
    if (telegramForm.chat_id.trim()) body.chat_id = telegramForm.chat_id.trim();

    telegramLoaded.value = await apiFetch<TelegramSettings>("/api/v1/settings/notifications/telegram", {
      method: "PUT",
      body: JSON.stringify(body),
    });
    telegramForm.bot_token = "";
    telegramForm.chat_id = "";
    success.value = "Настройки Telegram сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения";
  } finally {
    savingTelegram.value = false;
  }
}

async function saveWebhook() {
  savingWebhook.value = true;
  error.value = "";
  success.value = "";
  try {
    const body: Record<string, unknown> = {
      enabled: webhookForm.enabled,
      secret_header: webhookForm.secret_header.trim() || null,
    };
    if (webhookForm.url.trim()) body.url = webhookForm.url.trim();
    if (webhookForm.secret.trim()) body.secret = webhookForm.secret.trim();

    webhookLoaded.value = await apiFetch<WebhookSettings>("/api/v1/settings/notifications/webhook", {
      method: "PUT",
      body: JSON.stringify(body),
    });
    webhookForm.url = "";
    webhookForm.secret = "";
    success.value = "Настройки webhook сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения";
  } finally {
    savingWebhook.value = false;
  }
}

async function testTelegram() {
  testingTelegram.value = true;
  error.value = "";
  success.value = "";
  try {
    const res = await apiFetch<{ message: string }>("/api/v1/settings/notifications/telegram/test", {
      method: "POST",
    });
    success.value = res.message;
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка отправки";
  } finally {
    testingTelegram.value = false;
  }
}

async function testWebhook() {
  testingWebhook.value = true;
  error.value = "";
  success.value = "";
  try {
    const res = await apiFetch<{ message: string }>("/api/v1/settings/notifications/webhook/test", {
      method: "POST",
    });
    success.value = res.message;
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка отправки";
  } finally {
    testingWebhook.value = false;
  }
}

async function saveEmail() {
  savingEmail.value = true;
  error.value = "";
  success.value = "";
  try {
    const body: Record<string, unknown> = {
      enabled: emailForm.enabled,
      smtp_port: emailForm.smtp_port,
      smtp_starttls: emailForm.smtp_starttls,
      smtp_ssl: emailForm.smtp_ssl,
    };
    if (emailForm.smtp_host.trim()) body.smtp_host = emailForm.smtp_host.trim();
    if (emailForm.smtp_user.trim()) body.smtp_user = emailForm.smtp_user.trim();
    if (emailForm.smtp_password.trim()) body.smtp_password = emailForm.smtp_password.trim();
    if (emailForm.mail_from.trim()) body.mail_from = emailForm.mail_from.trim();
    if (emailForm.mail_to.trim()) body.mail_to = emailForm.mail_to.trim();

    emailLoaded.value = await apiFetch<EmailSettings>("/api/v1/settings/notifications/email", {
      method: "PUT",
      body: JSON.stringify(body),
    });
    emailForm.smtp_host = "";
    emailForm.smtp_password = "";
    emailForm.mail_to = "";
    success.value = "Настройки email сохранены.";
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка сохранения";
  } finally {
    savingEmail.value = false;
  }
}

async function testEmail() {
  testingEmail.value = true;
  error.value = "";
  success.value = "";
  try {
    const res = await apiFetch<{ message: string }>("/api/v1/settings/notifications/email/test", {
      method: "POST",
    });
    success.value = res.message;
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Ошибка отправки";
  } finally {
    testingEmail.value = false;
  }
}

onMounted(() => {
  restoreLastCreatedCode();
  load();
});
</script>

<style scoped>
.settings-page {
  max-width: 52rem;
}

.settings-intro {
  color: #9aa4b2;
  margin-bottom: 1.25rem;
}

.settings-card {
  margin-bottom: 1.25rem;
}

.settings-card h2 {
  margin-top: 0;
}

.settings-form {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  margin-bottom: 1rem;
}

.settings-field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.settings-field input[type="text"],
.settings-field input[type="password"],
.settings-field input[type="url"],
.settings-field select {
  max-width: 28rem;
}

.settings-policy {
  border-color: #3d4f66;
}

.settings-hint-top {
  margin-top: 0;
  margin-bottom: 1rem;
}

.settings-channels {
  border: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.settings-channels legend {
  font-size: 0.9rem;
  color: #9aa4b2;
  margin-bottom: 0.35rem;
}

.settings-inline {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}

.settings-meta {
  font-size: 0.85rem;
  color: #9aa4b2;
  margin: 0;
}

.settings-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.settings-actions button.secondary {
  background: #2a3441;
  color: #c5d0dc;
}

.settings-hint {
  font-size: 0.9rem;
  color: #9aa4b2;
  margin: 0;
}

.success {
  color: #6dd196;
}

.settings-severity-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  align-items: center;
}

.settings-severity-toolbar input[type="search"] {
  flex: 1;
  min-width: 12rem;
}

.settings-severity-table-wrap {
  max-height: 24rem;
  overflow: auto;
  border: 1px solid #2a3441;
  border-radius: 6px;
}

.settings-severity-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.settings-severity-table th,
.settings-severity-table td {
  text-align: left;
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid #2a3441;
}

.settings-severity-table select {
  min-width: 11rem;
}

.settings-mobile-table-wrap {
  max-height: 16rem;
  overflow: auto;
  border: 1px solid #2a3441;
  border-radius: 6px;
  margin-bottom: 1rem;
}

.settings-mobile-code-form {
  margin-top: 0.5rem;
}

.settings-enrollment-banner {
  margin: 0 0 1.25rem;
  padding: 1rem 1.1rem;
  border: 1px solid #3d8b5f;
  border-radius: 8px;
  background: #15251c;
}

.settings-enrollment-banner-title {
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
  font-weight: 600;
  color: #8ce8b0;
}

.settings-enrollment-banner-hint {
  margin: 0 0 0.75rem;
  color: #a8b8c8;
  font-size: 0.92rem;
}

.settings-enrollment-code-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.settings-enrollment-code-value {
  flex: 1 1 16rem;
  padding: 0.55rem 0.65rem;
  border-radius: 6px;
  background: #0f1419;
  color: #e8f0f8;
  word-break: break-all;
  user-select: all;
}

.settings-mobile-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
</style>
