import { t, toggleLang, applyTranslations } from './i18n.js';

// ── Tauri invoke shim ────────────────────────────────────────────────────────
let invoke = () => Promise.resolve(null);
if (window.__TAURI__?.core) {
  invoke = window.__TAURI__.core.invoke;
}

// ── JSONC parsing ─────────────────────────────────────────────────────────────
// config.json is JSONC: save_config prepends a `// OSTP Configuration` header
// and the default template carries comments. Strip // line and /* */ block
// comments before JSON.parse, while preserving them inside string literals
// (config values contain URLs like https:// and paths like ./wintun.dll).
function parseJsonc(raw) {
  let out = '';
  let inStr = false, inLine = false, inBlock = false, escaped = false;
  for (let i = 0; i < raw.length; i++) {
    const c = raw[i], n = raw[i + 1];
    if (inLine) { if (c === '\n') { inLine = false; out += c; } continue; }
    if (inBlock) { if (c === '*' && n === '/') { inBlock = false; i++; } continue; }
    if (inStr) {
      out += c;
      if (escaped) escaped = false;
      else if (c === '\\') escaped = true;
      else if (c === '"') inStr = false;
      continue;
    }
    if (c === '"') { inStr = true; out += c; continue; }
    if (c === '/' && n === '/') { inLine = true; i++; continue; }
    if (c === '/' && n === '*') { inBlock = true; i++; continue; }
    out += c;
  }
  return JSON.parse(out);
}

// ── State ────────────────────────────────────────────────────────────────────
let appState    = 'disconnected'; // 'disconnected' | 'connecting' | 'connected'
let pollTimer   = null;
let uptimeTimer = null;
let uptimeSecs  = 0;
let rawConfig   = null;           // parsed config.json object
let serverAddr  = '';             // current server address (for badge)

// ── DOM refs ─────────────────────────────────────────────────────────────────
const $ = id => document.getElementById(id);

const homeScreen     = $('home-screen');
const settingsScreen = $('settings-screen');
const btnConnect     = $('btn-connect');
const orbitWrap      = $('orbit-wrap');
const brandDot       = $('brand-dot');
const statusLabel    = $('status-text');
const statusSub      = $('uptime-text');
const connInfo       = $('connection-info');
const serverBadgeTxt = $('server-badge-text');
const metricDown     = $('metric-down');
const metricUp       = $('metric-up');
const pingValueTxt   = $('ping-text-value');
const btnTestPing    = $('btn-test-ping');
const toast          = $('toast');

const btnGoSettings  = $('btn-go-settings');
const btnAutoConnect = $('btn-auto-connect');
const btnBack        = $('btn-back');
const btnImport      = $('btn-import-url');
const btnPeekKey     = $('btn-peek-key');
const importInput    = $('in-import-url');
const inServer       = $('in-server');
const inKey          = $('in-key');
const inSocks        = $('in-socks');
const inDns          = $('in-dns');

const groupCustomDns = $('group-custom-dns');
const inTransport    = $('in-transport');
const groupDnsProxy  = $('group-dns-proxy');
const inDnsDomain    = $('in-dns-domain');
const inDnsRegion    = $('in-dns-region');
const inMtu          = $('in-mtu');
const inTun          = $('in-tun-mode');
const inKillSwitch   = $('in-kill-switch');
const inMux          = $('in-mux-mode');
const inMuxSessions  = $('in-mux-sessions');
const inDebug          = $('in-debug');
const inAutoconnect    = $('in-autoconnect');
const inLaunchStartup  = $('in-launch-startup');

function bindSettingsInputs() {
  const ids = [
    'in-server', 'in-key', 'in-socks', 'in-dns',
    'in-transport', 'in-dns-domain', 'in-dns-region',
    'in-mtu', 'in-mux-sessions',
    'in-tun-mode', 'in-kill-switch', 'in-mux-mode',
    'in-debug', 'in-autoconnect', 'in-launch-startup'
  ];
  ids.forEach(id => {
    const el = $(id);
    if (el) el.addEventListener('change', scheduleAutoSave);
    if (el && el.type === 'text') el.addEventListener('input', scheduleAutoSave);
    if (el && el.type === 'password') el.addEventListener('input', scheduleAutoSave);
  });

  if (inTransport) {
    inTransport.addEventListener('change', () => {
      if (inTransport.value === 'dns') {
        groupDnsProxy.style.display = 'flex';
      } else {
        groupDnsProxy.style.display = 'none';
      }
    });
  }
}

