import './styles/main.css';
import './styles/settings-window.css';
import { SettingsManager } from '@/services/settings-manager';
import { exportSettings, importSettings, type ImportResult } from '@/utils/settings-persistence';
import {
  SETTINGS_CATEGORIES,
  HUMAN_LABELS,
  SIGNUP_URLS,
  PLAINTEXT_KEYS,
  MASKED_SENTINEL,
  type SettingsCategory,
} from '@/services/settings-constants';
import { fetchOllamaModels } from '@/services/ollama-models';
import {
  RUNTIME_FEATURES,
  getEffectiveSecrets,
  getRuntimeConfigSnapshot,
  getSecretState,
  isFeatureAvailable,
  isFeatureEnabled,
  setFeatureToggle,
  setSecretValue,
  validateSecret,
  loadDesktopSecrets,
  type RuntimeFeatureDefinition,
  type RuntimeFeatureId,
  type RuntimeSecretKey,
} from '@/services/runtime-config';
import { getApiBaseUrl, isDesktopRuntime, resolveLocalApiPort, startSmartPollLoop, type SmartPollLoopHandle } from '@/services/runtime';
import { tryInvokeTauri, invokeTauri } from '@/services/tauri-bridge';
import { escapeHtml } from '@/utils/sanitize';
import { initI18n, t } from '@/services/i18n';
import { applyStoredTheme } from '@/utils/theme-manager';
import { applyFont } from '@/services/font-settings';
import { trackFeatureToggle } from '@/services/analytics';
import { setTrustedHtml, trustedHtml } from '@/utils/dom-utils';


let activeSection = 'overview';
let settingsManager: SettingsManager;
let _diagCleanup: (() => void) | null = null;

function setActionStatus(message: string, tone: 'ok' | 'error' = 'ok'): void {
  const statusEl = document.getElementById('settingsActionStatus');
  if (!statusEl) return;
  statusEl.textContent = message;
  statusEl.classList.remove('ok', 'error');
  statusEl.classList.add(tone);
}

async function invokeDesktopAction(command: string, successLabel: string): Promise<void> {
  const result = await tryInvokeTauri<string>(command);
  if (result) {
    setActionStatus(`${successLabel}: ${result}`, 'ok');
    return;
  }
  setActionStatus(t('modals.settingsWindow.invokeFail', { command }), 'error');
}

function closeSettingsWindow(): void {
  void tryInvokeTauri<void>('close_settings_window').then(() => { }, () => window.close());
}

function getSidecarBase(): string {
  return getApiBaseUrl() || '';
}

let _diagToken: string | null = null;

async function diagFetch(path: string, init?: RequestInit): Promise<Response> {
  if (!_diagToken) {
    try {
      _diagToken = await tryInvokeTauri<string>('get_local_api_token');
    } catch { /* token unavailable */ }
  }
  const headers = new Headers(init?.headers);
  if (_diagToken) headers.set('Authorization', `Bearer ${_diagToken}`);
  return fetch(`${getSidecarBase()}${path}`, { ...init, headers });
}

// ── Sidebar icons ──

const SIDEBAR_ICONS: Record<string, string> = {
  overview: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56A7.987 7.987 0 015.08 16zm2.95-8H5.08a7.987 7.987 0 014.33-3.56A15.65 15.65 0 008.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 01-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/></svg>',
  ai: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M21 10.12h-6.78l2.74-2.82c-2.73-2.7-7.15-2.8-9.88-.1s-2.73 7.08 0 9.79 7.15 2.71 9.88 0C18.32 15.65 19 14.08 19 12.1h2c0 1.98-.88 4.55-2.64 6.29-3.51 3.48-9.21 3.48-12.72 0-3.5-3.47-3.53-9.11-.02-12.58s9.14-3.49 12.65 0L21 3v7.12zM12.5 8v4.25l3.5 2.08-.72 1.21L11 13V8h1.5z"/></svg>',
  economy: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z"/></svg>',
  markets: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M5 9.2h3V19H5V9.2zM10.6 5h2.8v14h-2.8V5zm5.6 8H19v6h-2.8v-6z"/></svg>',
  security: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/></svg>',
  tracking: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>',
  debug: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"/></svg>',
};

// ── Sidebar ──

function getFeatureStatusCounts(cat: SettingsCategory): { ready: number; total: number } {
  let ready = 0;
  for (const fid of cat.features) {
    if (isFeatureAvailable(fid)) ready++;
  }
  return { ready, total: cat.features.length };
}

function getTotalProgress(): { ready: number; total: number } {
  let ready = 0;
  for (const f of RUNTIME_FEATURES) {
    if (isFeatureAvailable(f.id)) ready++;
  }
  return { ready, total: RUNTIME_FEATURES.length };
}

