const state = {
  user: 'supervisor',
  role: 'supervisor',
  token: null,
  authSource: 'local',
  fullName: null,
  selectedRecordingId: null,
  recordingPreviewUrl: null,
  logLines: [],
  knowledge: {
    categories: [],
    articles: [],
    selectedArticleId: '',
    lastQuery: '',
  },
  oidc: {
    enabled: false,
    loginPath: '/auth/oidc/start?return_mode=popup',
    providerLabel: 'Keycloak',
  },
};

const $ = (id) => document.getElementById(id);
const SESSION_STORAGE_KEY = 'cc_session';
const ROLE_LABELS = {
  admin: 'Администратор',
  supervisor: 'Супервизор',
  operator: 'Оператор',
  analyst: 'Аналитик',
};


function destinationForRole(role) {
  if (role === 'admin') {
    return '/admin';
  }
  if (role === 'supervisor') {
    return '/supervisor';
  }
  if (role === 'analyst') {
    return '/analyst';
  }
  return '/operator';
}

function applyRoleNavigation() {
  document.querySelectorAll('[data-shell]').forEach((link) => {
    const allowed = (link.dataset.roles || '')
      .split(',')
      .map((item) => item.trim())
      .filter(Boolean);
    link.style.display = !allowed.length || allowed.includes(state.role) ? '' : 'none';
  });
}

function ensurePageAccess() {
  const allowed = ['admin', 'supervisor'];
  if (!allowed.includes(state.role)) {
    window.location.href = destinationForRole(state.role);
    return false;
  }
  return true;
}

function persistSession() {
  window.localStorage.setItem(
    SESSION_STORAGE_KEY,
    JSON.stringify({
      access_token: state.token,
      user: state.user,
      role: state.role,
      auth_source: state.authSource,
      full_name: state.fullName,
    }),
  );
}

function clearStoredSession() {
  window.localStorage.removeItem(SESSION_STORAGE_KEY);
}

function restoreStoredSession() {
  const raw = window.localStorage.getItem(SESSION_STORAGE_KEY);
  if (!raw) {
    return false;
  }
  try {
    const payload = JSON.parse(raw);
    if (!payload?.access_token) {
      clearStoredSession();
      return false;
    }
    state.token = payload.access_token;
    state.user = payload.user || state.user;
    state.role = payload.role || state.role;
    state.authSource = payload.auth_source || 'local';
    state.fullName = payload.full_name || null;
    $('sessionUser').value = state.user;
    $('sessionRole').value = state.role;
    if ($('authPanel')) {
      $('authPanel').style.display = 'none';
    }
    updateProfileMeta();
    applyRoleNavigation();
    return true;
  } catch {
    clearStoredSession();
    return false;
  }
}

function logout() {
  if (state.recordingPreviewUrl) {
    URL.revokeObjectURL(state.recordingPreviewUrl);
    state.recordingPreviewUrl = null;
  }
  clearStoredSession();
  window.location.href = '/';
}

function syncSessionFromInputs() {
  state.user = $('sessionUser').value.trim() || 'supervisor';
  state.role = $('sessionRole').value || 'supervisor';
}

function log(message, payload) {
  const ts = new Date().toLocaleTimeString();
  const suffix = payload ? ` ${JSON.stringify(payload)}` : '';
  state.logLines.unshift(`[${ts}] ${message}${suffix}`);
  state.logLines = state.logLines.slice(0, 14);
  $('eventLog').textContent = state.logLines.join('\n');
}

function escapeHtml(value) {
  return String(value ?? '')
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;');
}

function renderSummaryCard(label, value, note = '') {
  return `
    <div class="summary-card">
      <div class="summary-label">${label}</div>
      <div class="summary-value">${value}</div>
      ${note ? `<div class="summary-note">${note}</div>` : ''}
    </div>
  `;
}

function updateProfileMeta() {
  const profileName = $('profileName');
  const profileRole = $('profileRole');
  if (!profileName || !profileRole) {
    return;
  }
  profileName.textContent = state.fullName || state.user || 'Пользователь KonturCC';
  profileRole.textContent = ROLE_LABELS[state.role] || state.role || 'Пользователь';
}

function updateSessionInfo(extra = '') {
  if (!state.token) {
    $('sessionInfo').textContent = 'Токен: отсутствует';
    return;
  }
  const source = state.authSource === 'oidc' ? 'корпоративный' : 'локальный';
  $('sessionInfo').textContent = `Токен активен | роль: ${state.role} | вход: ${source}${extra ? ` | ${extra}` : ''}`;
}

function applyAuthenticatedSession(data) {
  state.token = data.access_token;
  state.role = data.role;
  state.user = data.username || state.user;
  state.authSource = data.auth_source || 'local';
  state.fullName = data.full_name || null;
  $('sessionUser').value = state.user;
  $('sessionRole').value = data.role;
  updateSessionInfo(data.provider ? `провайдер: ${data.provider}` : '');
  updateProfileMeta();
  persistSession();
  applyRoleNavigation();
}

async function api(service, path, options = {}) {
  syncSessionFromInputs();
  const headers = {
    'Content-Type': 'application/json',
    'X-User': state.user,
    'X-Role': state.role,
    ...(options.headers || {}),
  };
  if (state.token) {
    headers.Authorization = `Bearer ${state.token}`;
  }

  const response = await fetch(`/proxy/${service}/${path}`, { ...options, headers });
  let data;
  try {
    data = await response.json();
  } catch {
    data = { error: 'Сервис вернул ответ не в JSON-формате' };
  }
  if (!response.ok) {
    if (response.status === 401 && state.token) {
      state.token = null;
      state.authSource = 'local';
      clearStoredSession();
      updateSessionInfo('требуется повторный вход');
      window.location.href = '/';
    }
    throw new Error(`${response.status}: ${JSON.stringify(data)}`);
  }
  return data;
}

