const authStatus = document.getElementById('auth-status');
const runState = document.getElementById('run-state');
const tiktokRunState = document.getElementById('tiktok-run-state');
const logList = document.getElementById('log-list');

let fontSizeDirty = false;
let formDirty = false;
let detectedPrinters = [];
let defaultPrinterName = '';
let recommendedDriverMode = 'auto';

init();

function init() {
  bindEvents();
  ensurePreviewUi();
  loadPrinters();
  loadStatus();
  window.setInterval(loadStatus, 3000);
}

function bindEvents() {
  bindClick('open-console-btn', openConsoleWithLaunch);
  bindClick('open-console-hint-btn', openConsoleWithLaunch);
  bindClick('refresh-btn', loadStatus);
  bindClick('stop-bridge-btn', stopBridge);
  bindClick('test-print-btn', testPrint);
  bindClick('preview-print-btn', previewPrint);
  bindClick('reload-printers-btn', loadPrinters);

  [
    'printer-name',
    'tiktok-unique-id',
    'tiktok-euler-api-key',
    'tiktok-reconnect-delay'
  ].forEach((id) => bindInput(id, () => {
    formDirty = true;
  }));

  [
    'test-font-size',
    'test-user-font-size',
    'test-offset-x',
    'test-offset-y',
    'test-paper-width-mm',
    'test-paper-height-mm'
  ].forEach((id) => bindInput(id, () => {
    fontSizeDirty = true;
  }));

  bindChange('printer-name', () => {
    formDirty = true;
    applyRecommendedDriverMode();
    savePreferences();
  });
  bindChange('print-mode', savePreferences);
  bindChange('printer-driver-mode', savePreferences);
  bindChange('tiktok-enabled', savePreferences);
  bindChange('tiktok-unique-id', savePreferences);
  bindChange('tiktok-euler-api-key', savePreferences);
  bindChange('tiktok-reconnect-delay', savePreferences);
  bindChange('test-font-size', savePreferences);
  bindChange('test-user-font-size', savePreferences);
  bindChange('test-offset-x', savePreferences);
  bindChange('test-offset-y', savePreferences);
  bindChange('test-paper-width-mm', savePreferences);
  bindChange('test-paper-height-mm', savePreferences);
}

function bindClick(id, handler) {
  const element = document.getElementById(id);
  if (element) element.addEventListener('click', handler);
}

function bindChange(id, handler) {
  const element = document.getElementById(id);
  if (element) element.addEventListener('change', handler);
}

function bindInput(id, handler) {
  const element = document.getElementById(id);
  if (element) element.addEventListener('input', handler);
}

function ensurePreviewUi() {
  if (!document.getElementById('print-preview-image') && authStatus) {
    const previewImage = document.createElement('img');
    previewImage.id = 'print-preview-image';
    previewImage.alt = 'Print preview';
    previewImage.style.display = 'none';
    previewImage.style.maxWidth = '100%';
    previewImage.style.border = '1px solid #d7d7d7';
    previewImage.style.borderRadius = '12px';
    previewImage.style.background = '#fff';

    const container = document.createElement('div');
    container.className = 'stack';
    container.appendChild(previewImage);
    authStatus.insertAdjacentElement('afterend', container);
  }
}

async function loadPrinters() {
  const select = document.getElementById('printer-name');
  const currentValue = select?.value || '';

  try {
    const response = await fetch('/api/printers', { cache: 'no-store' });
    const result = await response.json();
    if (!response.ok || !result.success) {
      renderPrinterOptions(currentValue, [], '', []);
      return;
    }

    detectedPrinters = Array.isArray(result.printers) ? result.printers : [];
    defaultPrinterName = result.recommendedPrinter || result.defaultPrinter || '';
    recommendedDriverMode = result.recommendedDriverMode || 'auto';
    renderPrinterOptions(currentValue, detectedPrinters, defaultPrinterName, result.usbDevices || []);
    applyRecommendedDriverMode();
  } catch (_error) {
    renderPrinterOptions(currentValue, [], '', []);
  }
}

function isVirtualPrinterName(name) {
  const normalized = String(name || '').toLowerCase();
  return [
    'microsoft print to pdf',
    'onenote',
    'fax',
    'pdf',
    'xps document',
    'send to onenote'
  ].some((keyword) => normalized.includes(keyword));
}