function renderSidebar(): void {
  const nav = document.getElementById('sidebarNav');
  if (!nav) return;

  const items: string[] = [];

  const progress = getTotalProgress();
  const overviewDotClass = progress.ready === progress.total ? 'dot-ok' : progress.ready > 0 ? 'dot-partial' : 'dot-warn';
  items.push(`
    <button class="settings-nav-item${activeSection === 'overview' ? ' active' : ''}" data-section="overview" role="tab" aria-selected="${activeSection === 'overview'}">
      ${SIDEBAR_ICONS.overview}
      <span class="settings-nav-label">Overview</span>
      <span class="settings-nav-dot ${overviewDotClass}"></span>
    </button>
  `);

  items.push('<div class="settings-nav-sep"></div>');

  for (const cat of SETTINGS_CATEGORIES) {
    const { ready, total } = getFeatureStatusCounts(cat);
    const dotClass = ready === total ? 'dot-ok' : ready > 0 ? 'dot-partial' : 'dot-warn';
    items.push(`
      <button class="settings-nav-item${activeSection === cat.id ? ' active' : ''}" data-section="${cat.id}" role="tab" aria-selected="${activeSection === cat.id}">
        ${SIDEBAR_ICONS[cat.id] || ''}
        <span class="settings-nav-label">${escapeHtml(cat.label)}</span>
        <span class="settings-nav-count">${ready}/${total}</span>
        <span class="settings-nav-dot ${dotClass}"></span>
      </button>
    `);
  }

  items.push('<div class="settings-nav-sep"></div>');

  items.push(`
    <button class="settings-nav-item${activeSection === 'debug' ? ' active' : ''}" data-section="debug" role="tab" aria-selected="${activeSection === 'debug'}">
      ${SIDEBAR_ICONS.debug}
      <span class="settings-nav-label">Debug &amp; Logs</span>
    </button>
  `);

  setTrustedHtml(nav, trustedHtml(items.join(''), "legacy direct innerHTML migration"));
}

// ── Section rendering ──

function renderSection(sectionId: string): void {
  const area = document.getElementById('contentArea');
  if (!area) return;

  if (_diagCleanup) { _diagCleanup(); _diagCleanup = null; }
  activeSection = sectionId;
  renderSidebar();

  area.classList.add('fade-out');
  area.classList.remove('fade-in');

  requestAnimationFrame(() => {
    if (sectionId === 'overview') {
      renderOverview(area);
    } else if (sectionId === 'debug') {
      renderDebug(area);
    } else {
      const cat = SETTINGS_CATEGORIES.find(c => c.id === sectionId);
      if (cat) renderFeatureSection(area, cat);
    }

    requestAnimationFrame(() => {
      area.classList.remove('fade-out');
      area.classList.add('fade-in');
    });
  });
}

// ── Overview ──

function renderOverview(area: HTMLElement): void {
  const { ready, total } = getTotalProgress();
  const pct = total > 0 ? (ready / total) * 100 : 0;
  const circumference = 2 * Math.PI * 40;
  const dashOffset = circumference - (pct / 100) * circumference;
  const ringColor = ready === total ? 'var(--settings-green)' : ready > 0 ? 'var(--settings-blue)' : 'var(--settings-yellow)';

  const wmState = getSecretState('WORLDMONITOR_API_KEY');
  const wmStatusText = wmState.present ? 'Active' : 'Not set';
  const wmStatusClass = wmState.present ? 'ok' : 'warn';
  const catCards = SETTINGS_CATEGORIES.map(cat => {
    const { ready: catReady, total: catTotal } = getFeatureStatusCounts(cat);
    const cls = catReady === catTotal ? 'ov-cat-ok' : catReady > 0 ? 'ov-cat-partial' : 'ov-cat-warn';
    return `<button class="settings-ov-cat ${cls}" data-section="${cat.id}">
      <span class="settings-ov-cat-label">${escapeHtml(cat.label)}</span>
      <span class="settings-ov-cat-count">${catReady}/${catTotal} ready</span>
    </button>`;
  }).join('');

  setTrustedHtml(area, trustedHtml(`
    <div class="settings-overview">
      <div class="settings-ov-progress">
        <svg class="settings-ov-ring" viewBox="0 0 100 100" width="120" height="120">
          <circle cx="50" cy="50" r="40" fill="none" stroke="rgba(255,255,255,0.08)" stroke-width="8"/>
          <circle cx="50" cy="50" r="40" fill="none" stroke="${ringColor}" stroke-width="8"
            stroke-linecap="round" stroke-dasharray="${circumference}" stroke-dashoffset="${dashOffset}"
            transform="rotate(-90 50 50)" style="transition:stroke-dashoffset 0.6s ease"/>
        </svg>
        <div class="settings-ov-ring-text">
          <span class="settings-ov-ring-num">${ready}</span>
          <span class="settings-ov-ring-label">of ${total} ready</span>
        </div>
      </div>
      <div class="settings-ov-cats">${catCards}</div>
    </div>

    <div class="settings-ov-license">
      <section class="wm-section">
        <h2 class="wm-section-title">${t('modals.settingsWindow.worldMonitor.apiKey.title')}</h2>
        <p class="wm-section-desc">${t('modals.settingsWindow.worldMonitor.apiKey.description')}</p>
        <div class="wm-key-row">
          <div class="wm-input-wrap">
            <input type="password" class="wm-input" data-wm-key-input
              placeholder="${t('modals.settingsWindow.worldMonitor.apiKey.placeholder')}"
              autocomplete="off" spellcheck="false"
              ${wmState.present ? `value="${MASKED_SENTINEL}"` : ''} />
            <button type="button" class="wm-toggle-vis" data-wm-toggle title="Show/hide">&#x1f441;</button>
          </div>
          <span class="wm-badge ${wmStatusClass}">${wmStatusText}</span>
        </div>
      </section>

      <div class="wm-divider"><span>${t('modals.settingsWindow.worldMonitor.dividerOr')}</span></div>

      <section class="wm-section">
        <h2 class="wm-section-title">${t('modals.settingsWindow.worldMonitor.register.title')}</h2>
        <p class="wm-section-desc">${t('modals.settingsWindow.worldMonitor.register.description')}</p>
        <div class="wm-register-row">
          <button type="button" class="wm-submit-btn" data-wm-open-pro>
            ${t('modals.settingsWindow.worldMonitor.register.submitBtn')}
          </button>
        </div>
      </section>
    </div>
  `, "legacy direct innerHTML migration"));

  initOverviewListeners(area);
}