const wintunModal        = $('wintun-modal');
const btnWintunCancel    = $('btn-wintun-cancel');
const btnWintunOpen      = $('btn-wintun-open');
const wintunInstallPath  = $('wintun-install-path');

const dnsProberModal     = $('dns-prober-modal');
const proberStatus       = $('prober-status');
const proberList         = $('prober-list');
const btnProberClose     = $('btn-prober-close');
const btnDnsProber       = $('btn-dns-prober');

// ── DNS Prober ───────────────────────────────────────────────────────────────
async function openDnsProber() {
  dnsProberModal.classList.remove('hidden');
  proberList.innerHTML = '';
  proberStatus.textContent = 'Running probes...';

  const domain = inDnsDomain?.value?.trim() || 'example.com';

  let results;
  try {
    results = await invoke('run_dns_prober', { domain });
  } catch (err) {
    proberStatus.textContent = 'Error: ' + err;
    return;
  }

  proberList.innerHTML = '';

  if (!results || results.length === 0) {
    proberStatus.textContent = 'No results.';
    return;
  }

  let bestIp = null;

  results.forEach((r, i) => {
    const isBest = i === 0 && r.latency_ms != null;
    if (isBest && !bestIp) bestIp = r.ip;

    const row = document.createElement('div');
    row.style.cssText = `
      display: flex; align-items: center; justify-content: space-between;
      padding: 6px 10px; border-radius: 6px; cursor: pointer;
      background: ${isBest ? 'rgba(99,179,237,0.12)' : 'rgba(255,255,255,0.04)'};
      border: 1px solid ${isBest ? 'rgba(99,179,237,0.35)' : 'transparent'};
      transition: background 0.15s;
    `;

    const latText = r.latency_ms != null ? `${r.latency_ms} ms` : 'TIMEOUT';
    const latColor = r.latency_ms == null ? '#f56565'
      : r.latency_ms < 50 ? '#68d391'
      : r.latency_ms < 150 ? '#f6e05e'
      : '#fc8181';

    row.innerHTML = `
      <span style="font-size:0.78rem; color: var(--c-txt-1);">${isBest ? '⭐ ' : ''}${r.name}</span>
      <span style="font-size:0.78rem; color: var(--c-txt-2);">${r.ip}</span>
      <span style="font-size:0.78rem; font-weight:600; color:${latColor};">${latText}</span>
    `;

    if (r.latency_ms != null) {
      row.addEventListener('click', () => {
        inDnsRegion.value = r.ip;
        scheduleAutoSave();
        dnsProberModal.classList.add('hidden');
        showToast('DNS server set to ' + r.ip, 'ok');
      });
      row.addEventListener('mouseenter', () => { row.style.background = 'rgba(99,179,237,0.18)'; });
      row.addEventListener('mouseleave', () => { row.style.background = isBest ? 'rgba(99,179,237,0.12)' : 'rgba(255,255,255,0.04)'; });
    }

    proberList.appendChild(row);
  });

  if (bestIp) {
    proberStatus.textContent = `✓ Best: ${bestIp} — click any row to select`;
    // Auto-fill best
    inDnsRegion.value = bestIp;
    scheduleAutoSave();
  } else {
    proberStatus.textContent = 'All servers timed out.';
  }
}

// ── Tag-input state ───────────────────────────────────────────────────────────
// Map of tagId -> Set<string>
const tagState = {
  domains:   new Set(),
  ips:       new Set(),
  processes: new Set(),
};

function renderTagList(key) {
  const list = $('tag-list-' + key);
  if (!list) return;
  list.innerHTML = '';
  for (const val of tagState[key]) {
    const chip = document.createElement('span');
    chip.className = 'tag-chip';
    chip.innerHTML = `${val}<button class="tag-chip-remove" title="Remove" tabindex="-1"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>`;
    chip.querySelector('.tag-chip-remove').addEventListener('click', () => {
      tagState[key].delete(val);
      renderTagList(key);
      scheduleAutoSave();
    });
    list.appendChild(chip);
  }
}