async function fetchBinary(service, path) {
  syncSessionFromInputs();
  const headers = {
    'X-User': state.user,
    'X-Role': state.role,
  };
  if (state.token) {
    headers.Authorization = `Bearer ${state.token}`;
  }
  const response = await fetch(`/proxy/${service}/${path}`, {
    method: 'GET',
    headers,
  });
  if (!response.ok) {
    const detail = await response.text();
    if (response.status === 401 && state.token) {
      state.token = null;
      state.authSource = 'local';
      clearStoredSession();
      updateSessionInfo('требуется повторный вход');
      window.location.href = '/';
    }
    throw new Error(`${response.status}: ${detail}`);
  }
  return response;
}

async function checkGateway() {
  try {
    const response = await fetch('/health');
    const data = await response.json();
    $('gatewayStatus').textContent = `Шлюз: ${data.status === 'ok' ? 'готов' : data.status}`;
  } catch {
    $('gatewayStatus').textContent = 'Шлюз: недоступен';
  }
}

async function loadOidcConfig() {
  try {
    const response = await fetch('/proxy/auth/auth/oidc/config');
    const data = await response.json();
    state.oidc.enabled = Boolean(data.enabled);
    state.oidc.loginPath = data.login_path || '/auth/oidc/start?return_mode=popup';
    state.oidc.providerLabel = data.provider_label || 'Keycloak';
    $('corporateLoginBtn').disabled = !state.oidc.enabled;
    $('corporateLoginBtn').textContent = state.oidc.enabled
      ? `Корпоративный вход (${state.oidc.providerLabel})`
      : 'Корпоративный вход недоступен';
  } catch {
    $('corporateLoginBtn').disabled = true;
    $('corporateLoginBtn').textContent = 'Корпоративный вход недоступен';
  }
}

function startCorporateLogin() {
  if (!state.oidc.enabled) {
    log('Корпоративный вход недоступен');
    return;
  }
  const popup = window.open(`/proxy/auth${state.oidc.loginPath}`, 'oidc-login', 'width=620,height=760');
  if (!popup) {
    log('Не удалось открыть окно корпоративного входа');
    return;
  }
  log('Открыт корпоративный вход', { provider: state.oidc.providerLabel });
}

function handleOidcMessage(event) {
  if (!event?.data || typeof event.data !== 'object') {
    return;
  }
  if (event.data.type === 'oidc-login' && event.data.access_token) {
    applyAuthenticatedSession(event.data);
    log('Корпоративный вход выполнен', { user: state.user, role: state.role });
    return;
  }
  if (event.data.type === 'oidc-error') {
    log('Корпоративный вход не выполнен', { error: event.data.message || 'неизвестно' });
    updateSessionInfo(`ошибка SSO: ${event.data.message || 'неизвестно'}`);
  }
}

async function login() {
  syncSessionFromInputs();
  try {
    const data = await api('auth', 'auth/login', {
      method: 'POST',
      body: JSON.stringify({
        username: state.user,
        password: $('sessionPassword').value,
      }),
    });
    applyAuthenticatedSession({ ...data, username: state.user, auth_source: data.auth_source || 'local' });
    log('Локальный вход выполнен', { user: state.user, role: data.role });
  } catch (err) {
    $('sessionInfo').textContent = `Ошибка входа: ${err.message}`;
    log('Вход не выполнен', { error: err.message });
  }
}

function renderRealtime(data) {
  const agents = data?.agents || {};
  const byState = agents.by_state || {};
  const firstQueue = (data?.queues || [])[0] || {};
  $('realtimeSummary').innerHTML = [
    renderSummaryCard('Агентов', Number(agents.total || 0), 'Текущее состояние команды'),
    renderSummaryCard('Готовы', Number(byState.READY || 0), 'Свободны'),
    renderSummaryCard('Заняты', Number(byState.BUSY || 0), 'В обработке'),
    renderSummaryCard('Очередь', firstQueue.queue_id || 'нет', `В очереди: ${Number(firstQueue.in_queue || 0)}`),
  ].join('');
  $('realtimeOutput').textContent = JSON.stringify(data, null, 2);
}

function renderKpi(data) {
  const kpi = data?.kpi || {};
  $('kpiSummary').innerHTML = [
    renderSummaryCard('SL', `${Number(kpi.SL || 0).toFixed(2)}%`),
    renderSummaryCard('ASA', `${Number(kpi.ASA || 0).toFixed(2)} с`),
    renderSummaryCard('AHT', `${Number(kpi.AHT || 0).toFixed(2)} с`),
    renderSummaryCard('Потери', `${Number(kpi.Abandon || 0).toFixed(2)}%`),
    renderSummaryCard('FCR', `${Number(kpi.FCR || 0).toFixed(2)}%`),
  ].join('');
  $('kpiOutput').textContent = JSON.stringify(data, null, 2);
}

function formatTime(ts) {
  if (!ts) {
    return '-';
  }
  const date = new Date(ts);
  if (Number.isNaN(date.getTime())) {
    return ts;
  }
  return date.toLocaleString();
}