function initOverviewListeners(area: HTMLElement): void {
  area.querySelector('[data-wm-toggle]')?.addEventListener('click', () => {
    const input = area.querySelector<HTMLInputElement>('[data-wm-key-input]');
    if (input) input.type = input.type === 'password' ? 'text' : 'password';
  });

  area.querySelector<HTMLInputElement>('[data-wm-key-input]')?.addEventListener('input', (e) => {
    const input = e.target as HTMLInputElement;
    if (input.value.startsWith(MASKED_SENTINEL)) {
      input.value = input.value.slice(MASKED_SENTINEL.length);
    }
  });

  area.querySelector('[data-wm-open-pro]')?.addEventListener('click', () => {
    const url = 'https://worldmonitor.app/pro';
    void invokeTauri<void>('open_url', { url }).catch(() => window.open(url, '_blank', 'noopener,noreferrer'));
  });

  area.querySelectorAll<HTMLButtonElement>('.settings-ov-cat[data-section]').forEach(btn => {
    btn.addEventListener('click', () => {
      const section = btn.dataset.section;
      if (section) renderSection(section);
    });
  });
}

// ── Feature sections ──

function renderFeatureSection(area: HTMLElement, cat: SettingsCategory): void {
  const features = cat.features
    .map(fid => RUNTIME_FEATURES.find(f => f.id === fid))
    .filter(Boolean) as RuntimeFeatureDefinition[];

  const featureCards = features.map(feature => {
    const enabled = isFeatureEnabled(feature.id);
    const available = isFeatureAvailable(feature.id);
    const effectiveSecrets = getEffectiveSecrets(feature);
    const allStaged = !available && effectiveSecrets.every(
      k => getSecretState(k).valid || (settingsManager.hasPending(k) && settingsManager.getValidationState(k).validated !== false)
    );
    const borderClass = available ? 'ready' : allStaged ? 'staged' : 'needs';
    const pillClass = available ? 'ok' : allStaged ? 'staged' : 'warn';
    const pillLabel = available ? 'Ready' : allStaged ? 'Staged' : 'Needs keys';
    const secretRows = effectiveSecrets.map(key => renderSecretInput(key, feature.id)).join('');
    const fallbackHtml = (available || allStaged) ? '' : `<p class="settings-feat-fallback">${escapeHtml(feature.fallback)}</p>`;

    return `
      <div class="settings-feat ${borderClass}" data-feature-id="${feature.id}">
        <div class="settings-feat-header" data-feat-toggle-expand="${feature.id}">
          <label class="settings-feat-toggle-label" data-click-stop>
            <div class="settings-feat-switch">
              <input type="checkbox" data-toggle="${feature.id}" ${enabled ? 'checked' : ''} />
              <span class="settings-feat-slider"></span>
            </div>
          </label>
          <div class="settings-feat-info">
            <span class="settings-feat-name">${escapeHtml(feature.name)}</span>
            <span class="settings-feat-desc">${escapeHtml(feature.description)}</span>
          </div>
          <span class="settings-feat-pill ${pillClass}">${pillLabel}</span>
          <span class="settings-feat-chevron">&#x25B8;</span>
        </div>
        <div class="settings-feat-body">
          ${secretRows}
          ${fallbackHtml}
        </div>
      </div>
    `;
  }).join('');

  setTrustedHtml(area, trustedHtml(`
    <div class="settings-section-header">
      <h2>${escapeHtml(cat.label)}</h2>
    </div>
    <div class="settings-feat-list">${featureCards}</div>
  `, "legacy direct innerHTML migration"));

  initFeatureSectionListeners(area);
}