function renderPrinterOptions(selectedValue, printers, fallbackPrinter, usbDevices = []) {
  const select = document.getElementById('printer-name');
  if (!select) return;

  const names = printers
    .map((printer) => printer.name || '')
    .filter(Boolean);
  const uniqueNames = Array.from(new Set(names));
  const hasUsbDirect = Array.isArray(usbDevices) && usbDevices.length > 0;
  if (hasUsbDirect && !uniqueNames.includes('USB 直連標籤機')) {
    uniqueNames.unshift('USB 直連標籤機');
  }

  const usableSelected = selectedValue && !isVirtualPrinterName(selectedValue) ? selectedValue : '';
  const nextValue = usableSelected
    || fallbackPrinter
    || uniqueNames.find((name) => !isVirtualPrinterName(name))
    || uniqueNames[0]
    || '';

  if (nextValue && !uniqueNames.includes(nextValue)) {
    uniqueNames.unshift(nextValue);
  }

  select.innerHTML = uniqueNames.length
    ? uniqueNames.map((name) => {
      const printer = printers.find((item) => item.name === name) || {};
      const labelParts = [name];
      if (printer.isDefault) labelParts.push('預設');
      if (printer.offline) labelParts.push('離線');
      if (isVirtualPrinterName(name)) labelParts.push('不建議');
      if (name === 'USB 直連標籤機') labelParts.push('不走 Windows Driver');
      return `<option value="${escapeAttr(name)}">${escapeHtml(labelParts.join(' / '))}</option>`;
    }).join('')
    : '<option value="">找不到可用印表機</option>';

  select.value = nextValue;
}

function applyRecommendedDriverMode() {
  const modeSelect = document.getElementById('printer-driver-mode');
  const printerSelect = document.getElementById('printer-name');
  if (!modeSelect || !printerSelect) return;

  if (printerSelect.value === 'USB 直連標籤機') {
    modeSelect.value = 'usb_direct';
    return;
  }

  if ((!modeSelect.value || modeSelect.value === 'auto') && recommendedDriverMode && recommendedDriverMode !== 'auto') {
    modeSelect.value = recommendedDriverMode;
  }
}

async function loadStatus() {
  try {
    const response = await fetch('/api/status', { cache: 'no-store' });
    const result = await response.json();
    if (!response.ok || !result.success) return;

    const config = result.config || {};
    const status = result.status || {};
    const authorization = result.authorization || {};
    setText('app-version', result.appVersion ? `v${result.appVersion}` : '-');

    syncInputValue('api-base-url', config.apiBaseUrl || '');
    syncInputValue('printer-name', config.printerName || '');
    if (!config.printerName && defaultPrinterName) {
      syncInputValue('printer-name', defaultPrinterName);
    }
    syncInputValue('print-mode', config.printMode || 'desktop_print');
    syncInputValue('printer-driver-mode', config.printerDriverMode || 'auto');
    syncInputValue('test-font-size', config.testFontSize || 'auto');
    syncInputValue('test-user-font-size', config.testUserFontSize || 'auto');
    syncInputValue('test-offset-x', Number(config.testOffsetX || 0));
    syncInputValue('test-offset-y', Number(config.testOffsetY || 0));
    syncInputValue('test-paper-width-mm', Number(config.testPaperWidthMm || 80));
    syncInputValue('test-paper-height-mm', Number(config.testPaperHeightMm || 50));
    syncCheckboxValue('tiktok-enabled', Boolean(config.tiktokEnabled));
    syncInputValue('tiktok-unique-id', config.tiktokUniqueId || '');
    syncInputValue('tiktok-euler-api-key', config.hasTiktokEulerApiKey ? '********' : '');
    syncInputValue('tiktok-reconnect-delay', Number(config.tiktokReconnectDelaySec || 3));
    applyRecommendedDriverMode();

    renderStatus(status, config, authorization);
    renderAuthorizationStatus(authorization, status);
    renderTikTokStatus(result.tiktokStatus || {}, config);
    renderLogs(result.logs || []);
  } catch (error) {
    setStatus(`讀取狀態失敗: ${error.message}`);
  }
}