function addTag(key, raw) {
  const vals = raw.split(/[\s,;]+/).map(v => v.trim()).filter(Boolean);
  let added = false;
  for (const v of vals) {
    if (!tagState[key].has(v)) { tagState[key].add(v); added = true; }
  }
  if (added) { renderTagList(key); scheduleAutoSave(); }
}

function wireTagInput(key) {
  const input = $('tag-input-' + key);
  const wrap  = $('tag-wrap-' + key);
  if (!input) return;
  input.addEventListener('keydown', e => {
    if (e.key === 'Enter' || e.key === ',') {
      e.preventDefault();
      const v = input.value.trim();
      if (v) { addTag(key, v); input.value = ''; }
    } else if (e.key === 'Backspace' && !input.value) {
      const arr = [...tagState[key]];
      if (arr.length) { tagState[key].delete(arr[arr.length - 1]); renderTagList(key); scheduleAutoSave(); }
    }
  });
  input.addEventListener('paste', e => {
    e.preventDefault();
    const text = (e.clipboardData || window.clipboardData).getData('text');
    addTag(key, text);
    input.value = '';
  });
  // click on wrap focuses input
  if (wrap) wrap.addEventListener('click', () => input.focus());
}

// ── Utilities ────────────────────────────────────────────────────────────────
function fmtBytes(b) {
  if (!b || b === 0) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.min(Math.floor(Math.log2(b) / 10), 4);
  return (b / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1) + ' ' + units[i];
}

function fmtTime(s) {
  const h = Math.floor(s / 3600);
  const m = Math.floor((s % 3600) / 60);
  const sec = s % 60;
  const pad = n => String(n).padStart(2, '0');
  return h > 0
    ? `${h}:${pad(m)}:${pad(sec)}`
    : `${pad(m)}:${pad(sec)}`;
}

function splitLines(val) {
  return val.split('\n').map(l => l.trim()).filter(Boolean);
}

// ── Theme ────────────────────────────────────────────────────────────────────
function applyTheme(theme) {
  document.documentElement.setAttribute('data-theme', theme);
  localStorage.setItem('ostp-theme', theme);
}

function toggleTheme() {
  const current = document.documentElement.getAttribute('data-theme');
  applyTheme(current === 'light' ? 'dark' : 'light');
}

// Apply saved theme immediately (before any paint)
(function() {
  const saved = localStorage.getItem('ostp-theme') || 'dark';
  document.documentElement.setAttribute('data-theme', saved);
})();
// ── Toast ────────────────────────────────────────────────────────────────────
let toastTimer = null;
function showToast(msg, variant = '') {
  toast.textContent = msg;
  toast.className = 'toast show' + (variant ? ' is-' + variant : '');
  clearTimeout(toastTimer);
  toastTimer = setTimeout(() => {
    toast.classList.remove('show');
  }, 2400);
}

// ── DNS & Kill Switch visibility ──────────────────────────────────────────────


function updateKillSwitchVisibility() {
  const group = $('group-kill-switch');
  if (!group || !inTun) return;
  group.style.display = inTun.checked ? 'flex' : 'none';
}