function renderSecretInput(key: RuntimeSecretKey, _featureId: RuntimeFeatureId): string {
  const state = getSecretState(key);
  const pending = settingsManager.hasPending(key);
  const { validated, message } = settingsManager.getValidationState(key);
  const label = HUMAN_LABELS[key] || key;
  const signupUrl = SIGNUP_URLS[key];
  const isPlaintext = PLAINTEXT_KEYS.has(key);
  const showGetKey = signupUrl && !state.present && !pending;

  const statusText = pending
    ? (validated === false ? 'Invalid' : 'Staged')
    : !state.present ? 'Missing' : state.valid ? 'Valid' : 'Looks invalid';
  const statusClass = pending
    ? (validated === false ? 'warn' : 'staged')
    : state.valid ? 'ok' : 'warn';
  const inputClass = pending ? (validated === false ? 'invalid' : 'valid-staged') : '';
  const hintText = pending && validated === false ? (message || 'Invalid value') : null;

  if (key === 'OLLAMA_MODEL') {
    const storedModel = pending
      ? settingsManager.getPending(key) || ''
      : getRuntimeConfigSnapshot().secrets[key]?.value || '';
    return `
      <div class="settings-secret-row">
        <div class="settings-secret-label">${escapeHtml(label)}</div>
        <span class="settings-secret-status ${statusClass}">${escapeHtml(statusText)}</span>
        <select data-model-select data-feature="${_featureId}" class="${inputClass}">
          ${storedModel ? `<option value="${escapeHtml(storedModel)}" selected>${escapeHtml(storedModel)}</option>` : '<option value="" selected disabled>Loading models...</option>'}
        </select>
        <input type="text" data-model-manual data-feature="${_featureId}" class="${inputClass} hidden-input"
          placeholder="Or type model name" autocomplete="off"
          ${storedModel ? `value="${escapeHtml(storedModel)}"` : ''}>
        ${hintText ? `<span class="settings-secret-hint">${escapeHtml(hintText)}</span>` : ''}
      </div>
    `;
  }

  const getKeyHtml = showGetKey
    ? `<a href="#" data-signup-url="${signupUrl}" class="settings-secret-link">Get key</a>`
    : '';

  return `
    <div class="settings-secret-row">
      <div class="settings-secret-label">${escapeHtml(label)}</div>
      <span class="settings-secret-status ${statusClass}">${escapeHtml(statusText)}</span>
      <div class="settings-input-wrapper${showGetKey ? ' has-suffix' : ''}">
        <input type="${isPlaintext ? 'text' : 'password'}" data-secret="${key}" data-feature="${_featureId}"
          placeholder="${pending ? 'Staged' : 'Enter value...'}" autocomplete="off" class="${inputClass}"
          ${pending ? `value="${isPlaintext ? escapeHtml(settingsManager.getPending(key) || '') : MASKED_SENTINEL}"` : (isPlaintext && state.present ? `value="${escapeHtml(getRuntimeConfigSnapshot().secrets[key]?.value || '')}"` : '')}>
        ${getKeyHtml}
      </div>
      ${hintText ? `<span class="settings-secret-hint">${escapeHtml(hintText)}</span>` : ''}
    </div>
  `;
}

function initFeatureSectionListeners(area: HTMLElement): void {
  area.querySelectorAll<HTMLElement>('[data-feat-toggle-expand]').forEach(header => {
    header.addEventListener('click', (e) => {
      if ((e.target as HTMLElement).closest('[data-click-stop]')) return;
      const card = header.closest('.settings-feat');
      card?.classList.toggle('expanded');
    });
  });

  area.querySelectorAll<HTMLInputElement>('input[data-toggle]').forEach(input => {
    input.addEventListener('change', () => {
      const featureId = input.dataset.toggle as RuntimeFeatureId;
      if (!featureId) return;
      trackFeatureToggle(featureId, input.checked);
      setFeatureToggle(featureId, input.checked);
      renderSidebar();
    });
  });

  area.querySelectorAll<HTMLInputElement>('input[data-secret]').forEach(input => {
    input.addEventListener('input', () => {
      const key = input.dataset.secret as RuntimeSecretKey;
      if (!key) return;
      if (settingsManager.hasPending(key) && input.value.startsWith(MASKED_SENTINEL)) {
        input.value = input.value.slice(MASKED_SENTINEL.length);
      }
      settingsManager.setValidation(key, true);
      input.classList.remove('valid-staged', 'invalid');
      const hint = input.closest('.settings-secret-row')?.querySelector('.settings-secret-hint');
      if (hint) hint.remove();
    });

    input.addEventListener('blur', () => {
      const key = input.dataset.secret as RuntimeSecretKey;
      if (!key) return;
      const raw = input.value.trim();

      if (!raw) {
        if (settingsManager.hasPending(key)) {
          settingsManager.deletePending(key);
          renderSection(activeSection);
        }
        return;
      }
      if (raw === MASKED_SENTINEL) return;

      settingsManager.setPending(key, raw);
      const result = validateSecret(key, raw);
      if (result.valid) {
        settingsManager.setValidation(key, true);
      } else {
        settingsManager.setValidation(key, false, result.hint || 'Invalid format');
      }

      if (PLAINTEXT_KEYS.has(key)) {
        input.value = raw;
      } else {
        input.type = 'password';
        input.value = MASKED_SENTINEL;
      }

      input.classList.remove('valid-staged', 'invalid');
      input.classList.add(result.valid ? 'valid-staged' : 'invalid');

      const statusEl = input.closest('.settings-secret-row')?.querySelector('.settings-secret-status');
      if (statusEl) {
        statusEl.textContent = result.valid ? 'Staged' : 'Invalid';
        statusEl.className = `settings-secret-status ${result.valid ? 'staged' : 'warn'}`;
      }

      const row = input.closest('.settings-secret-row');
      const existingHint = row?.querySelector('.settings-secret-hint');
      if (existingHint) existingHint.remove();
      if (!result.valid && result.hint) {
        const hint = document.createElement('span');
        hint.className = 'settings-secret-hint';
        hint.textContent = result.hint;
        row?.appendChild(hint);
      }

      updateFeatureCardStatus(input.dataset.feature as RuntimeFeatureId);

      if (key === 'OLLAMA_API_URL' && result.valid) {
        const modelSelect = area.querySelector<HTMLSelectElement>('select[data-model-select]');
        if (modelSelect) void loadOllamaModelsIntoSelect(modelSelect);
      }

      renderSidebar();
    });
  });

  area.querySelectorAll<HTMLAnchorElement>('a[data-signup-url]').forEach(link => {
    link.addEventListener('click', (e) => {
      e.preventDefault();
      const url = link.dataset.signupUrl;
      if (!url) return;
      if (isDesktopRuntime()) {
        void invokeTauri<void>('open_url', { url }).catch(() => window.open(url, '_blank', 'noopener,noreferrer'));
      } else {
        window.open(url, '_blank', 'noopener,noreferrer');
      }
    });
  });

  const modelSelect = area.querySelector<HTMLSelectElement>('select[data-model-select]');
  if (modelSelect) {
    modelSelect.addEventListener('change', () => {
      const model = modelSelect.value;
      if (model) {
        settingsManager.setPending('OLLAMA_MODEL', model);
        settingsManager.setValidation('OLLAMA_MODEL', true);
        modelSelect.classList.remove('invalid');
        modelSelect.classList.add('valid-staged');
        updateFeatureCardStatus('aiOllama');
        renderSidebar();
      }
    });
    void loadOllamaModelsIntoSelect(modelSelect);
  }
}