function renderLiveCalls(items) {
  if (!items.length) {
    $('liveCallsList').innerHTML = '<li class="empty-state">Новых звонков от Asterisk пока нет.</li>';
    return;
  }

  $('liveCallsList').innerHTML = items
    .map(
      (item) => `
        <li>
          <strong>${item.call_id}</strong><br/>
          обращение: ${item.interaction_id || '-'}<br/>
          статус: ${item.status}<br/>
          очередь: ${item.queue_id || '-'}<br/>
          начат: ${formatTime(item.started_at)}<br/>
          завершён: ${formatTime(item.ended_at)}<br/>
          запись: ${item.has_recording ? 'да' : 'нет'}
        </li>
      `,
    )
    .join('');
}

function kbCategoryName(categoryId) {
  const category = state.knowledge.categories.find((item) => item.category_id === categoryId);
  return category?.name || categoryId || 'Без категории';
}

function kbLanguageLabel(language) {
  return String(language || 'ru').toUpperCase();
}

function renderKnowledgeSummary() {
  const ruCount = state.knowledge.articles.filter((item) => item.language === 'ru').length;
  const kzCount = state.knowledge.articles.filter((item) => item.language === 'kz').length;
  $('kbSummary').innerHTML = [
    renderSummaryCard('Категорий', state.knowledge.categories.length, 'Структура базы знаний'),
    renderSummaryCard('Статей', state.knowledge.articles.length, 'Текущий результат поиска'),
    renderSummaryCard('RU', ruCount, 'Русские статьи в текущем срезе'),
    renderSummaryCard('KZ', kzCount, 'Казахские статьи в текущем срезе'),
  ].join('');
}

function syncKnowledgeCategoryOptions(preferredCategoryId = '') {
  const select = $('kbArticleCategory');
  const currentValue = preferredCategoryId || select.value;
  if (!state.knowledge.categories.length) {
    select.innerHTML = '<option value="">Сначала создайте категорию</option>';
    select.value = '';
    return;
  }
  select.innerHTML = state.knowledge.categories
    .map((item) => `<option value="${escapeHtml(item.category_id)}">${escapeHtml(item.name)}</option>`)
    .join('');
  const nextValue = state.knowledge.categories.some((item) => item.category_id === currentValue)
    ? currentValue
    : state.knowledge.categories[0].category_id;
  select.value = nextValue;
}

function renderKnowledgeCategories() {
  if (!state.knowledge.categories.length) {
    $('kbCategoriesList').innerHTML = '<li class="empty-state">Категории базы знаний пока не созданы.</li>';
    return;
  }
  $('kbCategoriesList').innerHTML = state.knowledge.categories
    .map(
      (item) => `
        <li>
          <strong>${escapeHtml(item.name)}</strong><br/>
          ID: ${escapeHtml(item.category_id)}<br/>
          описание: ${escapeHtml(item.description || '—')}
        </li>
      `,
    )
    .join('');
}

function renderKnowledgeArticlePreview(article = null) {
  $('kbArticlePreview').textContent = article ? JSON.stringify(article, null, 2) : 'Выберите статью из списка ниже.';
}

function resetKnowledgeArticleForm() {
  state.knowledge.selectedArticleId = '';
  $('kbArticleGroupId').value = '';
  $('kbArticleLanguage').value = 'ru';
  $('kbArticleTitle').value = '';
  $('kbArticleTags').value = '';
  $('kbArticleBody').value = '';
  $('saveKbArticleBtn').textContent = 'Сохранить статью';
  $('kbFormHint').textContent = 'Создайте новую статью или выберите существующую для редактирования.';
  syncKnowledgeCategoryOptions();
  renderKnowledgeArticlePreview(null);
}

function fillKnowledgeArticleForm(article) {
  if (!article) {
    resetKnowledgeArticleForm();
    return;
  }
  state.knowledge.selectedArticleId = article.article_id;
  $('kbArticleGroupId').value = article.article_group_id || '';
  $('kbArticleLanguage').value = article.language || 'ru';
  $('kbArticleTitle').value = article.title || '';
  $('kbArticleTags').value = Array.isArray(article.tags) ? article.tags.join(', ') : '';
  $('kbArticleBody').value = article.body || '';
  syncKnowledgeCategoryOptions(article.category_id || '');
  $('saveKbArticleBtn').textContent = 'Обновить статью';
  $('kbFormHint').textContent = `Редактирование статьи: ${article.title || article.article_id}`;
  renderKnowledgeArticlePreview(article);
}

function renderKnowledgeArticles() {
  renderKnowledgeSummary();
  if (!state.knowledge.articles.length) {
    $('kbArticlesList').innerHTML = '<li class="empty-state">Статьи базы знаний не найдены.</li>';
    return;
  }
  $('kbArticlesList').innerHTML = state.knowledge.articles
    .map((item) => {
      const tags = Array.isArray(item.tags) && item.tags.length ? item.tags.join(', ') : '—';
      return `
        <li>
          <strong>${escapeHtml(item.title)}</strong><br/>
          категория: ${escapeHtml(kbCategoryName(item.category_id))}<br/>
          язык: ${escapeHtml(kbLanguageLabel(item.language))}<br/>
          ID группы: ${escapeHtml(item.article_group_id || item.article_id)}<br/>
          теги: ${escapeHtml(tags)}<br/>
          <button class="btn ghost" type="button" data-kb-article-id="${escapeHtml(item.article_id)}">Открыть</button>
        </li>
      `;
    })
    .join('');
}