// ── State machine ────────────────────────────────────────────────────────────
function setState(next) {
  if (appState === next) return;
  appState = next;

  // Reset all dynamic classes
  btnConnect.className = 'power-btn';
  orbitWrap.className  = 'orbit-wrap';
  brandDot.className   = 'brand-dot';
  statusLabel.className = 'status-label';

  if (next === 'disconnected') {
    statusLabel.textContent = t('status_disconnected');
    statusSub.textContent   = t('hint_tap');
    connInfo.classList.add('hidden');
    metricDown.textContent  = '0 B';
    metricUp.textContent    = '0 B';
    pingValueTxt.textContent = 'Target Ping: -- ms';
    pingValueTxt.className = 'ping-test-value';
    clearInterval(pollTimer);
    clearInterval(uptimeTimer);
    pollTimer = uptimeTimer = null;
    uptimeSecs = 0;

  } else if (next === 'connecting') {
    btnConnect.classList.add('connecting');
    orbitWrap.classList.add('connecting');
    brandDot.classList.add('connecting');
    statusLabel.classList.add('is-connecting');
    statusLabel.textContent = t('status_connecting');
    statusSub.textContent   = t('hint_connecting');
    connInfo.classList.add('hidden');
    clearInterval(uptimeTimer);
    uptimeTimer = null;
    uptimeSecs = 0;

  } else if (next === 'connected') {
    btnConnect.classList.add('connected');
    orbitWrap.classList.add('connected');
    brandDot.classList.add('connected');
    statusLabel.classList.add('is-connected');
    statusLabel.textContent = t('status_connected');

    // Show connection info
    connInfo.classList.remove('hidden');

    // Start uptime counter
    if (!uptimeTimer) {
      uptimeSecs = 0;
      statusSub.textContent = fmtTime(uptimeSecs);
      uptimeTimer = setInterval(() => {
        uptimeSecs++;
        statusSub.textContent = fmtTime(uptimeSecs);
      }, 1000);
    }
  }
}

// ── Polling ──────────────────────────────────────────────────────────────────
async function poll() {
  if (!pollTimer) return;
  try {
    const code = await invoke('get_tunnel_status');
    if (!pollTimer) return; // Prevent race condition if disconnected during await
    console.log('[OSTP] poll status code:', code);
    
    if      (code === 0) { setState('disconnected'); return; }
    else if (code === 1)   setState('connecting');
    else if (code === 2)   setState('connected');

    const metrics = await invoke('get_metrics');
    if (metrics && pollTimer) {
      metricDown.textContent = fmtBytes(metrics.bytes_recv);
      metricUp.textContent   = fmtBytes(metrics.bytes_sent);
      if (metrics.rtt_ms > 0 && pingValueTxt.textContent !== 'Testing...') {
        const rtt = metrics.rtt_ms;
        pingValueTxt.textContent = `Target Ping: ${rtt} ms`;
        pingValueTxt.className = 'ping-test-value ' + (rtt < 80 ? 'good' : rtt < 200 ? 'warn' : 'bad');
      }
    }
  } catch (err) {
    console.error('[OSTP] poll threw:', err);
    if (pollTimer) {
      setState('disconnected');
      showToast(String(err), 'error');
      alert('[OSTP POLL ERROR] ' + String(err));
    }
  }
}

function startPolling() {
  clearInterval(pollTimer);
  poll();
  pollTimer = setInterval(poll, 1000);
}

// ── Connect / Disconnect ─────────────────────────────────────────────────────
async function handleToggle() {
  if (appState === 'disconnected') {
    try {
      const raw = await invoke('get_config');
      const cfg = parseJsonc(raw);
      serverAddr = cfg.server || '';
    } catch { serverAddr = ''; }

    setState('connecting');

    try {
      console.log('[OSTP] invoking start_tunnel...');
      const ok = await invoke('start_tunnel');
      console.log('[OSTP] start_tunnel returned:', ok);
      if (ok) {
        startPolling();
      } else {
        setState('disconnected');
        showToast(t('toast_error') || 'Failed to connect', 'error');
        alert('[OSTP] start_tunnel returned false');
      }
    } catch (err) {
      console.error('[OSTP] start_tunnel threw:', err);
      setState('disconnected');
      if (String(err).includes("WINTUN_MISSING")) {
        if (wintunModal) wintunModal.classList.remove('hidden');
      } else {
        showToast(String(err), 'error');
        alert('[OSTP ERROR] ' + String(err));
      }
    }
  } else {
    setState('disconnected');
    try { await invoke('stop_tunnel'); } catch { /* ignore */ }
    showToast(t('toast_disconnected') || 'Disconnected');
  }
}

// ── Screen navigation ────────────────────────────────────────────────────────
function showScreen(name) {
  if (name === 'settings') {
    loadConfigIntoForm();
    homeScreen.classList.remove('active');
    settingsScreen.classList.add('active');
  } else {
    settingsScreen.classList.remove('active');
    homeScreen.classList.add('active');
  }
}