function updateFeatureCardStatus(featureId: RuntimeFeatureId): void {
  const card = document.querySelector<HTMLElement>(`.settings-feat[data-feature-id="${featureId}"]`);
  if (!card) return;
  const feature = RUNTIME_FEATURES.find(f => f.id === featureId);
  if (!feature) return;

  const available = isFeatureAvailable(featureId);
  const effectiveSecrets = getEffectiveSecrets(feature);
  const allStaged = !available && effectiveSecrets.every(
    k => getSecretState(k).valid || (settingsManager.hasPending(k) && settingsManager.getValidationState(k).validated !== false)
  );

  const wasExpanded = card.classList.contains('expanded');
  card.className = `settings-feat ${available ? 'ready' : allStaged ? 'staged' : 'needs'}${wasExpanded ? ' expanded' : ''}`;

  const pill = card.querySelector('.settings-feat-pill');
  if (pill) {
    pill.className = `settings-feat-pill ${available ? 'ok' : allStaged ? 'staged' : 'warn'}`;
    pill.textContent = available ? 'Ready' : allStaged ? 'Staged' : 'Needs keys';
  }
}

async function loadOllamaModelsIntoSelect(select: HTMLSelectElement): Promise<void> {
  const snapshot = getRuntimeConfigSnapshot();
  const ollamaUrl = settingsManager.getPending('OLLAMA_API_URL')
    || snapshot.secrets['OLLAMA_API_URL']?.value
    || '';
  if (!ollamaUrl) {
    setTrustedHtml(select, trustedHtml('<option value="" disabled selected>Set Ollama URL first</option>', "legacy direct innerHTML migration"));
    return;
  }

  const currentModel = settingsManager.getPending('OLLAMA_MODEL')
    || snapshot.secrets['OLLAMA_MODEL']?.value
    || '';

  const models = await fetchOllamaModels(ollamaUrl);

  if (models.length === 0) {
    const manual = select.parentElement?.querySelector<HTMLInputElement>('input[data-model-manual]');
    if (manual) {
      select.style.display = 'none';
      manual.classList.remove('hidden-input');
      if (!manual.dataset.listenerAttached) {
        manual.dataset.listenerAttached = '1';
        manual.addEventListener('blur', () => {
          const model = manual.value.trim();
          if (model) {
            settingsManager.setPending('OLLAMA_MODEL', model);
            settingsManager.setValidation('OLLAMA_MODEL', true);
            manual.classList.remove('invalid');
            manual.classList.add('valid-staged');
            updateFeatureCardStatus('aiOllama');
            renderSidebar();
          }
        });
      }
    }
    return;
  }

  const options = currentModel ? '' : '<option value="" selected disabled>Select a model...</option>';
  setTrustedHtml(select, trustedHtml(options + models.map(name =>
    `<option value="${escapeHtml(name)}" ${name === currentModel ? 'selected' : ''}>${escapeHtml(name)}</option>`
  ).join(''), "legacy direct innerHTML migration"));
}

// ── Debug section ──