function knowledgeArticlePayloadFromForm() {
  return {
    category_id: $('kbArticleCategory').value.trim(),
    article_group_id: $('kbArticleGroupId').value.trim() || null,
    language: $('kbArticleLanguage').value.trim() || 'ru',
    title: $('kbArticleTitle').value.trim(),
    body: $('kbArticleBody').value.trim(),
    tags: $('kbArticleTags').value
      .split(',')
      .map((item) => item.trim())
      .filter(Boolean),
  };
}

async function loadKnowledgeCategories(silent = false) {
  const data = await api('kb', 'knowledge/categories');
  state.knowledge.categories = Array.isArray(data) ? data : [];
  syncKnowledgeCategoryOptions();
  renderKnowledgeCategories();
  renderKnowledgeSummary();
  if (!silent) {
    log('Категории базы знаний обновлены', { count: state.knowledge.categories.length });
  }
}

async function loadKnowledgeArticles(silent = false) {
  const query = $('kbSearchQuery').value.trim();
  const params = new URLSearchParams({ q: query, limit: '100' });
  const data = await api('kb', `knowledge/search?${params.toString()}`);
  state.knowledge.articles = Array.isArray(data) ? data : [];
  state.knowledge.lastQuery = query;
  renderKnowledgeArticles();
  if (state.knowledge.selectedArticleId) {
    const selected = state.knowledge.articles.find((item) => item.article_id === state.knowledge.selectedArticleId);
    if (selected) {
      renderKnowledgeArticlePreview(selected);
    }
  }
  if (!silent) {
    log('Статьи базы знаний обновлены', { count: state.knowledge.articles.length, query });
  }
}

async function refreshKnowledgeBase(silent = false) {
  try {
    await loadKnowledgeCategories(true);
    await loadKnowledgeArticles(true);
    if (!silent) {
      log('База знаний обновлена', {
        categories: state.knowledge.categories.length,
        articles: state.knowledge.articles.length,
      });
    }
  } catch (err) {
    $('kbCategoriesList').innerHTML = '<li class="empty-state">Не удалось загрузить категории базы знаний.</li>';
    $('kbArticlesList').innerHTML = '<li class="empty-state">Не удалось загрузить статьи базы знаний.</li>';
    $('kbArticlePreview').textContent = `Ошибка базы знаний: ${err.message}`;
    if (!silent) {
      log('Не удалось обновить базу знаний', { error: err.message });
    }
  }
}

async function createKnowledgeCategory() {
  const name = $('kbCategoryName').value.trim();
  const description = $('kbCategoryDescription').value.trim();
  if (!name) {
    $('kbFormHint').textContent = 'Укажите название категории перед сохранением.';
    return;
  }
  try {
    const created = await api('kb', 'knowledge/categories', {
      method: 'POST',
      body: JSON.stringify({ name, description }),
    });
    $('kbCategoryName').value = '';
    $('kbCategoryDescription').value = '';
    await loadKnowledgeCategories(true);
    syncKnowledgeCategoryOptions(created.category_id);
    renderKnowledgeCategories();
    renderKnowledgeSummary();
    $('kbFormHint').textContent = `Категория создана: ${created.name}`;
    log('Категория базы знаний создана', { category_id: created.category_id, name: created.name });
  } catch (err) {
    $('kbFormHint').textContent = `Не удалось создать категорию: ${err.message}`;
    log('Не удалось создать категорию базы знаний', { error: err.message });
  }
}

async function saveKnowledgeArticle() {
  const payload = knowledgeArticlePayloadFromForm();
  if (!payload.category_id) {
    $('kbFormHint').textContent = 'Сначала создайте или выберите категорию.';
    return;
  }
  if (!payload.title || !payload.body) {
    $('kbFormHint').textContent = 'Заполните заголовок и текст статьи.';
    return;
  }
  try {
    const articleId = state.knowledge.selectedArticleId;
    const data = articleId
      ? await api('kb', `knowledge/articles/${encodeURIComponent(articleId)}`, {
          method: 'PATCH',
          body: JSON.stringify({
            article_group_id: payload.article_group_id,
            language: payload.language,
            title: payload.title,
            body: payload.body,
            tags: payload.tags,
          }),
        })
      : await api('kb', 'knowledge/articles', {
          method: 'POST',
          body: JSON.stringify(payload),
        });
    await loadKnowledgeArticles(true);
    fillKnowledgeArticleForm(data);
    $('kbFormHint').textContent = articleId ? `Статья обновлена: ${data.title}` : `Статья создана: ${data.title}`;
    log(articleId ? 'Статья базы знаний обновлена' : 'Статья базы знаний создана', { article_id: data.article_id, title: data.title });
  } catch (err) {
    $('kbFormHint').textContent = `Не удалось сохранить статью: ${err.message}`;
    log('Не удалось сохранить статью базы знаний', { error: err.message });
  }
}

function openKnowledgeArticleFromList(event) {
  const button = event.target.closest('[data-kb-article-id]');
  if (!button) {
    return;
  }
  const articleId = button.dataset.kbArticleId || '';
  const article = state.knowledge.articles.find((item) => item.article_id === articleId);
  if (!article) {
    return;
  }
  fillKnowledgeArticleForm(article);
}