// ── Config — load ─────────────────────────────────────────────────────────────
async function loadConfigIntoForm() {
  try {
    const raw = await invoke('get_config');
    rawConfig = parseJsonc(raw);
    const c = rawConfig.mode === 'client' ? rawConfig : null;
    if (!c) return;

    if (c.version === '0.3.1' || c.outbounds !== undefined) {
      // NEW FORMAT
      const ostpOut = (c.outbounds || []).find(o => o.type === 'ostp');
      if (ostpOut) {
        inServer.value = ostpOut.server ? `${ostpOut.server}:${ostpOut.port || 50000}` : '';
        inKey.value = ostpOut.access_key || '';
        inTransport.value = ostpOut.transport?.type || 'udp';
        if (inTransport.value === 'dns') {
          groupDnsProxy.style.display = 'flex';
          inDnsDomain.value = ostpOut.transport?.domain || '';
          inDnsRegion.value = ostpOut.transport?.resolver || 'Global';
        } else {
          groupDnsProxy.style.display = 'none';
        }
        inMux.checked   = !!ostpOut.multiplex?.enabled;
        inMuxSessions.value = ostpOut.multiplex?.sessions || '';
      }

      const tunIn = (c.inbounds || []).find(i => i.type === 'tun');
      if (tunIn) {
        inTun.checked = true;
        inMtu.value = tunIn.mtu || '';
      } else {
        inTun.checked = false;
      }
      
      const socksIn = (c.inbounds || []).find(i => i.type === 'local_proxy');
      if (socksIn) {
        inSocks.value = `${socksIn.listen || '127.0.0.1'}:${socksIn.port || 1088}`;
      }

      inDns.value = ''; // DNS handling is manual in routing now, ignore here
      if (inKillSwitch) inKillSwitch.checked = !!c.gui?.kill_switch;
      inDebug.checked = c.log?.level === 'debug';
      
      const ex = c.routing?.rules || [];
      const doms = new Set();
      const ips = new Set();
      const procs = new Set();
      ex.forEach(r => {
         if (r.outbound === 'direct') {
             if (r.domain_suffix) r.domain_suffix.forEach(d => doms.add(d));
             if (r.ip_cidr) r.ip_cidr.forEach(ip => ips.add(ip));
             if (r.process_name) r.process_name.forEach(p => procs.add(p));
         }
      });
      tagState.domains = doms;
      tagState.ips = ips;
      tagState.processes = procs;

    } else {
      // OLD FORMAT
      inServer.value  = c.server        || '';
      inKey.value     = c.access_key    || '';
      inSocks.value   = c.socks5_bind   || '127.0.0.1:1088';
      inTransport.value = c.transport?.mode || 'udp';
      if (inTransport.value === 'dns') {
        groupDnsProxy.style.display = 'block';
      } else {
        groupDnsProxy.style.display = 'none';
      }

      inMtu.value     = c.mtu           || '';
      inTun.checked   = !!c.tun?.enable;
      if (inKillSwitch) inKillSwitch.checked = !!c.tun?.kill_switch;
      inMux.checked   = !!c.mux?.enabled;
      inMuxSessions.value = c.mux?.sessions || '';
      
      inDns.value = c.tun?.dns || '';
      inDebug.checked = !!c.debug;

      const ex = c.exclude || {};
      tagState.domains   = new Set(ex.domains   || []);
      tagState.ips       = new Set(ex.ips       || []);
      tagState.processes = new Set(ex.processes || []);
    }

    if (inAutoconnect) inAutoconnect.checked = !!c.gui?.autoconnect;
    if (inLaunchStartup) inLaunchStartup.checked = !!c.gui?.launch_startup;

    updateKillSwitchVisibility();
    renderTagList('domains');
    renderTagList('ips');
    renderTagList('processes');
  } catch (err) {
    showToast(String(err), 'error');
  }
}

// ── Config — save ─────────────────────────────────────────────────────────────
let autoSaveTimer = null;
function scheduleAutoSave() {
  clearTimeout(autoSaveTimer);
  autoSaveTimer = setTimeout(() => handleSave(true), 600);
}