function renderDebug(area: HTMLElement): void {
  setTrustedHtml(area, trustedHtml(`
    <div class="settings-section-header">
      <h2>Debug &amp; Logs</h2>
    </div>
    <div class="debug-actions">
      <button id="openLogsBtn" type="button">Open Logs Folder</button>
      <button id="openSidecarLogBtn" type="button">Open API Log</button>
    </div>
    <section class="debug-data-section">
      <h3>Data Management</h3>
      <div class="debug-data-actions">
        <button type="button" class="settings-btn settings-btn-secondary" id="exportSettingsBtn">
          ${t('components.settings.exportSettings')}
        </button>
        <button type="button" class="settings-btn settings-btn-secondary" id="importSettingsBtn">
          ${t('components.settings.importSettings')}
        </button>
        <input type="file" id="importSettingsInput" accept=".json" style="display: none;" />
      </div>
    </section>
    <section class="settings-diagnostics" id="diagnosticsSection">
      <header class="diag-header">
        <h2>Diagnostics</h2>
        <div class="diag-toggles">
          <label><input type="checkbox" id="verboseApiLog"> Verbose Sidecar Log</label>
          <label><input type="checkbox" id="fetchDebugLog"> Frontend Fetch Debug</label>
        </div>
      </header>
      <div class="diag-traffic-bar">
        <h3>API Traffic <span id="trafficCount"></span></h3>
        <div class="diag-traffic-controls">
          <label><input type="checkbox" id="autoRefreshLog" checked> Auto</label>
          <button id="refreshLogBtn" type="button">Refresh</button>
          <button id="clearLogBtn" type="button">Clear</button>
        </div>
      </div>
      <div id="trafficLog" class="diag-traffic-log"></div>
    </section>
  `, "legacy direct innerHTML migration"));

  area.querySelector('#openLogsBtn')?.addEventListener('click', () => {
    void invokeDesktopAction('open_logs_folder', t('modals.settingsWindow.openLogs'));
  });

  area.querySelector('#openSidecarLogBtn')?.addEventListener('click', () => {
    void invokeDesktopAction('open_sidecar_log_file', t('modals.settingsWindow.openApiLog'));
  });

  area.querySelector('#exportSettingsBtn')?.addEventListener('click', () => {
    exportSettings();
  });

  const importInput = area.querySelector<HTMLInputElement>('#importSettingsInput');
  area.querySelector('#importSettingsBtn')?.addEventListener('click', () => {
    importInput?.click();
  });

  importInput?.addEventListener('change', async (e) => {
    const file = (e.target as HTMLInputElement).files?.[0];
    if (!file) return;
    try {
      const result: ImportResult = await importSettings(file);
      setActionStatus(t('components.settings.importSuccess', { count: String(result.keysImported) }), 'ok');
    } catch (err: unknown) {
      if (err instanceof DOMException) {
        if (err.name === 'QuotaExceededError' || err.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
          setActionStatus(t('components.settings.importFailed') + ': storage limit reached', 'error');
        } else if (err.name === 'SecurityError') {
          setActionStatus(t('components.settings.importFailed') + ': storage blocked', 'error');
        } else {
          setActionStatus(`${t('components.settings.importFailed')}: ${err.message || err.name}`, 'error');
        }
      } else if (err instanceof Error && err.message) {
        setActionStatus(`${t('components.settings.importFailed')}: ${err.message}`, 'error');
      } else {
        setActionStatus(t('components.settings.importFailed'), 'error');
      }
    }
    importInput.value = '';
  });

  initDiagnostics();
}