async function loadLiveCalls(silent = false) {
  try {
    const data = await api('supervisor', 'supervisor/live-calls?limit=20');
    const items = Array.isArray(data?.items) ? data.items : [];
    renderLiveCalls(items);
    if (!silent) {
      log('Лента звонков обновлена', { count: items.length });
    }
  } catch (err) {
    $('liveCallsList').innerHTML = '<li class="empty-state">Не удалось загрузить звонки.</li>';
    if (!silent) {
      log('Не удалось обновить ленту звонков', { error: err.message });
    }
  }
}

async function loadRealtime() {
  try {
    const data = await api('supervisor', 'supervisor/realtime');
    renderRealtime(data);
      log('Оперативный срез обновлён', { agents: data?.agents?.total || 0 });
  } catch (err) {
    $('realtimeOutput').textContent = err.message;
    log('Не удалось обновить оперативный срез', { error: err.message });
  }
}

async function loadKpi() {
  try {
    const queueId = encodeURIComponent($('queueMetricId').value.trim() || 'line2');
    const data = await api('reporting', `reports/kpi?queue_id=${queueId}`);
    renderKpi(data);
    log('KPI обновлены', data?.kpi || {});
  } catch (err) {
    $('kpiOutput').textContent = err.message;
    log('Не удалось обновить KPI', { error: err.message });
  }
}

async function loadAgents() {
  try {
    const data = await api('supervisor', 'supervisor/agents');
    if (!data.length) {
      $('agentsList').innerHTML = '<li class="empty-state">Агенты пока не зафиксированы.</li>';
      return;
    }
    $('agentsList').innerHTML = data
        .map((item) => `<li><strong>${item.agent_id}</strong><br/>статус: ${item.state}<br/>очередь: ${item.queue_id || 'не указана'}</li>`)
      .join('');
    log('Список агентов обновлён', { count: data.length });
  } catch (err) {
    $('agentsList').innerHTML = '<li class="empty-state">Не удалось загрузить агентов.</li>';
    log('Не удалось загрузить агентов', { error: err.message });
  }
}

async function setAgentState() {
  try {
    const data = await api('supervisor', 'supervisor/agent-states', {
      method: 'POST',
      body: JSON.stringify({
        agent_id: $('agentId').value.trim() || 'supervisor_demo_agent',
        state: $('agentState').value,
        queue_id: $('agentQueueId').value.trim() || null,
      }),
    });
    log('Статус агента обновлён', { agent_id: data.agent_id, state: data.state });
    await Promise.all([loadAgents(), loadRealtime()]);
  } catch (err) {
    log('Не удалось обновить статус агента', { error: err.message });
  }
}

const AGENT_STATUS_LABELS = {
  AVAILABLE: 'Свободен',
  RESERVED: 'Зарезервирован',
  RINGING: 'Звонок идёт',
  TALKING: 'На линии',
  AFTER_CALL_WORK: 'Постобработка',
  PAUSED: 'Пауза',
  OFFLINE: 'Offline',
};

const AGENT_STATUS_TONE = {
  AVAILABLE: 'is-success',
  RESERVED: 'is-warning',
  RINGING: 'is-warning',
  TALKING: 'is-success',
  AFTER_CALL_WORK: 'is-warning',
  PAUSED: 'is-muted',
  OFFLINE: 'is-danger',
};

const ESCALATION_STATUS_LABELS = {
  requested: 'Запрошена',
  ringing: 'Дозвон оператора',
  completed: 'Завершена',
  failed: 'Не удалась',
};

const ESCALATION_STATUS_TONE = {
  requested: 'is-warning',
  ringing: 'is-warning',
  completed: 'is-success',
  failed: 'is-danger',
};

function statusBadge(labels, tones, value) {
  const label = labels[value] || value || 'неизвестно';
  const tone = tones[value] || 'is-muted';
  return `<span class="status-badge ${tone}">${escapeHtml(label)}</span>`;
}

function renderCallAgents(items) {
  const byStatus = {};
  items.forEach((item) => {
    byStatus[item.status] = (byStatus[item.status] || 0) + 1;
  });
  $('callAgentsSummary').innerHTML = [
    renderSummaryCard('Всего в пуле', items.length, 'Операторы L2/L3'),
    renderSummaryCard('Свободны', byStatus.AVAILABLE || 0, 'Могут принять звонок сейчас'),
    renderSummaryCard('На линии', (byStatus.TALKING || 0) + (byStatus.RINGING || 0) + (byStatus.RESERVED || 0), 'Заняты звонком или дозвоном'),
    renderSummaryCard('Offline', byStatus.OFFLINE || 0, 'Недоступны'),
  ].join('');

  if (!items.length) {
    $('callAgentsList').innerHTML = '<li class="empty-state">Пул пуст. Добавьте оператора ниже.</li>';
    return;
  }

  $('callAgentsList').innerHTML = items
    .map(
      (item) => `
        <li class="agent-pool-item">
          <strong>${escapeHtml(item.display_name)} · вн. ${escapeHtml(item.extension)}</strong>
          ${statusBadge(AGENT_STATUS_LABELS, AGENT_STATUS_TONE, item.status)}
          <span class="status-badge is-muted">${escapeHtml(item.level)}</span>
          ${item.enabled ? '' : '<span class="status-badge is-danger">отключён</span>'}
          <span class="agent-pool-meta">tenant: ${escapeHtml((item.tenant_ids || []).join(', ') || '-')}</span>
          <span class="agent-pool-meta">навыки: ${escapeHtml((item.skills || []).join(', ') || '-')}</span>
          <span class="agent-pool-meta">принято звонков: ${Number(item.calls_handled_count || 0)}</span>
          ${item.current_call_id ? `<span class="agent-pool-meta">текущий звонок: ${escapeHtml(item.current_call_id)}</span>` : ''}
        </li>
      `,
    )
    .join('');
}