function renderStatus(status, config, authorization) {
  const running = Boolean(status.running);
  const loggedIn = Boolean(status.loggedIn);
  const captureOnly = config.printMode === 'capture_only';
  const authState = String(authorization.state || 'waiting_web');

  if (running && loggedIn) {
    runState.textContent = captureOnly ? '只抓留言' : '橋接在線';
    runState.className = 'badge-chip online';
    setStatus(captureOnly
      ? '桌面橋接器已連線，現在只抓留言。'
      : '桌面橋接器已連線，可接收 Web 指派的列印工作。');
  } else if (authState === 'exchanging') {
    runState.textContent = '授權交換中';
    runState.className = 'badge-chip offline';
    setStatus('桌面橋接器收到 Web 授權，正在等待雲端完成交換。');
  } else if (authState === 'pending_api') {
    runState.textContent = '等待 API 完成';
    runState.className = 'badge-chip offline';
    setStatus('桌面橋接器已收到 Web 授權，但雲端 authorize API 尚未完成。');
  } else if (authState === 'error') {
    runState.textContent = '授權失敗';
    runState.className = 'badge-chip offline';
    setStatus(status.lastError
      ? `桌面橋接器授權失敗: ${status.lastError}`
      : '桌面橋接器授權失敗，請回 Web 重新發起授權。');
  } else {
    runState.textContent = '等待 Web 授權';
    runState.className = 'badge-chip offline';
    setStatus('等待 Web 留言控制台授權桌面橋接器。');
  }

  setText('info-email', status.email || authorization.memberEmail || '-');
  setText('info-printer', status.printerName || config.printerName || '-');
  setText('info-print-mode', captureOnly ? '只抓留言' : '抓留言 + 桌面列印');
  setText('info-device-id', status.deviceId || authorization.deviceId || '-');
  setText('info-heartbeat', status.lastHeartbeatAt ? formatTime(status.lastHeartbeatAt) : '-');
  setText('info-error', status.lastError || authorization.lastError || '-');
  setText('last-time', status.lastPrintedAt ? formatTime(status.lastPrintedAt) : '-');
  setText(
    'last-print-summary',
    status.lastPrintedUser ? `${status.lastPrintedUser} / ${status.lastPrintedText || ''}` : '-'
  );
}

function renderAuthorizationStatus(authorization, status) {
  const state = String(authorization.state || 'waiting_web');
  setText('bridge-auth-state', state);
  setText('bridge-auth-member', authorization.memberEmail || status.email || '-');
  setText('bridge-auth-updated', authorization.updatedAt ? formatTime(authorization.updatedAt) : '-');
  setText('bridge-auth-note', authorization.note || '尚未收到 Web 授權');
}

function renderTikTokStatus(status, config) {
  const running = Boolean(status.running);
  const connected = Boolean(status.connected);
  const now = Date.now();
  const recentActivity = Boolean(status.lastForwardAt && now - status.lastForwardAt < 120000)
    || Boolean(status.lastMessageAt && now - status.lastMessageAt < 120000)
    || Boolean(status.lastCommentUser);
  const effectivelyActive = connected || recentActivity;

  tiktokRunState.textContent = !config.tiktokEnabled
    ? 'TikTok 未啟用'
    : effectivelyActive
      ? 'TikTok 監聽中'
      : running
        ? 'TikTok 連線中'
        : 'TikTok 未啟動';
  tiktokRunState.className = `badge-chip ${effectivelyActive ? 'online' : 'offline'}`;

  setText('tiktok-enabled-text', config.tiktokEnabled ? '已啟用' : '未啟用');
  setText('tiktok-unique-id-text', config.tiktokUniqueId || '-');
  setText('tiktok-room-url', status.roomUrl || '-');
  setText('tiktok-last-connect', status.lastConnectAt ? formatTime(status.lastConnectAt) : '-');
  setText('tiktok-last-message', status.lastMessageAt ? formatTime(status.lastMessageAt) : '-');
  setText('tiktok-last-forward', status.lastForwardAt ? formatTime(status.lastForwardAt) : '-');
  setText(
    'tiktok-last-comment',
    status.lastCommentUser
      ? `${status.lastCommentUser}${status.lastCommentUid ? ` (@${status.lastCommentUid})` : ''} / ${status.lastCommentText || ''}`
      : '-'
  );
  setText('tiktok-last-error', status.lastError || '-');
}