async function handleSave(silent = false) {
  if (!rawConfig) rawConfig = { mode: 'client', log_level: 'info' };

  const server = inServer.value.trim();
  const key    = inKey.value.trim();

  if (!server) { if (!silent) showToast(t('err_server_req') || 'Server address required', 'error'); return; }
  if (!key)    { if (!silent) showToast(t('err_key_req')    || 'Access key required',     'error'); return; }

  if (inLaunchStartup) {
    try { await invoke('set_autostart', { enable: inLaunchStartup.checked }); } catch (err) { console.error('autostart error', err); }
  }

  const sHost = server.includes(':') ? server.substring(0, server.lastIndexOf(':')) : server;
  const sPort = server.includes(':') ? parseInt(server.substring(server.lastIndexOf(':') + 1), 10) : 50000;

  const socksStr = inSocks.value.trim() || '127.0.0.1:1088';
  const socksHost = socksStr.includes(':') ? socksStr.substring(0, socksStr.lastIndexOf(':')) : '127.0.0.1';
  const socksPort = socksStr.includes(':') ? parseInt(socksStr.substring(socksStr.lastIndexOf(':') + 1), 10) : 1088;

  const inbounds = [];
  inbounds.push({
    type: "local_proxy",
    tag: "socks-in",
    protocol: "socks",
    listen: socksHost,
    port: socksPort
  });

  if (inTun.checked) {
    inbounds.push({
      type: "tun",
      tag: "tun-in",
      auto_route: !(inKillSwitch && inKillSwitch.checked), 
      mtu: parseInt(inMtu.value, 10) || 1140
    });
  }

  const outbounds = [
    {
      type: "ostp",
      tag: "proxy",
      server: sHost,
      port: sPort,
      access_key: key,
      transport: {
        type: inTransport.value,
        domain: inTransport.value === 'dns' ? inDnsDomain.value.trim() : undefined,
        resolver: inTransport.value === 'dns' ? inDnsRegion.value : undefined
      },
      multiplex: inMux.checked ? {
        enabled: true,
        sessions: parseInt(inMuxSessions.value, 10) || 1
      } : { enabled: false, sessions: 1 }
    },
    { type: "direct", tag: "direct" },
    { type: "block", tag: "block" }
  ];

  const rules = [];
  if (tagState.domains.size > 0) rules.push({ domain_suffix: Array.from(tagState.domains), outbound: "direct" });
  if (tagState.ips.size > 0) rules.push({ ip_cidr: Array.from(tagState.ips), outbound: "direct" });
  if (tagState.processes.size > 0) rules.push({ process_name: Array.from(tagState.processes), outbound: "direct" });

  if (inKillSwitch && inKillSwitch.checked && inTun.checked) {
      rules.push({ ip_cidr: ["0.0.0.0/0", "::/0"], outbound: "proxy" });
  }

  rawConfig = {
    mode: 'client',
    version: '0.3.1',
    log: { level: inDebug.checked ? 'debug' : 'info' },
    inbounds,
    outbounds,
    routing: { rules, default_outbound: "proxy" },
    gui: rawConfig.gui || {},
    api: rawConfig.api || undefined
  };

  if (inAutoconnect) rawConfig.gui.autoconnect = inAutoconnect.checked;
  if (inLaunchStartup) rawConfig.gui.launch_startup = inLaunchStartup.checked;
  if (inKillSwitch) rawConfig.gui.kill_switch = inKillSwitch.checked;

  try {
    const ok = await invoke('save_config', { jsonContent: JSON.stringify(rawConfig, null, 2) });
    if (!ok && !silent) {
      showToast(t('toast_error'), 'error');
    } else if (ok && appState === 'connected') {
      // Hot-reload exclusions into the running tunnel (no reconnect needed)
      try { await invoke('reload_tunnel'); } catch { /* ignore */ }
    }
  } catch (err) {
    if (!silent) showToast(String(err), 'error');
  }
}