async function loadCallAgents(silent = false) {
  try {
    const data = await api('routing', 'agents');
    renderCallAgents(Array.isArray(data) ? data : []);
    if (!silent) {
      log('Пул L2-операторов обновлён', { count: data.length });
    }
  } catch (err) {
    $('callAgentsList').innerHTML = '<li class="empty-state">Не удалось загрузить пул операторов.</li>';
    if (!silent) {
      log('Не удалось загрузить пул операторов', { error: err.message });
    }
  }
}

async function createCallAgent() {
  try {
    const extension = $('newAgentExtension').value.trim();
    if (!extension) {
      log('Укажите внутренний номер оператора');
      return;
    }
    const skills = $('newAgentSkills').value
      .split(',')
      .map((item) => item.trim())
      .filter(Boolean);
    const tenantId = $('newAgentTenant').value.trim();
    const data = await api('routing', 'agents', {
      method: 'POST',
      body: JSON.stringify({
        extension,
        display_name: $('newAgentDisplayName').value.trim() || `Оператор ${extension}`,
        level: $('newAgentLevel').value,
        tenant_ids: tenantId ? [tenantId] : [],
        skills,
        max_concurrent_calls: 1,
        enabled: true,
      }),
    });
    log('Оператор добавлен в пул', { agent_id: data.agent_id, extension: data.extension });
    $('newAgentExtension').value = '';
    $('newAgentDisplayName').value = '';
    $('newAgentSkills').value = '';
    await loadCallAgents();
  } catch (err) {
    log('Не удалось добавить оператора', { error: err.message });
  }
}

function renderEscalations(items) {
  const byStatus = {};
  items.forEach((item) => {
    byStatus[item.status] = (byStatus[item.status] || 0) + 1;
  });
  $('escalationsSummary').innerHTML = [
    renderSummaryCard('Активных', (byStatus.requested || 0) + (byStatus.ringing || 0), 'Ждут/дозваниваются до оператора'),
    renderSummaryCard('Завершено', byStatus.completed || 0, 'Успешно приняты оператором'),
    renderSummaryCard('Не удалось', byStatus.failed || 0, 'Свободного оператора не нашлось'),
  ].join('');

  if (!items.length) {
    $('escalationsList').innerHTML = '<li class="empty-state">Эскалаций пока не было.</li>';
    return;
  }

  $('escalationsList').innerHTML = items
    .map(
      (item) => `
        <li class="escalation-item">
          <strong>звонок ${escapeHtml(item.call_id)}</strong>
          ${statusBadge(ESCALATION_STATUS_LABELS, ESCALATION_STATUS_TONE, item.status)}
          <span class="status-badge is-muted">${escapeHtml(item.from_level)} → ${escapeHtml(item.to_level)}</span>
          <span class="escalation-meta">причина: ${escapeHtml(item.reason_code || '-')}</span>
          <span class="escalation-meta">оператор: ${escapeHtml(item.assigned_agent_id || 'не назначен')}</span>
          <span class="escalation-meta">запрошена: ${formatTime(item.requested_at)}</span>
          ${item.summary ? `<span class="escalation-meta">тема: ${escapeHtml(item.summary)}</span>` : ''}
        </li>
      `,
    )
    .join('');
}

async function loadEscalations(silent = false) {
  try {
    const data = await api('routing', 'escalations?limit=30');
    renderEscalations(Array.isArray(data) ? data : []);
    if (!silent) {
      log('Лента эскалаций обновлена', { count: data.length });
    }
  } catch (err) {
    $('escalationsList').innerHTML = '<li class="empty-state">Не удалось загрузить эскалации.</li>';
    if (!silent) {
      log('Не удалось загрузить эскалации', { error: err.message });
    }
  }
}

async function saveQueueMetrics() {
  try {
    const queueId = encodeURIComponent($('queueMetricId').value.trim() || 'line2');
    const inQueue = Number($('queueInQueue').value || 0);
    const avgWait = Number($('queueAvgWait').value || 0);
    const data = await api(
      'supervisor',
      `supervisor/queue-metrics?queue_id=${queueId}&in_queue=${inQueue}&avg_wait_seconds=${avgWait}`,
      { method: 'POST' },
    );
    $('queueMetricsOutput').textContent = JSON.stringify(data, null, 2);
    log('Показатели очереди обновлены', { queue_id: data.queue_id, in_queue: data.in_queue });
    await Promise.all([loadRealtime(), loadKpi()]);
  } catch (err) {
    $('queueMetricsOutput').textContent = err.message;
    log('Не удалось обновить очередь', { error: err.message });
  }
}

function setRecordingPreviewEmpty(message) {
  $('recordingMetaOutput').textContent = message || '';
  $('recordingSelectionHint').textContent = message || 'Выберите запись для предпросмотра.';
  $('recordingPlayer').removeAttribute('src');
  $('recordingPlayer').load();
  $('downloadRecordingBtn').disabled = true;
  state.selectedRecordingId = null;
  if (state.recordingPreviewUrl) {
    URL.revokeObjectURL(state.recordingPreviewUrl);
    state.recordingPreviewUrl = null;
  }
}