function renderLogs(logs) {
  if (!logList) return;

  const visibleLogs = (logs || []).filter((log) => !shouldHideDesktopLog(log));
  if (!visibleLogs.length) {
    logList.innerHTML = '<div class="empty">尚無日誌</div>';
    return;
  }

  logList.innerHTML = visibleLogs.map((log) => `
    <article class="log-entry ${escapeAttr(log.level || 'info')}">
      <div class="log-top">
        <span>${formatTime(log.at)}</span>
        <span>${escapeHtml(log.section || 'system')}</span>
      </div>
      <div class="log-message">${escapeHtml(log.message || '')}</div>
    </article>
  `).join('');
}

function shouldHideDesktopLog(log = {}) {
  const section = String(log.section || '').trim().toLowerCase();
  const message = String(log.message || '').trim().toLowerCase();
  if (section === 'ingest') return true;
  if (section === 'tiktok') return true;
  return message.includes('tiktok comment:')
    || message.includes('local ingest printed from')
    || message.includes('cloud ingest forward failed')
    || message.includes('request_timeout')
    || message.includes('heartbeat failed')
    || message.includes('job polling failed')
    || message.includes('auth refresh')
    || message.includes('recovering session')
    || message.includes('recovery failed');
}

async function openConsoleWithLaunch() {
  try {
    const response = await fetch('/api/console-launch', { cache: 'no-store' });
    const result = await response.json();
    if (!response.ok || !result.success || !result.url) {
      setStatus(`打開 Web 留言控制台失敗: ${result.error || 'unknown_error'}`);
      return;
    }

    window.open(result.url, '_blank', 'noopener,noreferrer');
    setStatus('已打開 Web 留言控制台，請在 Web 完成會員登入與桌面授權。');
  } catch (error) {
    setStatus(`打開 Web 留言控制台失敗: ${error.message}`);
  }
}

async function stopBridge() {
  try {
    const response = await fetch('/api/logout', { method: 'POST' });
    const result = await response.json().catch(() => ({ success: response.ok }));
    if (!response.ok || !result.success) {
      setStatus(`停止橋接器失敗: ${result.error || 'unknown_error'}`);
      return;
    }
    setStatus('桌面橋接器已停止，仍可繼續本機測試列印。');
    await loadStatus();
  } catch (error) {
    setStatus(`停止橋接器失敗: ${error.message}`);
  }
}

async function testPrint() {
  setStatus('送出測試列印中...');
  const currentFontSize = readFontSetting('test-font-size', 'auto');
  const currentUserFontSize = readFontSetting('test-user-font-size', 'auto');
  const currentOffsetX = Number(document.getElementById('test-offset-x')?.value || 0);
  const currentOffsetY = Number(document.getElementById('test-offset-y')?.value || 0);
  const currentPaperWidthMm = Number(document.getElementById('test-paper-width-mm')?.value || 80);
  const currentPaperHeightMm = Number(document.getElementById('test-paper-height-mm')?.value || 50);

  await savePreferences(currentFontSize);

  try {
    const response = await fetch('/api/test-print', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        fontSize: currentFontSize,
        userFontSize: currentUserFontSize,
        offsetX: currentOffsetX,
        offsetY: currentOffsetY,
        paperWidthMm: currentPaperWidthMm,
        paperHeightMm: currentPaperHeightMm
      })
    });
    const result = await response.json();
    if (!response.ok || !result.success) {
      setStatus(`測試列印失敗: ${result.error || 'unknown_error'}`);
      return;
    }

    setStatus(`已送出測試列印，字級 ${result.fontSize || currentFontSize}`);
    await loadStatus();
  } catch (error) {
    setStatus(`測試列印失敗: ${error.message}`);
  }
}