function initDiagnostics(): void {
  const verboseToggle = document.getElementById('verboseApiLog') as HTMLInputElement | null;
  const fetchDebugToggle = document.getElementById('fetchDebugLog') as HTMLInputElement | null;
  const autoRefreshToggle = document.getElementById('autoRefreshLog') as HTMLInputElement | null;
  const refreshBtn = document.getElementById('refreshLogBtn');
  const clearBtn = document.getElementById('clearLogBtn');
  const trafficLogEl = document.getElementById('trafficLog');
  const trafficCount = document.getElementById('trafficCount');

  if (fetchDebugToggle) {
    fetchDebugToggle.checked = localStorage.getItem('wm-debug-log') === '1';
    fetchDebugToggle.addEventListener('change', () => {
      localStorage.setItem('wm-debug-log', fetchDebugToggle.checked ? '1' : '0');
    });
  }

  async function syncVerboseState(): Promise<void> {
    if (!verboseToggle) return;
    try {
      const res = await diagFetch('/api/local-debug-toggle');
      const data = await res.json();
      verboseToggle.checked = data.verboseMode;
    } catch { /* sidecar not running */ }
  }

  verboseToggle?.addEventListener('change', async () => {
    try {
      const res = await diagFetch('/api/local-debug-toggle', { method: 'POST' });
      const data = await res.json();
      if (verboseToggle) verboseToggle.checked = data.verboseMode;
      setActionStatus(data.verboseMode ? t('modals.settingsWindow.verboseOn') : t('modals.settingsWindow.verboseOff'), 'ok');
    } catch {
      setActionStatus(t('modals.settingsWindow.sidecarError'), 'error');
    }
  });

  void syncVerboseState();

  async function refreshTrafficLog(): Promise<void> {
    if (!trafficLogEl) return;
    try {
      const res = await diagFetch('/api/local-traffic-log');
      const data = await res.json();
      const entries: Array<{ timestamp: string; method: string; path: string; status: number; durationMs: number }> = data.entries || [];
      if (trafficCount) trafficCount.textContent = `(${entries.length})`;

      if (entries.length === 0) {
        setTrustedHtml(trafficLogEl, trustedHtml(`<p class="diag-empty">${t('modals.settingsWindow.noTraffic')}</p>`, "legacy direct innerHTML migration"));
        return;
      }

      const rows = entries.slice().reverse().map((e) => {
        const ts = e.timestamp.split('T')[1]?.replace('Z', '') || e.timestamp;
        const cls = e.status < 300 ? 'ok' : e.status < 500 ? 'warn' : 'err';
        return `<tr class="diag-${cls}"><td>${escapeHtml(ts)}</td><td>${e.method}</td><td title="${escapeHtml(e.path)}">${escapeHtml(e.path)}</td><td>${e.status}</td><td>${e.durationMs}ms</td></tr>`;
      }).join('');

      setTrustedHtml(trafficLogEl, trustedHtml(`<table class="diag-table"><thead><tr><th>${t('modals.settingsWindow.table.time')}</th><th>${t('modals.settingsWindow.table.method')}</th><th>${t('modals.settingsWindow.table.path')}</th><th>${t('modals.settingsWindow.table.status')}</th><th>${t('modals.settingsWindow.table.duration')}</th></tr></thead><tbody>${rows}</tbody></table>`, "legacy direct innerHTML migration"));
    } catch {
      setTrustedHtml(trafficLogEl, trustedHtml(`<p class="diag-empty">${t('modals.settingsWindow.sidecarUnreachable')}</p>`, "legacy direct innerHTML migration"));
    }
  }

  refreshBtn?.addEventListener('click', () => void refreshTrafficLog());

  clearBtn?.addEventListener('click', async () => {
    try { await diagFetch('/api/local-traffic-log', { method: 'DELETE' }); } catch { /* ignore */ }
    if (trafficLogEl) setTrustedHtml(trafficLogEl, trustedHtml(`<p class="diag-empty">${t('modals.settingsWindow.logCleared')}</p>`, "legacy direct innerHTML migration"));
    if (trafficCount) trafficCount.textContent = '(0)';
  });

  let refreshPollLoop: SmartPollLoopHandle | null = null;

  function startAutoRefresh(): void {
    stopAutoRefresh();
    refreshPollLoop = startSmartPollLoop(() => refreshTrafficLog(), {
      intervalMs: 3000,
      pauseWhenHidden: true,
      refreshOnVisible: true,
      runImmediately: true,
      jitterFraction: 0,
    });
  }

  function stopAutoRefresh(): void {
    if (refreshPollLoop) { refreshPollLoop.stop(); refreshPollLoop = null; }
  }

  autoRefreshToggle?.addEventListener('change', () => {
    if (autoRefreshToggle.checked) startAutoRefresh(); else stopAutoRefresh();
  });

  startAutoRefresh();

  _diagCleanup = stopAutoRefresh;
}

// ── Search ──