function renderRecordingsList(data) {
  if (!data.length) {
    $('recordingsList').innerHTML = '<li class="empty-state">Записи не найдены.</li>';
    setRecordingPreviewEmpty('По текущим фильтрам записи не найдены.');
    return;
  }

  $('recordingsList').innerHTML = data
    .map(
      (item) => `
        <li>
          <strong>${item.recording_id}</strong><br/>
          звонок: ${item.call_id}<br/>
          файл: ${item.file_name}<br/>
          статус: ${item.status}
          <div class="actions compact-actions">
            <button type="button" class="btn ghost recording-open-btn" data-recording-id="${item.recording_id}">Открыть</button>
          </div>
        </li>
      `,
    )
    .join('');

  document.querySelectorAll('.recording-open-btn').forEach((button) => {
    button.addEventListener('click', () => {
      void openRecording(button.dataset.recordingId || '');
    });
  });
}

function renderRecordingMeta(data) {
  $('recordingMetaOutput').textContent = JSON.stringify(data, null, 2);
  $('recordingSelectionHint').textContent = `Выбрано: ${data.recording_id} | статус: ${data.status}`;
  $('downloadRecordingBtn').disabled = false;
}

async function loadRecordings() {
  try {
    const params = new URLSearchParams();
    const callId = $('recordingCallId').value.trim();
    const interactionId = $('recordingInteractionId').value.trim();
    const status = $('recordingStatus').value;
    if (callId) {
      params.set('call_id', callId);
    }
    if (interactionId) {
      params.set('interaction_id', interactionId);
    }
    if (status) {
      params.set('status', status);
    }
    params.set('limit', '50');

    const suffix = params.toString() ? `?${params.toString()}` : '';
    const data = await api('recording', `recordings${suffix}`);
    renderRecordingsList(data);
      log('Список записей обновлён', { count: data.length });
  } catch (err) {
    $('recordingsList').innerHTML = '<li class="empty-state">Не удалось загрузить записи.</li>';
    setRecordingPreviewEmpty(`Ошибка: ${err.message}`);
    log('Не удалось загрузить записи', { error: err.message });
  }
}

async function loadRecordingPreview(recordingId) {
  const response = await fetchBinary('recording', `recordings/${encodeURIComponent(recordingId)}/content?disposition=inline`);
  const blob = await response.blob();
  if (state.recordingPreviewUrl) {
    URL.revokeObjectURL(state.recordingPreviewUrl);
  }
  state.recordingPreviewUrl = URL.createObjectURL(blob);
  $('recordingPlayer').src = state.recordingPreviewUrl;
  $('recordingPlayer').load();
}

async function openRecording(recordingId) {
  if (!recordingId) {
    return;
  }
  try {
    const data = await api('recording', `recordings/${encodeURIComponent(recordingId)}`);
    state.selectedRecordingId = recordingId;
    renderRecordingMeta(data);
    await loadRecordingPreview(recordingId);
    log('Запись открыта', { recording_id: recordingId });
  } catch (err) {
    setRecordingPreviewEmpty(`Не удалось открыть запись: ${err.message}`);
    log('Не удалось открыть запись', { error: err.message });
  }
}

async function resolveRecordingEvent(rawValue) {
  const value = (rawValue || '').trim();
  if (!value) {
    return null;
  }

  const events = await api('voice', 'integrations/voice/events?limit=300');
  if (value.startsWith('vev_')) {
    const direct = events.find((item) => item.event_id === value) || null;
    return direct ? { eventId: value, event: direct } : { eventId: value, event: null };
  }

  const recordingEvents = events.filter((item) => item.event_type === 'recording.ready');
  for (let i = recordingEvents.length - 1; i >= 0; i -= 1) {
    const item = recordingEvents[i];
    if (item.interaction_id === value || item.call_id === value) {
      return { eventId: item.event_id, event: item };
    }
  }
  return null;
}

async function resolveVoiceContext(rawValue) {
  const value = (rawValue || '').trim();
  if (!value) {
    return null;
  }
  const events = await api('voice', 'integrations/voice/events?limit=300');
  const matches = events.filter(
    (item) => item.event_id === value || item.interaction_id === value || item.call_id === value,
  );
  if (!matches.length) {
    return null;
  }
  return matches[matches.length - 1];
}

async function importRecording() {
  const rawInput = $('recordingEventId').value.trim();
  if (!rawInput) {
    setRecordingPreviewEmpty('Укажите ID voice_event, обращения или звонка перед импортом.');
    return;
  }
  try {
    const resolved = await resolveRecordingEvent(rawInput);
    if (!resolved?.eventId) {
      const ctx = await resolveVoiceContext(rawInput);
      const callId = ctx?.call_id || '';
      const interactionId = ctx?.interaction_id || '';
      if (callId) {
        $('recordingCallId').value = callId;
      }
      if (interactionId) {
        $('recordingInteractionId').value = interactionId;
      }

      if (callId) {
        const existing = await api('recording', `recordings?call_id=${encodeURIComponent(callId)}&limit=10`);
        if (Array.isArray(existing) && existing.length) {
          await loadRecordings();
          await openRecording(existing[0].recording_id);
          log('Запись уже импортирована автоматически', { call_id: callId, recording_id: existing[0].recording_id });
          return;
        }
      }

      const reason = ctx?.event_type === 'call.started'
        ? 'Звонок ещё активен или только что завершился. Подождите 5-15 секунд до события recording.ready.'
        : `Событие recording.ready для "${rawInput}" не найдено.`;
      setRecordingPreviewEmpty(reason);
      log('Импорт записи не выполнен', { error: reason, input: rawInput });
      return;
    }
    const { eventId, event } = resolved;
    if (eventId !== rawInput) {
      $('recordingEventId').value = eventId;
      log('Событие записи найдено автоматически', { input: rawInput, event_id: eventId });
    }

    if (event?.payload?.source === 'asterisk' && !event?.payload?.source_path) {
      $('recordingCallId').value = event.call_id || '';
      $('recordingInteractionId').value = event.interaction_id || '';
      await loadRecordings();
      setRecordingPreviewEmpty(
        'Записи Asterisk импортируются автоматически. Используйте список ниже и откройте последнюю запись.',
      );
      log('Ручной импорт пропущен для события Asterisk', { event_id: eventId, call_id: event.call_id });
      return;
    }

    const data = await api('recording', `recordings/import-from-voice-event/${encodeURIComponent(eventId)}`, {
      method: 'POST',
    });
    $('recordingCallId').value = data.call_id;
    $('recordingInteractionId').value = data.interaction_id || '';
    await loadRecordings();
    await openRecording(data.recording_id);
    log('Запись импортирована', { recording_id: data.recording_id, event_id: eventId });
  } catch (err) {
    setRecordingPreviewEmpty(`Импорт не выполнен: ${err.message}`);
    log('Импорт записи не выполнен', { error: err.message });
  }
}