// ── Import share link ─────────────────────────────────────────────────────────
function handleImport() {
  const raw = importInput.value.trim();
  if (!raw) return;
  try {
    if (!raw.startsWith('ostp://')) throw new Error('Link must start with ostp://');
    const url = new URL(raw);
    const key  = decodeURIComponent(url.username);
    const host = url.host;
    if (!key || !host) throw new Error('Incomplete link parameters');
    inServer.value = host;
    inKey.value    = key;
    inTransport.value = 'udp';
    groupDnsProxy.style.display = 'none';
    
    const type = url.searchParams.get('type');
    if (type === 'tcp' || type === 'http') inTransport.value = 'uot';
    else inTransport.value = 'udp';
    
    importInput.value = '';
    showToast(t('toast_imported'), 'ok');
    handleSave(false);
  } catch (err) {
    showToast(err.message, 'error');
  }
}

// ── Peek key ──────────────────────────────────────────────────────────────────
let peeking = false;
function togglePeek() {
  peeking = !peeking;
  inKey.type = peeking ? 'text' : 'password';
  btnPeekKey.style.color = peeking
    ? 'var(--c-accent)'
    : 'var(--c-txt-3)';
}

// ── Init ──────────────────────────────────────────────────────────────────────
window.addEventListener('DOMContentLoaded', async () => {
  applyTranslations();
  setState('disconnected');
  updateKillSwitchVisibility();
  bindSettingsInputs();

  // Event wiring
  if (window.__TAURI__ && window.__TAURI__.event) {
    window.__TAURI__.event.listen('tunnel-error', (evt) => {
      setState('disconnected');
      const errStr = String(evt.payload);
      showToast(errStr, 'error');
      alert(errStr);
    });
  }

  // Load wintun install path for modal instruction
  if (wintunInstallPath) {
    try {
      const p = await invoke('get_wintun_install_path');
      if (p) wintunInstallPath.textContent = p;
    } catch { /* ignore */ }
  }

  // Auto-connect on startup
  try {
    const raw = await invoke('get_config');
    rawConfig = parseJsonc(raw);
    if (rawConfig?.gui?.autoconnect) {
      setTimeout(() => {
        if (appState === 'disconnected') handleToggle();
      }, 800);
    }
  } catch (err) {
    console.error('Failed to load config on startup', err);
  }

  btnConnect.addEventListener('click',       handleToggle);
  
  if (btnAutoConnect) {
    btnAutoConnect.addEventListener('click', async () => {
      if (appState !== 'disconnected') {
        showToast('Disconnect first to run Auto mode', 'error');
        return;
      }
      
      try {
        const raw = await invoke('get_config');
        rawConfig = parseJsonc(raw);
      } catch {
        showToast('Please save your config first', 'error');
        return;
      }

      showToast('Starting Auto search...', 'ok');
      try {
        const mtus = [1500, 1350, 1280];
        const modes = [
          { t: 'udp', w: false, r: false },
          { t: 'uot', w: false, r: false },
          { t: 'uot', w: true, r: false },
          { t: 'uot', w: false, r: true }
        ];

        for (let mode of modes) {
          for (let mtu of mtus) {
            showToast(`Testing: ${mode.t} | WSS: ${mode.w} | XTLS: ${mode.r} | MTU: ${mtu}`);
            
            const ostpOut = (rawConfig.outbounds || []).find(o => o.type === 'ostp');
            if (ostpOut) {
              ostpOut.transport = ostpOut.transport || { type: 'udp' };
              ostpOut.transport.type = mode.t;
              ostpOut.transport.wss = mode.w ? true : undefined;
            } else {
              rawConfig.transport = { mode: mode.t, wss: mode.w };
            }
            
            const tunIn = (rawConfig.inbounds || []).find(i => i.type === 'tun');
            if (tunIn) {
               tunIn.mtu = mtu;
            } else {
               rawConfig.mtu = mtu;
            }
            


            await invoke('save_config', { jsonContent: JSON.stringify(rawConfig, null, 2) });
            
            setState('connecting');
            const ok = await invoke('start_tunnel');
            if (ok) {
              startPolling();
              // Wait a bit to see if it stays connected and ping works
              await new Promise(r => setTimeout(r, 3000));
              try {
                const metrics = await invoke('get_metrics');
                if (metrics && metrics.rtt_ms > 0) {
                  showToast(`Success! Found working config: ${mode.t} (MTU ${mtu})`, 'ok');
                  return; // Stop on first working
                }
              } catch {}
              
              // If we are here, ping failed, so stop and try next
              await invoke('stop_tunnel');
              setState('disconnected');
            }
          }
        }
        showToast('Auto search finished. No working config found.', 'error');
      } catch (err) {
        showToast('Error during auto-connect: ' + String(err), 'error');
      }
    });
  }

  btnGoSettings.addEventListener('click',    () => showScreen('settings'));
  btnBack.addEventListener('click',          () => showScreen('home'));
  btnImport.addEventListener('click',        handleImport);
  btnPeekKey.addEventListener('click',       togglePeek);

  // Theme toggle
  const btnThemeToggle = $('btn-theme-toggle');
  if (btnThemeToggle) {
    btnThemeToggle.addEventListener('click', toggleTheme);
  }
  scheduleAutoSave();
  inTun.addEventListener('change', () => {
    updateKillSwitchVisibility();
    scheduleAutoSave();
  });
  importInput.addEventListener('keydown', e => { if (e.key === 'Enter') handleImport(); });

  // Auto-save wiring for standard form elements (excluding tag-inputs which wire themselves)
  const formInputs = document.querySelectorAll('#settings-screen input:not(#in-import-url):not(.tag-input-field), #settings-screen select');
  formInputs.forEach(el => {
    el.addEventListener('input', scheduleAutoSave);
    el.addEventListener('change', scheduleAutoSave);
  });

  // Wire tag inputs
  wireTagInput('domains');
  wireTagInput('ips');
  wireTagInput('processes');

  btnTestPing.addEventListener('click', runPingTest);

  btnWintunCancel.addEventListener('click', () => {
    wintunModal.classList.add('hidden');
  });

  // DNS Prober modal
  if (btnDnsProber) {
    btnDnsProber.addEventListener('click', openDnsProber);
  }
  if (btnProberClose) {
    btnProberClose.addEventListener('click', () => {
      dnsProberModal.classList.add('hidden');
    });
  }
  // Close prober on backdrop click
  if (dnsProberModal) {
    dnsProberModal.addEventListener('click', (e) => {
      if (e.target === dnsProberModal) dnsProberModal.classList.add('hidden');
    });
  }

  // Open wintun.net link — handled natively by <a target="_blank">, but also wire as fallback
  if (btnWintunOpen && window.__TAURI__) {
    btnWintunOpen.addEventListener('click', (e) => {
      e.preventDefault();
      const opener = window.__TAURI__?.opener || window.__TAURI__?.shell;
      if (opener && opener.open) {
        opener.open('https://www.wintun.net');
      } else {
        window.open('https://www.wintun.net', '_blank');
      }
    });
  }

  async function runPingTest() {
    pingValueTxt.textContent = 'Testing...';
    pingValueTxt.className = 'ping-test-value';
    try {
      const metrics = await invoke('get_metrics');
      if (metrics && metrics.rtt_ms > 0) {
        const rtt = metrics.rtt_ms;
        pingValueTxt.textContent = `Target Ping: ${rtt} ms`;
        if (rtt < 80) pingValueTxt.classList.add('good');
        else if (rtt < 200) pingValueTxt.classList.add('warn');
        else pingValueTxt.classList.add('bad');
      } else {
        pingValueTxt.textContent = 'Target Ping: -- ms';
      }
    } catch {
      pingValueTxt.textContent = 'Target Ping: Error';
    }
  }

  // Restore status on app open
  try {
    const code = await invoke('get_tunnel_status');
    if (code > 0) startPolling();
  } catch { /* not in Tauri context */ }

  if (window.__TAURI__?.event) {
    const { listen } = window.__TAURI__.event;
    listen('tray_connect', () => {
      if (appState === 'disconnected') handleToggle();
    });
    listen('tray_disconnect', () => {
      if (appState !== 'disconnected') handleToggle();
    });
  }
});