async function previewPrint() {
  setStatus('產生列印預覽中...');
  const currentFontSize = readFontSetting('test-font-size', 'auto');
  const currentUserFontSize = readFontSetting('test-user-font-size', 'auto');
  const currentOffsetX = Number(document.getElementById('test-offset-x')?.value || 0);
  const currentOffsetY = Number(document.getElementById('test-offset-y')?.value || 0);
  const currentPaperWidthMm = Number(document.getElementById('test-paper-width-mm')?.value || 80);
  const currentPaperHeightMm = Number(document.getElementById('test-paper-height-mm')?.value || 50);

  await savePreferences(currentFontSize);

  try {
    const response = await fetch('/api/test-preview', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        fontSize: currentFontSize,
        userFontSize: currentUserFontSize,
        offsetX: currentOffsetX,
        offsetY: currentOffsetY,
        paperWidthMm: currentPaperWidthMm,
        paperHeightMm: currentPaperHeightMm
      })
    });
    const result = await response.json();
    if (!response.ok || !result.success) {
      setStatus(`列印預覽失敗: ${result.error || 'unknown_error'}`);
      return;
    }

    const previewImage = document.getElementById('print-preview-image');
    if (previewImage) {
      previewImage.src = result.previewUrl;
      previewImage.style.display = 'block';
    }
    setStatus(`預覽已更新，留言字級 ${result.fontSize}，客戶字級 ${result.userFontSize}`);
  } catch (error) {
    setStatus(`列印預覽失敗: ${error.message}`);
  }
}

async function savePreferences(forcedFontSize) {
  const payload = {
    printerName: document.getElementById('printer-name')?.value.trim() || '',
    printMode: document.getElementById('print-mode')?.value || 'desktop_print',
    printerDriverMode: document.getElementById('printer-driver-mode')?.value || 'auto',
    testFontSize: forcedFontSize === undefined ? readFontSetting('test-font-size', 'auto') : forcedFontSize,
    testUserFontSize: readFontSetting('test-user-font-size', 'auto'),
    testOffsetX: Number(document.getElementById('test-offset-x')?.value || 0),
    testOffsetY: Number(document.getElementById('test-offset-y')?.value || 0),
    testPaperWidthMm: Number(document.getElementById('test-paper-width-mm')?.value || 80),
    testPaperHeightMm: Number(document.getElementById('test-paper-height-mm')?.value || 50),
    tiktokEnabled: document.getElementById('tiktok-enabled')?.checked || false,
    tiktokUniqueId: document.getElementById('tiktok-unique-id')?.value.trim() || '',
    tiktokEulerApiKey: normalizeSecretInput(document.getElementById('tiktok-euler-api-key')?.value || ''),
    tiktokReconnectDelaySec: Number(document.getElementById('tiktok-reconnect-delay')?.value || 3)
  };

  try {
    const response = await fetch('/api/preferences', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    const result = await response.json().catch(() => ({ success: response.ok }));
    if (!response.ok || !result.success) {
      setStatus(`儲存設定失敗: ${result.error || 'unknown_error'}`);
      return;
    }
    fontSizeDirty = false;
    formDirty = false;
  } catch (error) {
    setStatus(`儲存設定失敗: ${error.message}`);
  }
}

function syncInputValue(id, value) {
  const element = document.getElementById(id);
  if (!element) return;
  const userEditableFields = new Set([
    'printer-name',
    'tiktok-unique-id',
    'tiktok-euler-api-key',
    'tiktok-reconnect-delay'
  ]);
  if (userEditableFields.has(id) && formDirty && element.value !== String(value)) return;
  if (document.activeElement === element && fontSizeDirty) return;
  element.value = value;
}

function syncCheckboxValue(id, checked) {
  const element = document.getElementById(id);
  if (element) element.checked = checked;
}

function setText(id, text) {
  const element = document.getElementById(id);
  if (element) element.textContent = text;
}

function setStatus(text) {
  if (authStatus) authStatus.textContent = text;
}

function formatTime(value) {
  if (!value) return '-';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  return date.toLocaleString('zh-TW', { hour12: false });
}

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

function escapeAttr(value) {
  return escapeHtml(value);
}

function normalizeSecretInput(value) {
  const text = String(value || '').trim();
  return text === '********' ? '' : text;
}

function readFontSetting(id, fallback) {
  const text = String(document.getElementById(id)?.value || '').trim();
  if (!text || text.toLowerCase() === 'auto' || text.toLowerCase() === 'fit') return fallback;
  const number = Number(text);
  return Number.isFinite(number) ? number : fallback;
}
