const state = {
  user: 'admin',
  role: 'admin',
  token: null,
  authSource: 'local',
  fullName: null,
  logLines: [],
  oidc: {
    enabled: false,
    loginPath: '/auth/oidc/start?return_mode=popup',
    providerLabel: 'Keycloak',
  },
  voiceNameConfig: null,
  voiceTtsConfig: null,
};

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() {
  if (state.role !== 'admin') {
    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() {
  clearStoredSession();
  window.location.href = '/';
}

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

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, 16);
  $('eventLog').textContent = state.logLines.join('\n');
}

function updateProfileMeta() {
  const profileName = $('profileName');
  const profileRole = $('profileRole');
  if (!profileName || !profileRole) {
    return;
  }
  profileName.textContent = state.fullName || state.user || 'Экран администратора';
  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 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('Открыт корпоративный вход', { провайдер: 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 queueRulePayload(channelId, slaSeconds) {
  return [
    {
      channel: channelId,
      priority: 3,
      strategy: 'round_robin',
      sla_seconds: Number(slaSeconds || 30),
    },
  ];
}

function defaultIvrFlowDocument() {
  return {
    nodes: [
      {
        node_id: 'root',
        prompt_text: 'Здравствуйте! Добро пожаловать в контакт-центр. Для русского языка нажмите цифру два. Саламатсыз ба! Қазақ тілін таңдау үшін бір цифрын теріңіз.',
        prompt_sequence: [
          {
            prompt_audio_key: 'ivr/demo-language-ru',
            prompt_text:
              '\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435! \u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0430\u043a\u0442-\u0446\u0435\u043d\u0442\u0440. \u0414\u043b\u044f \u0440\u0443\u0441\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u0446\u0438\u0444\u0440\u0443 \u0434\u0432\u0430.',
            language: 'ru',
          },
          {
            prompt_audio_key: 'ivr/demo-language-kz',
            prompt_text:
              '\u0421\u0430\u043b\u0430\u043c\u0430\u0442\u0441\u044b\u0437 \u0431\u0430! \u049a\u0430\u0437\u0430\u049b \u0442\u0456\u043b\u0456\u043d \u0442\u0430\u04a3\u0434\u0430\u0443 \u04af\u0448\u0456\u043d \u0431\u0456\u0440 \u0446\u0438\u0444\u0440\u044b\u043d \u0442\u0435\u0440\u0456\u04a3\u0456\u0437.',
            language: 'kz',
          },
        ],
        is_terminal: false,
        invalid_target_node_id: 'root',
        no_input_target_node_id: 'root',
        options: [
          { digit: '1', target_node_id: 'voice_start_kz' },
          { digit: '2', target_node_id: 'voice_start_ru' },
        ],
      },
      {
        node_id: 'menu_ru',
        prompt_text: 'Для отдела продаж нажмите 1. Для службы поддержки нажмите 2.',
        prompt_audio_key: 'ivr/demo-menu-ru',
        is_terminal: false,
        invalid_target_node_id: 'menu_ru',
        no_input_target_node_id: 'menu_ru',
        options: [
          { digit: '1', target_node_id: 'sales_ru' },
          { digit: '2', target_node_id: 'support_ru' },
        ],
      },
      {
        node_id: 'menu_kz',
        prompt_text: 'Сату бөлімі үшін 1 басыңыз. Қолдау қызметі үшін 2 басыңыз.',
        prompt_audio_key: 'ivr/demo-menu-kz',
        is_terminal: false,
        invalid_target_node_id: 'menu_kz',
        no_input_target_node_id: 'menu_kz',
        options: [
          { digit: '1', target_node_id: 'sales_kz' },
          { digit: '2', target_node_id: 'support_kz' },
        ],
      },
      {
        node_id: 'sales_ru',
        prompt_text: 'Переводим в отдел продаж.',
        prompt_audio_key: 'ivr/demo-sales-ru',
        is_terminal: true,
        outcome_code: 'sales_route_ru',
        resolved_queue_id: 'sales_line',
        resolved_queue_code: 'ivr_sales_ai_ru',
        options: [],
      },
      {
        node_id: 'support_ru',
        prompt_text: 'Переводим в службу поддержки.',
        prompt_audio_key: 'ivr/demo-support-ru',
        is_terminal: true,
        outcome_code: 'support_route_ru',
        resolved_queue_id: 'support_line',
        resolved_queue_code: 'ivr_support_ai_ru',
        options: [],
      },
      {
        node_id: 'sales_kz',
        prompt_text: 'Сату бөліміне қосып жатырмыз.',
        prompt_audio_key: 'ivr/demo-sales-kz',
        is_terminal: true,
        outcome_code: 'sales_route_kz',
        resolved_queue_id: 'sales_line',
        resolved_queue_code: 'ivr_sales_ai_kz',
        options: [],
      },
      {
        node_id: 'support_kz',
        prompt_text: 'Қолдау қызметіне қосып жатырмыз.',
        prompt_audio_key: 'ivr/demo-support-kz',
        is_terminal: true,
        outcome_code: 'support_route_kz',
        resolved_queue_id: 'support_line',
        resolved_queue_code: 'ivr_support_ai_kz',
        options: [],
      },
    ],
  };
}

function defaultVoiceStartIvrFlowDocument() {
  return {
    nodes: [
      {
        node_id: 'root',
        prompt_text:
          '\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435! \u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0430\u043a\u0442-\u0446\u0435\u043d\u0442\u0440. \u0414\u043b\u044f \u0440\u0443\u0441\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u0446\u0438\u0444\u0440\u0443 \u0434\u0432\u0430. \u0421\u0430\u043b\u0430\u043c\u0430\u0442\u0441\u044b\u0437 \u0431\u0430! \u049a\u0430\u0437\u0430\u049b \u0442\u0456\u043b\u0456\u043d \u0442\u0430\u04a3\u0434\u0430\u0443 \u04af\u0448\u0456\u043d \u0431\u0456\u0440 \u0446\u0438\u0444\u0440\u044b\u043d \u0442\u0435\u0440\u0456\u04a3\u0456\u0437.',
        prompt_sequence: [
          {
            prompt_audio_key: 'ivr/demo-language-ru',
            prompt_text:
              '\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435! \u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0430\u043a\u0442-\u0446\u0435\u043d\u0442\u0440. \u0414\u043b\u044f \u0440\u0443\u0441\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u0446\u0438\u0444\u0440\u0443 \u0434\u0432\u0430.',
            language: 'ru',
          },
          {
            prompt_audio_key: 'ivr/demo-language-kz',
            prompt_text:
              '\u0421\u0430\u043b\u0430\u043c\u0430\u0442\u0441\u044b\u0437 \u0431\u0430! \u049a\u0430\u0437\u0430\u049b \u0442\u0456\u043b\u0456\u043d \u0442\u0430\u04a3\u0434\u0430\u0443 \u04af\u0448\u0456\u043d \u0431\u0456\u0440 \u0446\u0438\u0444\u0440\u044b\u043d \u0442\u0435\u0440\u0456\u04a3\u0456\u0437.',
            language: 'kz',
          },
        ],
        is_terminal: false,
        invalid_target_node_id: 'root',
        no_input_target_node_id: 'root',
        options: [
          { digit: '1', target_node_id: 'voice_start_kz' },
          { digit: '2', target_node_id: 'voice_start_ru' },
        ],
      },
      {
        node_id: 'voice_start_kz',
        prompt_text: '\u049a\u043e\u04a3\u044b\u0440\u0430\u0443\u0434\u044b \u049b\u0430\u0437\u0430\u049b \u0442\u0456\u043b\u0456\u043d\u0434\u0435\u0433\u0456 \u0431\u0430\u0441\u0442\u0430\u043f\u049b\u044b voice-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439\u0433\u0435 \u04e9\u0442\u043a\u0456\u0437\u0456\u043f \u0436\u0430\u0442\u044b\u0440\u043c\u044b\u0437.',
        is_terminal: true,
        outcome_code: 'voice_start_kz',
        resolved_queue_id: 'voice_start_kz_line',
        resolved_queue_code: 'voice_start_kz',
        options: [],
      },
      {
        node_id: 'voice_start_ru',
        prompt_text: '\u041f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0437\u0432\u043e\u043d\u043e\u043a \u0432 \u0440\u0443\u0441\u0441\u043a\u043e\u044f\u0437\u044b\u0447\u043d\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 voice-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439.',
        is_terminal: true,
        outcome_code: 'voice_start_ru',
        resolved_queue_id: 'voice_start_ru_line',
        resolved_queue_code: 'voice_start_ru',
        options: [],
      },
    ],
  };
}

function ensureDefaultIvrFlowJson() {
  if (!$('ivrFlowJson').value.trim()) {
    $('ivrFlowJson').value = JSON.stringify(defaultVoiceStartIvrFlowDocument(), null, 2);
  }
}

function renderUsers(items) {
  if (!items.length) {
    $('usersList').innerHTML = '<li class="empty-state">Пользователи пока не найдены.</li>';
    return;
  }
  $('usersList').innerHTML = items
    .map((item) => `<li><strong>${item.username}</strong><br/>${item.full_name}<br/>роль: ${item.role}<br/>ID: ${item.user_id}</li>`)
    .join('');
}

function syncUserEditFields(user) {
  if (!user) {
    return;
  }
  $('userLookupId').value = user.user_id || $('userLookupId').value;
  $('editUserFullName').value = user.full_name || $('editUserFullName').value;
  $('editUserRole').value = user.role || $('editUserRole').value;
}

function renderQueues(items) {
  if (!items.length) {
    $('queuesList').innerHTML = '<li class="empty-state">Очереди пока не найдены.</li>';
    $('routeQueueId').value = '';
    $('updateQueueId').value = '';
    $('ivrQueueId').value = '';
    $('ivrTestQueueId').value = '';
    return;
  }
  $('queuesList').innerHTML = items
    .map((item) => `<li><strong>${item.name}</strong><br/>ID: ${item.queue_id}<br/>правил: ${(item.rules || []).length}</li>`)
    .join('');
}

function renderIvrFlows(items) {
  if (!items.length) {
    $('ivrFlowsList').innerHTML = '<li class="empty-state">IVR-сценарии пока не настроены.</li>';
    return;
  }
  $('ivrFlowsList').innerHTML = items
    .map(
      (item) =>
        `<li><strong>${item.name}</strong><br/>ID: ${item.flow_id}<br/>очередь: ${item.queue_id}<br/>активен: ${item.is_active}</li>`,
    )
    .join('');
}

function renderAsteriskEvents(items) {
  if (!items.length) {
    $('asteriskEventsList').innerHTML = '<li class="empty-state">Bridge-события Asterisk не найдены.</li>';
    return;
  }
  $('asteriskEventsList').innerHTML = items
    .map(
      (item) =>
        `<li><strong>${item.ami_event_name}</strong><br/>событие: ${item.bridge_event_id}<br/>звонок: ${item.call_id}<br/>статус: ${item.forward_status}</li>`,
    )
    .join('');
}

function syncAsteriskEventFields(item) {
  if (!item) {
    return;
  }
  $('asteriskBridgeEventId').value = item.bridge_event_id || $('asteriskBridgeEventId').value;
}

function syncIvrFlowFields(flow) {
  if (!flow) {
    return;
  }
  $('ivrFlowId').value = flow.flow_id || $('ivrFlowId').value;
  $('ivrName').value = flow.name || $('ivrName').value;
  $('ivrDescription').value = flow.description || $('ivrDescription').value;
  $('ivrQueueId').value = flow.queue_id || $('ivrQueueId').value;
  $('ivrEntryNodeId').value = flow.entry_node_id || $('ivrEntryNodeId').value;
  $('ivrIsActive').checked = Boolean(flow.is_active);
  $('ivrFlowJson').value = JSON.stringify(flow.flow_json || defaultVoiceStartIvrFlowDocument(), null, 2);
}

function syncIvrSessionFields(payload) {
  const session = payload?.session || payload;
  if (!session) {
    return;
  }
  $('ivrSessionId').value = session.session_id || $('ivrSessionId').value;
  $('ivrTestCallId').value = session.call_id || $('ivrTestCallId').value;
  $('ivrTestQueueId').value = session.queue_id || $('ivrTestQueueId').value;
  $('ivrTestInteractionId').value = session.interaction_id || $('ivrTestInteractionId').value;
}

function serializeVoiceNameConfigForm() {
  return {
    enabled: $('voiceNameEnabled').checked,
    start: {
      ask_name_on_start: $('voiceNameAskOnStart').checked,
      known_customer_behavior: $('voiceNameKnownCustomerBehavior').value,
      unknown_customer_behavior: $('voiceNameUnknownCustomerBehavior').value,
    },
    downstream: {
      missing_name_behavior: $('voiceNameMissingNameBehavior').value,
      uncertain_name_behavior: $('voiceNameUncertainNameBehavior').value,
      finalize_on_explicit_name: $('voiceNameFinalizeOnExplicitName').checked,
      finalize_on_confirmation: $('voiceNameFinalizeOnConfirmation').checked,
    },
    texts: {
      ru: {
        start_prompt: $('voiceNameRuStartPrompt').value.trim(),
        personalized_greeting_template: $('voiceNameRuPersonalizedGreeting').value.trim(),
        confirmation_greeting_template: $('voiceNameRuConfirmationGreeting').value.trim(),
        inline_followup_prompt: $('voiceNameRuInlineFollowup').value.trim(),
      },
      kz: {
        start_prompt: $('voiceNameKzStartPrompt').value.trim(),
        personalized_greeting_template: $('voiceNameKzPersonalizedGreeting').value.trim(),
        confirmation_greeting_template: $('voiceNameKzConfirmationGreeting').value.trim(),
        inline_followup_prompt: $('voiceNameKzInlineFollowup').value.trim(),
      },
    },
  };
}

function syncVoiceNameConfigFields(payload) {
  const config = payload?.config || payload;
  if (!config) {
    return;
  }
  $('voiceNameEnabled').checked = Boolean(config.enabled);
  $('voiceNameAskOnStart').checked = Boolean(config.start?.ask_name_on_start);
  $('voiceNameKnownCustomerBehavior').value = config.start?.known_customer_behavior || 'trust_and_handoff';
  $('voiceNameUnknownCustomerBehavior').value = config.start?.unknown_customer_behavior || 'ask_on_start';
  $('voiceNameMissingNameBehavior').value = config.downstream?.missing_name_behavior || 'ask_inline_once';
  $('voiceNameUncertainNameBehavior').value = config.downstream?.uncertain_name_behavior || 'confirm_then_finalize';
  $('voiceNameFinalizeOnExplicitName').checked = Boolean(config.downstream?.finalize_on_explicit_name);
  $('voiceNameFinalizeOnConfirmation').checked = Boolean(config.downstream?.finalize_on_confirmation);
  $('voiceNameRuStartPrompt').value = config.texts?.ru?.start_prompt || '';
  $('voiceNameRuPersonalizedGreeting').value = config.texts?.ru?.personalized_greeting_template || '';
  $('voiceNameRuConfirmationGreeting').value = config.texts?.ru?.confirmation_greeting_template || '';
  $('voiceNameRuInlineFollowup').value = config.texts?.ru?.inline_followup_prompt || '';
  $('voiceNameKzStartPrompt').value = config.texts?.kz?.start_prompt || '';
  $('voiceNameKzPersonalizedGreeting').value = config.texts?.kz?.personalized_greeting_template || '';
  $('voiceNameKzConfirmationGreeting').value = config.texts?.kz?.confirmation_greeting_template || '';
  $('voiceNameKzInlineFollowup').value = config.texts?.kz?.inline_followup_prompt || '';
}

function renderVoiceNameConfigSummary(payload) {
  const config = payload?.config || payload;
  const source = payload?.source || 'defaults';
  const updatedAt = payload?.updated_at || 'не сохранялось';
  const knownLabels = {
    trust_and_handoff: 'известное имя сразу принимается и передаётся дальше',
    confirm_in_downstream: 'известное имя уходит на подтверждение в downstream',
    ask_on_start: 'известного клиента всё равно спрашиваем на старте',
  };
  const unknownLabels = {
    ask_on_start: 'неизвестного клиента спрашиваем на старте',
    skip_to_downstream: 'неизвестного клиента сразу передаём в downstream',
  };
  const missingLabels = {
    ask_inline_once: 'если имя не получено, AI спросит inline один раз',
    do_not_ask: 'если имя не получено, AI inline не спрашивает',
  };
  const uncertainLabels = {
    confirm_then_finalize: 'неуверенное имя нужно подтвердить',
    finalize_immediately: 'неуверенное имя считается финальным сразу',
    discard_and_collect: 'неуверенное имя сбрасывается и собирается заново',
  };
  $('voiceNameSummary').textContent = [
    `Сценарий: ${config?.enabled ? 'включён' : 'выключен'}`,
    `Стартовый вопрос про имя: ${config?.start?.ask_name_on_start ? 'да' : 'нет'}`,
    `Известный клиент: ${knownLabels[config?.start?.known_customer_behavior] || config?.start?.known_customer_behavior || 'не задано'}`,
    `Неизвестный клиент: ${unknownLabels[config?.start?.unknown_customer_behavior] || config?.start?.unknown_customer_behavior || 'не задано'}`,
    `Если имя не получено: ${missingLabels[config?.downstream?.missing_name_behavior] || config?.downstream?.missing_name_behavior || 'не задано'}`,
    `Если имя неуверенное: ${uncertainLabels[config?.downstream?.uncertain_name_behavior] || config?.downstream?.uncertain_name_behavior || 'не задано'}`,
    `Финализация по явному имени: ${config?.downstream?.finalize_on_explicit_name ? 'да' : 'нет'}`,
    `Финализация по подтверждению: ${config?.downstream?.finalize_on_confirmation ? 'да' : 'нет'}`,
    `Источник конфигурации: ${source}`,
    `Последнее обновление: ${updatedAt}`,
  ].join('\n');
}

async function loadVoiceNameConfig() {
  try {
    const data = await api('ai', 'ai/voice/config/name-collection');
    state.voiceNameConfig = data;
    syncVoiceNameConfigFields(data);
    renderVoiceNameConfigSummary(data);
    $('voiceNameConfigOutput').textContent = JSON.stringify(data, null, 2);
    log('Настройки voice name collection загружены', {
      source: data.source,
      updated_at: data.updated_at,
    });
  } catch (err) {
    $('voiceNameConfigOutput').textContent = err.message;
    log('Не удалось загрузить настройки voice name collection', { error: err.message });
  }
}

async function saveVoiceNameConfig() {
  try {
    const payload = serializeVoiceNameConfigForm();
    const data = await api('ai', 'ai/voice/config/name-collection', {
      method: 'PUT',
      body: JSON.stringify(payload),
    });
    state.voiceNameConfig = data;
    syncVoiceNameConfigFields(data);
    renderVoiceNameConfigSummary(data);
    $('voiceNameConfigOutput').textContent = JSON.stringify(data, null, 2);
    log('Настройки voice name collection сохранены', {
      source: data.source,
      updated_at: data.updated_at,
    });
  } catch (err) {
    $('voiceNameConfigOutput').textContent = err.message;
    log('Не удалось сохранить настройки voice name collection', { error: err.message });
  }
}

function resetVoiceNameConfigForm() {
  const snapshot = state.voiceNameConfig;
  if (!snapshot) {
    $('voiceNameConfigOutput').textContent = 'Сначала загрузите текущие настройки voice name collection.';
    return;
  }
  syncVoiceNameConfigFields(snapshot);
  renderVoiceNameConfigSummary(snapshot);
  $('voiceNameConfigOutput').textContent = JSON.stringify(snapshot, null, 2);
  log('Форма voice name collection сброшена к текущим настройкам');
}

function voiceTtsProviderConfig(config, provider) {
  return config?.[provider] || { ru: {}, kz: {} };
}

function voiceTtsOptionsFor(provider, language) {
  return state.voiceTtsConfig?.voice_options?.[provider]?.[language] || [];
}

function fillVoiceTtsPresetSelect(selectId, options, selectedValue) {
  const select = $(selectId);
  if (!select) {
    return;
  }
  const rows = ['<option value="">Свой вариант</option>'].concat(
    (options || []).map((item) => `<option value="${item.value}">${item.label}</option>`),
  );
  select.innerHTML = rows.join('');
  const hasSelectedValue = (options || []).some((item) => item.value === selectedValue);
  select.value = hasSelectedValue ? selectedValue : '';
}

function syncVoiceTtsPresetOptions(payload) {
  const config = payload?.config || payload || state.voiceTtsConfig?.config;
  if (!config) {
    return;
  }
  fillVoiceTtsPresetSelect('voiceTtsYandexRuVoicePreset', voiceTtsOptionsFor('yandex', 'ru'), config.yandex?.ru?.voice || '');
  fillVoiceTtsPresetSelect('voiceTtsYandexKzVoicePreset', voiceTtsOptionsFor('yandex', 'kz'), config.yandex?.kz?.voice || '');
  fillVoiceTtsPresetSelect('voiceTtsElevenRuVoicePreset', voiceTtsOptionsFor('elevenlabs', 'ru'), config.elevenlabs?.ru?.voice || '');
  fillVoiceTtsPresetSelect('voiceTtsElevenKzVoicePreset', voiceTtsOptionsFor('elevenlabs', 'kz'), config.elevenlabs?.kz?.voice || '');
  fillVoiceTtsPresetSelect('voiceTtsOpenAiRuVoicePreset', voiceTtsOptionsFor('openai', 'ru'), config.openai?.ru?.voice || '');
  fillVoiceTtsPresetSelect('voiceTtsOpenAiKzVoicePreset', voiceTtsOptionsFor('openai', 'kz'), config.openai?.kz?.voice || '');
}

function serializeVoiceTtsConfigForm() {
  return {
    provider: $('voiceTtsProvider').value || 'yandex',
    yandex: {
      ru: { voice: $('voiceTtsYandexRuVoice').value.trim() || null },
      kz: { voice: $('voiceTtsYandexKzVoice').value.trim() || null },
    },
    elevenlabs: {
      ru: {
        voice: $('voiceTtsElevenRuVoice').value.trim() || null,
        model_id: $('voiceTtsElevenRuModel').value.trim() || null,
        language_code: $('voiceTtsElevenRuLanguageCode').value.trim() || null,
      },
      kz: {
        voice: $('voiceTtsElevenKzVoice').value.trim() || null,
        model_id: $('voiceTtsElevenKzModel').value.trim() || null,
        language_code: $('voiceTtsElevenKzLanguageCode').value.trim() || null,
      },
    },
    openai: {
      ru: {
        voice: $('voiceTtsOpenAiRuVoice').value.trim() || null,
        model_id: $('voiceTtsOpenAiRuModel').value.trim() || null,
        language_code: $('voiceTtsOpenAiRuLanguageCode').value.trim() || null,
      },
      kz: {
        voice: $('voiceTtsOpenAiKzVoice').value.trim() || null,
        model_id: $('voiceTtsOpenAiKzModel').value.trim() || null,
        language_code: $('voiceTtsOpenAiKzLanguageCode').value.trim() || null,
      },
    },
  };
}

function syncVoiceTtsConfigFields(payload) {
  const config = payload?.config || payload;
  if (!config) {
    return;
  }
  $('voiceTtsProvider').value = config.provider || 'yandex';
  $('voiceTtsYandexRuVoice').value = config.yandex?.ru?.voice || '';
  $('voiceTtsYandexKzVoice').value = config.yandex?.kz?.voice || '';
  $('voiceTtsElevenRuVoice').value = config.elevenlabs?.ru?.voice || '';
  $('voiceTtsElevenRuModel').value = config.elevenlabs?.ru?.model_id || '';
  $('voiceTtsElevenRuLanguageCode').value = config.elevenlabs?.ru?.language_code || '';
  $('voiceTtsElevenKzVoice').value = config.elevenlabs?.kz?.voice || '';
  $('voiceTtsElevenKzModel').value = config.elevenlabs?.kz?.model_id || '';
  $('voiceTtsElevenKzLanguageCode').value = config.elevenlabs?.kz?.language_code || '';
  $('voiceTtsOpenAiRuVoice').value = config.openai?.ru?.voice || '';
  $('voiceTtsOpenAiRuModel').value = config.openai?.ru?.model_id || '';
  $('voiceTtsOpenAiRuLanguageCode').value = config.openai?.ru?.language_code || '';
  $('voiceTtsOpenAiKzVoice').value = config.openai?.kz?.voice || '';
  $('voiceTtsOpenAiKzModel').value = config.openai?.kz?.model_id || '';
  $('voiceTtsOpenAiKzLanguageCode').value = config.openai?.kz?.language_code || '';
  syncVoiceTtsPresetOptions(payload);
}

function renderVoiceTtsConfigSummary(payload) {
  const config = payload?.config || payload;
  const source = payload?.source || 'defaults';
  const updatedAt = payload?.updated_at || 'не сохранялось';
  const activeProvider = config?.provider || 'yandex';
  const activeSettings = voiceTtsProviderConfig(config, activeProvider);
  $('voiceTtsSummary').textContent = [
    `Активный провайдер: ${activeProvider}`,
    `RU голос: ${activeSettings?.ru?.voice || 'не задан'}`,
    `RU модель: ${activeSettings?.ru?.model_id || 'по умолчанию'}`,
    `RU language_code: ${activeSettings?.ru?.language_code || 'по умолчанию'}`,
    `KZ голос: ${activeSettings?.kz?.voice || 'не задан'}`,
    `KZ модель: ${activeSettings?.kz?.model_id || 'по умолчанию'}`,
    `KZ language_code: ${activeSettings?.kz?.language_code || 'по умолчанию'}`,
    `Источник конфигурации: ${source}`,
    `Последнее обновление: ${updatedAt}`,
  ].join('\n');
}

async function loadVoiceTtsConfig() {
  try {
    const data = await api('ai', 'ai/voice/config/tts');
    state.voiceTtsConfig = data;
    syncVoiceTtsConfigFields(data);
    renderVoiceTtsConfigSummary(data);
    $('voiceTtsConfigOutput').textContent = JSON.stringify(data, null, 2);
    log('Настройки voice TTS загружены', { source: data.source, updated_at: data.updated_at });
  } catch (err) {
    $('voiceTtsConfigOutput').textContent = err.message;
    log('Не удалось загрузить настройки voice TTS', { error: err.message });
  }
}

async function saveVoiceTtsConfig() {
  try {
    const payload = serializeVoiceTtsConfigForm();
    const data = await api('ai', 'ai/voice/config/tts', {
      method: 'PUT',
      body: JSON.stringify(payload),
    });
    state.voiceTtsConfig = data;
    syncVoiceTtsConfigFields(data);
    renderVoiceTtsConfigSummary(data);
    $('voiceTtsConfigOutput').textContent = JSON.stringify(data, null, 2);
    log('Настройки voice TTS сохранены', { provider: data?.config?.provider, updated_at: data.updated_at });
  } catch (err) {
    $('voiceTtsConfigOutput').textContent = err.message;
    log('Не удалось сохранить настройки voice TTS', { error: err.message });
  }
}

function resetVoiceTtsConfigForm() {
  const snapshot = state.voiceTtsConfig;
  if (!snapshot) {
    $('voiceTtsConfigOutput').textContent = 'Сначала загрузите текущие настройки voice TTS.';
    return;
  }
  syncVoiceTtsConfigFields(snapshot);
  renderVoiceTtsConfigSummary(snapshot);
  $('voiceTtsConfigOutput').textContent = JSON.stringify(snapshot, null, 2);
  log('Форма voice TTS сброшена к текущим настройкам');
}

function bindVoiceTtsPreset(selectId, inputId) {
  const select = $(selectId);
  if (!select) {
    return;
  }
  select.addEventListener('change', () => {
    if (select.value) {
      $(inputId).value = select.value;
    }
  });
}

function parseIvrFlowJson() {
  ensureDefaultIvrFlowJson();
  return JSON.parse($('ivrFlowJson').value || '{}');
}

async function loadUsers() {
  try {
    const data = await api('auth', 'users');
    renderUsers(data);
    if (data.length && !$('userLookupId').value.trim()) {
      $('userLookupId').value = data[0].user_id;
    }
    log('Список пользователей обновлён', { count: data.length });
  } catch (err) {
    $('usersList').innerHTML = '<li class="empty-state">Не удалось загрузить пользователей.</li>';
    log('Не удалось загрузить пользователей', { error: err.message });
  }
}

async function loadUserDetail() {
  const userId = $('userLookupId').value.trim();
  if (!userId) {
    $('userDetailOutput').textContent = 'Укажите user_id для загрузки карточки.';
    return;
  }
  try {
    const data = await api('auth', `users/${encodeURIComponent(userId)}`);
    $('userDetailOutput').textContent = JSON.stringify(data, null, 2);
    syncUserEditFields(data);
    log('Карточка пользователя загружена', { user_id: data.user_id, role: data.role });
  } catch (err) {
    $('userDetailOutput').textContent = err.message;
    log('Не удалось загрузить карточку пользователя', { error: err.message });
  }
}

async function createUser() {
  try {
    const data = await api('auth', 'users', {
      method: 'POST',
      body: JSON.stringify({
        username: $('newUsername').value.trim() || 'wave2_admin_demo',
        password: $('newPassword').value,
        full_name: $('newFullName').value.trim() || 'Wave 2 Demo User',
        role: $('newUserRole').value,
      }),
    });
    $('userDetailOutput').textContent = JSON.stringify(data, null, 2);
    syncUserEditFields(data);
    $('editUserPassword').value = '';
    log('Пользователь создан', { username: data.username, role: data.role });
    await loadUsers();
  } catch (err) {
    $('userDetailOutput').textContent = err.message;
    log('Не удалось создать пользователя', { error: err.message });
  }
}

async function updateUser() {
  const userId = $('userLookupId').value.trim();
  if (!userId) {
    $('userDetailOutput').textContent = 'Укажите user_id для обновления пользователя.';
    return;
  }

  const payload = {
    full_name: $('editUserFullName').value.trim() || null,
    role: $('editUserRole').value || null,
  };
  const password = $('editUserPassword').value;
  if (password) {
    payload.password = password;
  }

  try {
    const data = await api('auth', `users/${encodeURIComponent(userId)}`, {
      method: 'PATCH',
      body: JSON.stringify(payload),
    });
    $('userDetailOutput').textContent = JSON.stringify(data, null, 2);
    syncUserEditFields(data);
    $('editUserPassword').value = '';
    log('Пользователь обновлён', { user_id: data.user_id, role: data.role });
    await loadUsers();
  } catch (err) {
    $('userDetailOutput').textContent = err.message;
    log('Не удалось обновить пользователя', { error: err.message });
  }
}

async function deleteUser() {
  const userId = $('userLookupId').value.trim();
  if (!userId) {
    $('userDetailOutput').textContent = 'Укажите user_id для удаления пользователя.';
    return;
  }

  try {
    const data = await api('auth', `users/${encodeURIComponent(userId)}`, {
      method: 'DELETE',
    });
    $('userDetailOutput').textContent = JSON.stringify(data, null, 2);
    $('userLookupId').value = '';
    $('editUserPassword').value = '';
    log('Пользователь удалён', { user_id: data.user_id });
    await loadUsers();
  } catch (err) {
    $('userDetailOutput').textContent = err.message;
    log('Не удалось удалить пользователя', { error: err.message });
  }
}

async function loadQueues() {
  try {
    const data = await api('routing', 'queues');
    renderQueues(data);
    if (data.length) {
      const first = data[0];
      if (!$('routeQueueId').value.trim()) {
        $('routeQueueId').value = first.queue_id;
      }
      if (!$('updateQueueId').value.trim()) {
        $('updateQueueId').value = first.queue_id;
      }
      if (!$('ivrQueueId').value.trim()) {
        $('ivrQueueId').value = first.queue_id;
      }
      if (!$('ivrTestQueueId').value.trim()) {
        $('ivrTestQueueId').value = first.queue_id;
      }
    }
    log('Список очередей обновлён', { count: data.length });
  } catch (err) {
    $('queuesList').innerHTML = '<li class="empty-state">Не удалось загрузить очереди.</li>';
    log('Не удалось загрузить очереди', { error: err.message });
  }
}

async function createQueue() {
  try {
    const payload = {
      name: $('queueName').value.trim() || 'Демонстрационная очередь',
      description: $('queueDescription').value.trim() || '',
      rules: queueRulePayload($('queueChannel').value, $('queueSla').value),
    };
    const data = await api('routing', 'queues', {
      method: 'POST',
      body: JSON.stringify(payload),
    });
    $('routeQueueId').value = data.queue_id;
    $('updateQueueId').value = data.queue_id;
    if (!$('ivrQueueId').value.trim()) {
      $('ivrQueueId').value = data.queue_id;
    }
    if (!$('ivrTestQueueId').value.trim()) {
      $('ivrTestQueueId').value = data.queue_id;
    }
    $('queueUpdateOutput').textContent = JSON.stringify(data, null, 2);
    log('Очередь создана', { queue_id: data.queue_id, channel: $('queueChannel').value });
    await loadQueues();
  } catch (err) {
    $('queueUpdateOutput').textContent = err.message;
    log('Не удалось создать очередь', { error: err.message });
  }
}

async function updateQueueRules() {
  const queueId = $('updateQueueId').value.trim();
  if (!queueId) {
    $('queueUpdateOutput').textContent = 'Укажите queue_id для обновления.';
    return;
  }
  try {
    const payload = {
      name: $('updateQueueName').value.trim() || 'Демонстрационная очередь обновлена',
      description: $('updateQueueDescription').value.trim() || '',
      rules: queueRulePayload($('updateQueueChannel').value, $('updateQueueSla').value),
    };
    const data = await api('routing', `queues/${encodeURIComponent(queueId)}/rules`, {
      method: 'PATCH',
      body: JSON.stringify(payload),
    });
    $('queueUpdateOutput').textContent = JSON.stringify(data, null, 2);
    $('routeQueueId').value = data.queue_id;
    log('Правила очереди обновлены', { queue_id: data.queue_id, rules: (data.rules || []).length });
    await loadQueues();
  } catch (err) {
    $('queueUpdateOutput').textContent = err.message;
    log('Не удалось обновить очередь', { error: err.message });
  }
}

async function deleteQueue() {
  const queueId = $('updateQueueId').value.trim();
  if (!queueId) {
    $('queueUpdateOutput').textContent = 'Укажите queue_id для удаления очереди.';
    return;
  }

  try {
    const data = await api('routing', `queues/${encodeURIComponent(queueId)}`, {
      method: 'DELETE',
    });
    $('queueUpdateOutput').textContent = JSON.stringify(data, null, 2);
    if ($('routeQueueId').value.trim() === queueId) {
      $('routeQueueId').value = '';
    }
    if ($('ivrQueueId').value.trim() === queueId) {
      $('ivrQueueId').value = '';
    }
    if ($('ivrTestQueueId').value.trim() === queueId) {
      $('ivrTestQueueId').value = '';
    }
    $('updateQueueId').value = '';
    log('Очередь удалена', { queue_id: data.queue_id });
    await loadQueues();
  } catch (err) {
    $('queueUpdateOutput').textContent = err.message;
    log('Не удалось удалить очередь', { error: err.message });
  }
}

async function previewRoute() {
  try {
    const queueId = encodeURIComponent($('routeQueueId').value.trim());
    const channel = encodeURIComponent($('routeChannel').value);
    const priority = Number($('routePriority').value || 3);
    const data = await api('routing', `queues/${queueId}/route?channel=${channel}&priority=${priority}`, {
      method: 'POST',
    });
    $('routeOutput').textContent = JSON.stringify(data, null, 2);
    log('Маршрут рассчитан', { queue_id: data.queue_id, assignee: data.assignee });
  } catch (err) {
    $('routeOutput').textContent = err.message;
    log('Не удалось рассчитать маршрут', { error: err.message });
  }
}

async function loadIvrFlows() {
  try {
    const data = await api('ivr', 'ivr/flows');
    renderIvrFlows(data);
    if (data.length && !$('ivrFlowId').value.trim()) {
      syncIvrFlowFields(data[0]);
    }
    log('IVR-сценарии загружены', { count: data.length });
  } catch (err) {
    $('ivrFlowsList').innerHTML = '<li class="empty-state">Не удалось загрузить IVR-сценарии.</li>';
    $('ivrFlowOutput').textContent = err.message;
    log('Не удалось загрузить IVR-сценарии', { error: err.message });
  }
}

async function loadIvrFlowDetail() {
  const flowId = $('ivrFlowId').value.trim();
  if (!flowId) {
    $('ivrFlowOutput').textContent = 'Укажите ID сценария для открытия IVR.';
    return;
  }
  try {
    const data = await api('ivr', `ivr/flows/${encodeURIComponent(flowId)}`);
    syncIvrFlowFields(data);
    $('ivrFlowOutput').textContent = JSON.stringify(data, null, 2);
    log('IVR-сценарий загружен', { flow_id: data.flow_id, active: data.is_active });
  } catch (err) {
    $('ivrFlowOutput').textContent = err.message;
    log('Не удалось загрузить IVR-сценарий', { error: err.message });
  }
}

async function createIvrFlow() {
  try {
    const data = await api('ivr', 'ivr/flows', {
      method: 'POST',
      body: JSON.stringify({
        name: $('ivrName').value.trim() || 'Демонстрационный IVR-сценарий',
        description: $('ivrDescription').value.trim() || '',
        queue_id: $('ivrQueueId').value.trim(),
        entry_node_id: $('ivrEntryNodeId').value.trim() || 'root',
        flow_json: parseIvrFlowJson(),
        is_active: $('ivrIsActive').checked,
      }),
    });
    syncIvrFlowFields(data);
    $('ivrFlowOutput').textContent = JSON.stringify(data, null, 2);
    log('IVR-сценарий создан', { flow_id: data.flow_id, queue_id: data.queue_id });
    await loadIvrFlows();
  } catch (err) {
    $('ivrFlowOutput').textContent = err.message;
    log('Не удалось создать IVR-сценарий', { error: err.message });
  }
}

async function updateIvrFlow() {
  const flowId = $('ivrFlowId').value.trim();
  if (!flowId) {
    $('ivrFlowOutput').textContent = 'Укажите ID сценария для обновления IVR.';
    return;
  }
  try {
    const data = await api('ivr', `ivr/flows/${encodeURIComponent(flowId)}`, {
      method: 'PATCH',
      body: JSON.stringify({
        name: $('ivrName').value.trim() || null,
        description: $('ivrDescription').value.trim() || null,
        entry_node_id: $('ivrEntryNodeId').value.trim() || null,
        flow_json: parseIvrFlowJson(),
        is_active: $('ivrIsActive').checked,
      }),
    });
    syncIvrFlowFields(data);
    $('ivrFlowOutput').textContent = JSON.stringify(data, null, 2);
    log('IVR-сценарий обновлён', { flow_id: data.flow_id, version: data.version });
    await loadIvrFlows();
  } catch (err) {
    $('ivrFlowOutput').textContent = err.message;
    log('Не удалось обновить IVR-сценарий', { error: err.message });
  }
}

async function activateIvrFlow() {
  const flowId = $('ivrFlowId').value.trim();
  if (!flowId) {
    $('ivrFlowOutput').textContent = 'Укажите ID сценария для активации IVR.';
    return;
  }
  try {
    const data = await api('ivr', `ivr/flows/${encodeURIComponent(flowId)}/activate`, {
      method: 'POST',
    });
    syncIvrFlowFields(data);
    $('ivrFlowOutput').textContent = JSON.stringify(data, null, 2);
    log('IVR-сценарий активирован', { flow_id: data.flow_id });
    await loadIvrFlows();
  } catch (err) {
    $('ivrFlowOutput').textContent = err.message;
    log('Не удалось активировать IVR-сценарий', { error: err.message });
  }
}

async function startIvrSession() {
  try {
    const data = await api('ivr', 'ivr/sessions/start', {
      method: 'POST',
      body: JSON.stringify({
        call_id: $('ivrTestCallId').value.trim() || 'demo_call_ivr',
        queue_id: $('ivrTestQueueId').value.trim(),
        interaction_id: $('ivrTestInteractionId').value.trim() || null,
      }),
    });
    syncIvrSessionFields(data);
    $('ivrSessionOutput').textContent = JSON.stringify(data, null, 2);
    log('IVR-сессия запущена', { session_id: data.session.session_id, flow_id: data.session.flow_id });
  } catch (err) {
    $('ivrSessionOutput').textContent = err.message;
    log('Не удалось запустить IVR-сессию', { error: err.message });
  }
}

async function sendIvrDigit() {
  const sessionId = $('ivrSessionId').value.trim();
  if (!sessionId) {
    $('ivrSessionOutput').textContent = 'Укажите ID сессии для отправки DTMF.';
    return;
  }
  try {
    const data = await api('ivr', `ivr/sessions/${encodeURIComponent(sessionId)}/dtmf`, {
      method: 'POST',
      body: JSON.stringify({
        digit: ($('ivrDigit').value.trim() || '2').slice(0, 1),
      }),
    });
    syncIvrSessionFields(data);
    $('ivrSessionOutput').textContent = JSON.stringify(data, null, 2);
    log('DTMF обработан', {
      session_id: data.session.session_id,
      completed: data.completed,
      outcome_code: data.session.outcome_code,
    });
  } catch (err) {
    $('ivrSessionOutput').textContent = err.message;
    log('Не удалось обработать DTMF', { error: err.message });
  }
}

async function loadIvrSession() {
  const sessionId = $('ivrSessionId').value.trim();
  if (!sessionId) {
    $('ivrSessionOutput').textContent = 'Укажите ID сессии для загрузки IVR.';
    return;
  }
  try {
    const data = await api('ivr', `ivr/sessions/${encodeURIComponent(sessionId)}`);
    syncIvrSessionFields(data);
    $('ivrSessionOutput').textContent = JSON.stringify(data, null, 2);
    log('IVR-сессия загружена', { session_id: data.session_id, status: data.status });
  } catch (err) {
    $('ivrSessionOutput').textContent = err.message;
    log('Не удалось загрузить IVR-сессию', { error: err.message });
  }
}

async function previewIvrRoute() {
  const queueId = $('ivrTestQueueId').value.trim();
  const sessionId = $('ivrSessionId').value.trim();
  if (!queueId || !sessionId) {
    $('ivrRouteOutput').textContent = 'Укажите ID очереди и ID сессии для предпросмотра IVR-маршрута.';
    return;
  }
  try {
    const data = await api(
      'routing',
      `queues/${encodeURIComponent(queueId)}/route?channel=voice&priority=3&ivr_session_id=${encodeURIComponent(sessionId)}`,
      {
        method: 'POST',
      },
    );
    $('ivrRouteOutput').textContent = JSON.stringify(data, null, 2);
    log('Предпросмотр IVR-маршрута готов', {
      original_queue_id: data.original_queue_id || queueId,
      resolved_queue_id: data.resolved_queue_id || data.queue_id,
    });
  } catch (err) {
    $('ivrRouteOutput').textContent = err.message;
    log('Не удалось показать IVR-маршрут', { error: err.message });
  }
}

async function loadAsteriskStatus() {
  try {
    const data = await api('asterisk-bridge', 'asterisk/status');
    $('asteriskStatusOutput').textContent = JSON.stringify(data, null, 2);
    log('Статус моста Asterisk загружен', {
      status: data.status,
      ami_connected: data.ami_connected,
    });
  } catch (err) {
    $('asteriskStatusOutput').textContent = err.message;
    log('Не удалось загрузить статус моста Asterisk', { error: err.message });
  }
}

async function loadAsteriskEvents() {
  try {
    const filter = $('asteriskEventStatus').value;
    const suffix = filter ? `?status=${encodeURIComponent(filter)}` : '';
    const data = await api('asterisk-bridge', `asterisk/events${suffix}`);
    renderAsteriskEvents(data);
    if (data.length && !$('asteriskBridgeEventId').value.trim()) {
      syncAsteriskEventFields(data[0]);
    }
    log('Bridge-события Asterisk загружены', { count: data.length, status: filter || 'all' });
  } catch (err) {
    $('asteriskEventsList').innerHTML = '<li class="empty-state">Не удалось загрузить bridge-события Asterisk.</li>';
    $('asteriskEventOutput').textContent = err.message;
    log('Не удалось загрузить bridge-события Asterisk', { error: err.message });
  }
}

async function loadAsteriskEventDetail() {
  const bridgeEventId = $('asteriskBridgeEventId').value.trim();
  if (!bridgeEventId) {
    $('asteriskEventOutput').textContent = 'Укажите ID bridge-события для открытия.';
    return;
  }
  try {
    const data = await api('asterisk-bridge', `asterisk/events/${encodeURIComponent(bridgeEventId)}`);
    syncAsteriskEventFields(data);
    $('asteriskEventOutput').textContent = JSON.stringify(data, null, 2);
    log('Bridge-событие Asterisk загружено', {
      bridge_event_id: data.bridge_event_id,
      status: data.forward_status,
    });
  } catch (err) {
    $('asteriskEventOutput').textContent = err.message;
    log('Не удалось загрузить bridge-событие Asterisk', { error: err.message });
  }
}

async function retryAsteriskEvent() {
  const bridgeEventId = $('asteriskBridgeEventId').value.trim();
  if (!bridgeEventId) {
    $('asteriskEventOutput').textContent = 'Укажите ID bridge-события для повтора.';
    return;
  }
  try {
    const data = await api('asterisk-bridge', `asterisk/events/${encodeURIComponent(bridgeEventId)}/retry`, {
      method: 'POST',
    });
    syncAsteriskEventFields(data);
    $('asteriskEventOutput').textContent = JSON.stringify(data, null, 2);
    log('Bridge-событие Asterisk отправлено на повтор', {
      bridge_event_id: data.bridge_event_id,
      status: data.forward_status,
    });
    await loadAsteriskEvents();
  } catch (err) {
    $('asteriskEventOutput').textContent = err.message;
    log('Не удалось повторить bridge-событие Asterisk', { error: err.message });
  }
}

async function reconnectAsterisk() {
  try {
    const data = await api('asterisk-bridge', 'asterisk/reconnect', {
      method: 'POST',
    });
    $('asteriskStatusOutput').textContent = JSON.stringify(data, null, 2);
    log('Запрошено переподключение моста Asterisk', {
      status: data.status,
      ami_connected: data.ami_connected,
    });
  } catch (err) {
    $('asteriskStatusOutput').textContent = err.message;
    log('Не удалось запросить переподключение Asterisk', { error: err.message });
  }
}

function wire() {
  $('loginBtn').addEventListener('click', login);
  $('corporateLoginBtn').addEventListener('click', startCorporateLogin);
  $('refreshBtn').addEventListener('click', async () => {
    await Promise.all([
      loadUsers(),
      loadQueues(),
      loadIvrFlows(),
      loadVoiceNameConfig(),
      loadVoiceTtsConfig(),
      loadAsteriskStatus(),
      loadAsteriskEvents(),
    ]);
    log('Данные админ-консоли обновлены');
  });
  $('loadUsersBtn').addEventListener('click', loadUsers);
  $('loadUserDetailBtn').addEventListener('click', loadUserDetail);
  $('createUserBtn').addEventListener('click', createUser);
  $('updateUserBtn').addEventListener('click', updateUser);
  $('deleteUserBtn').addEventListener('click', deleteUser);
  $('loadQueuesBtn').addEventListener('click', loadQueues);
  $('createQueueBtn').addEventListener('click', createQueue);
  $('updateQueueBtn').addEventListener('click', updateQueueRules);
  $('deleteQueueBtn').addEventListener('click', deleteQueue);
  $('previewRouteBtn').addEventListener('click', previewRoute);
  $('loadIvrFlowsBtn').addEventListener('click', loadIvrFlows);
  $('createIvrFlowBtn').addEventListener('click', createIvrFlow);
  $('loadIvrFlowDetailBtn').addEventListener('click', loadIvrFlowDetail);
  $('updateIvrFlowBtn').addEventListener('click', updateIvrFlow);
  $('activateIvrFlowBtn').addEventListener('click', activateIvrFlow);
  $('startIvrSessionBtn').addEventListener('click', startIvrSession);
  $('sendIvrDigitBtn').addEventListener('click', sendIvrDigit);
  $('loadIvrSessionBtn').addEventListener('click', loadIvrSession);
  $('previewIvrRouteBtn').addEventListener('click', previewIvrRoute);
  $('loadVoiceNameConfigBtn').addEventListener('click', loadVoiceNameConfig);
  $('saveVoiceNameConfigBtn').addEventListener('click', saveVoiceNameConfig);
  $('resetVoiceNameConfigBtn').addEventListener('click', resetVoiceNameConfigForm);
  $('loadVoiceTtsConfigBtn').addEventListener('click', loadVoiceTtsConfig);
  $('saveVoiceTtsConfigBtn').addEventListener('click', saveVoiceTtsConfig);
  $('resetVoiceTtsConfigBtn').addEventListener('click', resetVoiceTtsConfigForm);
  bindVoiceTtsPreset('voiceTtsYandexRuVoicePreset', 'voiceTtsYandexRuVoice');
  bindVoiceTtsPreset('voiceTtsYandexKzVoicePreset', 'voiceTtsYandexKzVoice');
  bindVoiceTtsPreset('voiceTtsElevenRuVoicePreset', 'voiceTtsElevenRuVoice');
  bindVoiceTtsPreset('voiceTtsElevenKzVoicePreset', 'voiceTtsElevenKzVoice');
  bindVoiceTtsPreset('voiceTtsOpenAiRuVoicePreset', 'voiceTtsOpenAiRuVoice');
  bindVoiceTtsPreset('voiceTtsOpenAiKzVoicePreset', 'voiceTtsOpenAiKzVoice');
  $('loadAsteriskStatusBtn').addEventListener('click', loadAsteriskStatus);
  $('loadAsteriskEventsBtn').addEventListener('click', loadAsteriskEvents);
  $('loadAsteriskEventDetailBtn').addEventListener('click', loadAsteriskEventDetail);
  $('retryAsteriskEventBtn').addEventListener('click', retryAsteriskEvent);
  $('reconnectAsteriskBtn').addEventListener('click', reconnectAsterisk);
  $('logoutBtn').addEventListener('click', logout);
  window.addEventListener('message', handleOidcMessage);
}

async function init() {
  if (!restoreStoredSession()) {
    window.location.href = '/';
    return;
  }
  if (!ensurePageAccess()) {
    return;
  }
  wire();
  ensureDefaultIvrFlowJson();
  updateSessionInfo();
  await Promise.all([
    checkGateway(),
    loadOidcConfig(),
    loadUsers(),
    loadQueues(),
    loadIvrFlows(),
    loadVoiceNameConfig(),
    loadVoiceTtsConfig(),
    loadAsteriskStatus(),
    loadAsteriskEvents(),
  ]);
  log('Админ-консоль готова');
}

init();