async function archiveRecording() {
  if (!state.selectedRecordingId) {
    setRecordingPreviewEmpty('Сначала откройте запись.');
    return;
  }
  try {
    const data = await api('recording', `recordings/${encodeURIComponent(state.selectedRecordingId)}/archive`, {
      method: 'POST',
    });
    renderRecordingMeta(data);
    await loadRecordings();
    log('Запись отправлена в архив', { recording_id: data.recording_id });
  } catch (err) {
    log('Не удалось отправить запись в архив', { error: err.message });
  }
}

async function downloadRecording() {
  if (!state.selectedRecordingId) {
    setRecordingPreviewEmpty('Сначала откройте запись.');
    return;
  }
  try {
    const response = await fetchBinary(
      'recording',
      `recordings/${encodeURIComponent(state.selectedRecordingId)}/content?disposition=attachment`,
    );
    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    const contentDisposition = response.headers.get('Content-Disposition') || '';
    const fileNameMatch = /filename=\"?([^\";]+)\"?/i.exec(contentDisposition);
    link.href = url;
    link.download = fileNameMatch?.[1] || `${state.selectedRecordingId}.wav`;
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
    log('Запись скачана', { recording_id: state.selectedRecordingId });
  } catch (err) {
    log('Не удалось скачать запись', { error: err.message });
  }
}

function wire() {
  $('loginBtn').addEventListener('click', login);
  $('corporateLoginBtn').addEventListener('click', startCorporateLogin);
  $('refreshBtn').addEventListener('click', async () => {
    await Promise.all([
      loadRealtime(),
      loadKpi(),
      loadLiveCalls(),
      loadAgents(),
      loadCallAgents(),
      loadEscalations(),
      loadRecordings(),
      refreshKnowledgeBase(true),
    ]);
    log('Данные супервизора обновлены');
  });
  $('loadRealtimeBtn').addEventListener('click', loadRealtime);
  $('loadKpiBtn').addEventListener('click', loadKpi);
  $('loadLiveCallsBtn').addEventListener('click', () => loadLiveCalls());
  $('loadAgentsBtn').addEventListener('click', loadAgents);
  $('setAgentBtn').addEventListener('click', setAgentState);
  $('loadCallAgentsBtn').addEventListener('click', () => loadCallAgents());
  $('createCallAgentBtn').addEventListener('click', createCallAgent);
  $('loadEscalationsBtn').addEventListener('click', () => loadEscalations());
  $('saveQueueMetricsBtn').addEventListener('click', saveQueueMetrics);
  $('createKbCategoryBtn').addEventListener('click', createKnowledgeCategory);
  $('loadKbBtn').addEventListener('click', () => refreshKnowledgeBase());
  $('searchKbBtn').addEventListener('click', () => loadKnowledgeArticles());
  $('saveKbArticleBtn').addEventListener('click', saveKnowledgeArticle);
  $('resetKbArticleBtn').addEventListener('click', resetKnowledgeArticleForm);
  $('kbArticlesList').addEventListener('click', openKnowledgeArticleFromList);
  $('importRecordingBtn').addEventListener('click', importRecording);
  $('loadRecordingsBtn').addEventListener('click', loadRecordings);
  $('archiveRecordingBtn').addEventListener('click', archiveRecording);
  $('downloadRecordingBtn').addEventListener('click', downloadRecording);
  $('logoutBtn').addEventListener('click', logout);
  window.addEventListener('message', handleOidcMessage);
}

async function init() {
  if (!restoreStoredSession()) {
    window.location.href = '/';
    return;
  }
  if (!ensurePageAccess()) {
    return;
  }
  wire();
  updateSessionInfo();
  setRecordingPreviewEmpty('');
  resetKnowledgeArticleForm();
  await Promise.all([
    checkGateway(),
    loadOidcConfig(),
    loadRealtime(),
    loadKpi(),
    loadLiveCalls(true),
    loadAgents(),
    loadCallAgents(true),
    loadEscalations(true),
    loadRecordings(),
    refreshKnowledgeBase(true),
  ]);
  window.setInterval(() => {
    void loadLiveCalls(true);
    void loadCallAgents(true);
    void loadEscalations(true);
  }, 5000);
  log('Пульт супервизора готов');
}

init();