function highlightMatch(text: string, query: string): string {
  const escaped = escapeHtml(text);
  const qEscaped = escapeHtml(query);
  if (!qEscaped) return escaped;
  const regex = new RegExp(`(${qEscaped.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
  return escaped.replace(regex, '<mark>$1</mark>');
}

function handleSearch(query: string): void {
  const area = document.getElementById('contentArea');
  if (!area) return;

  if (!query.trim()) {
    renderSection(activeSection);
    return;
  }

  const q = query.toLowerCase();
  const matches: Array<{ feature: RuntimeFeatureDefinition; catLabel: string }> = [];

  for (const cat of SETTINGS_CATEGORIES) {
    for (const fid of cat.features) {
      const feature = RUNTIME_FEATURES.find(f => f.id === fid);
      if (!feature) continue;
      const searchable = [
        feature.name,
        feature.description,
        ...getEffectiveSecrets(feature).map(k => HUMAN_LABELS[k] || k),
      ].join(' ').toLowerCase();
      if (searchable.includes(q)) {
        matches.push({ feature, catLabel: cat.label });
      }
    }
  }

  if (matches.length === 0) {
    setTrustedHtml(area, trustedHtml(`<div class="settings-search-empty"><p>No features match "${escapeHtml(query)}"</p></div>`, "legacy direct innerHTML migration"));
    return;
  }

  const cards = matches.map(({ feature, catLabel }) => {
    const enabled = isFeatureEnabled(feature.id);
    const available = isFeatureAvailable(feature.id);
    const effectiveSecrets = getEffectiveSecrets(feature);
    const allStaged = !available && effectiveSecrets.every(
      k => getSecretState(k).valid || (settingsManager.hasPending(k) && settingsManager.getValidationState(k).validated !== false)
    );
    const borderClass = available ? 'ready' : allStaged ? 'staged' : 'needs';
    const pillClass = available ? 'ok' : allStaged ? 'staged' : 'warn';
    const pillLabel = available ? 'Ready' : allStaged ? 'Staged' : 'Needs keys';
    const secretRows = effectiveSecrets.map(key => renderSecretInput(key, feature.id)).join('');

    return `
      <div class="settings-feat ${borderClass} expanded" data-feature-id="${feature.id}">
        <div class="settings-feat-header" data-feat-toggle-expand="${feature.id}">
          <label class="settings-feat-toggle-label" data-click-stop>
            <div class="settings-feat-switch">
              <input type="checkbox" data-toggle="${feature.id}" ${enabled ? 'checked' : ''} />
              <span class="settings-feat-slider"></span>
            </div>
          </label>
          <div class="settings-feat-info">
            <span class="settings-feat-name">${highlightMatch(feature.name, query)}</span>
            <span class="settings-feat-desc">${highlightMatch(feature.description, query)}</span>
          </div>
          <span class="settings-feat-pill ${pillClass}">${pillLabel}</span>
          <span class="settings-feat-chevron">&#x25B8;</span>
        </div>
        <div class="settings-feat-body">
          <div class="settings-feat-cat-tag">${escapeHtml(catLabel)}</div>
          ${secretRows}
        </div>
      </div>
    `;
  }).join('');

  setTrustedHtml(area, trustedHtml(`
    <div class="settings-section-header">
      <h2>Search results for "${escapeHtml(query)}"</h2>
    </div>
    <div class="settings-feat-list">${cards}</div>
  `, "legacy direct innerHTML migration"));

  initFeatureSectionListeners(area);
}

// ── Init ──

async function initSettingsWindow(): Promise<void> {
  await initI18n();
  applyStoredTheme();
  applyFont();

  // Localize the static HTML shell (settings.html) — labels are baked in
  // English so the page paints something before this script runs; once
  // i18n is ready we swap them to the user's locale.
  document.title = t('modals.settingsWindow.shellTitle');
  const headerTitle = document.querySelector('.settings-header-title');
  if (headerTitle) headerTitle.textContent = t('modals.settingsWindow.shellTitle');
  const searchInputEl = document.getElementById('settingsSearch') as HTMLInputElement | null;
  if (searchInputEl) searchInputEl.placeholder = t('modals.settingsWindow.shellSearchPlaceholder');
  const cancelEl = document.getElementById('cancelBtn');
  if (cancelEl) cancelEl.textContent = t('modals.settingsWindow.shellCancel');
  const okEl = document.getElementById('okBtn');
  if (okEl) okEl.textContent = t('modals.settingsWindow.shellSaveClose');

  try { await resolveLocalApiPort(); } catch { /* use default */ }

  requestAnimationFrame(() => {
    document.documentElement.classList.remove('no-transition');
  });

  await loadDesktopSecrets();
  settingsManager = new SettingsManager();

  renderSection('overview');

  document.getElementById('sidebarNav')?.addEventListener('click', (e) => {
    const btn = (e.target as HTMLElement).closest<HTMLButtonElement>('[data-section]');
    if (btn?.dataset.section) {
      renderSection(btn.dataset.section);
    }
  });

  const searchInput = document.getElementById('settingsSearch') as HTMLInputElement | null;
  let searchTimeout: ReturnType<typeof setTimeout>;
  searchInput?.addEventListener('input', () => {
    clearTimeout(searchTimeout);
    searchTimeout = setTimeout(() => handleSearch(searchInput.value), 200);
  });

  document.getElementById('okBtn')?.addEventListener('click', () => {
    void (async () => {
      try {
        const wmKeyInput = document.querySelector<HTMLInputElement>('[data-wm-key-input]');
        const wmKeyValue = wmKeyInput?.value.trim();
        const hasWmKeyChange = !!(wmKeyValue && wmKeyValue !== MASKED_SENTINEL && wmKeyValue.length > 0);

        const contentArea = document.getElementById('contentArea');
        if (contentArea) settingsManager.captureUnsavedInputs(contentArea);

        const hasPending = settingsManager.hasPendingChanges();
        if (!hasPending && !hasWmKeyChange) {
          closeSettingsWindow();
          return;
        }

        if (hasWmKeyChange && wmKeyValue) {
          await setSecretValue('WORLDMONITOR_API_KEY', wmKeyValue);
        }

        if (hasPending) {
          setActionStatus(t('modals.settingsWindow.validating'), 'ok');
          const missingRequired = settingsManager.getMissingRequiredSecrets();
          if (missingRequired.length > 0) {
            setActionStatus(`Missing required: ${missingRequired.join(', ')}`, 'error');
            return;
          }
          const errors = await settingsManager.verifyPendingSecrets();
          if (errors.length > 0) {
            setActionStatus(t('modals.settingsWindow.verifyFailed', { errors: errors.join(', ') }), 'error');
            return;
          }
          await settingsManager.commitVerifiedSecrets();
        }

        setActionStatus(t('modals.settingsWindow.saved'), 'ok');
        closeSettingsWindow();
      } catch (err) {
        console.error('[settings] save error:', err);
        setActionStatus(t('modals.settingsWindow.failed', { error: String(err) }), 'error');
      }
    })();
  });

  document.getElementById('cancelBtn')?.addEventListener('click', () => {
    closeSettingsWindow();
  });

  window.addEventListener('beforeunload', () => {
    settingsManager.destroy();
  });
}

localStorage.setItem('wm-settings-open', '1');
window.addEventListener('beforeunload', () => localStorage.removeItem('wm-settings-open'));

void initSettingsWindow();
