/**
 * KI-Shield Proxy — Popup v2.2.
 * Zugang, direkter Schutzstatus, PII-Kategorien und lokale Datenverwaltung.
 */

const Core = globalThis.KIShieldCore;
if (!Core) throw new Error('KIShieldCore wurde nicht geladen');

const LIC = 'https://kishieldguard.de/license/plugin';
const K = {
  API: 'kishield_api_key',
  TOKEN: 'kishield_customer_token',
  EMAIL: 'kishield_email',
  PENDING: 'kishield_pending',
  DEVICE: 'kishield_device_id',
  TRIAL_END: 'kishield_trial_end',
  VALID_UNTIL: 'kishield_valid_until',
  STATUS: 'kishield_status',
  SUB_STATUS: 'kishield_subscription_status',
  PLAN: 'kishield_plan',
  ENABLED: 'kishield_enabled',
  LIVE: 'kishield_live_detection',
  DISABLED: 'kishield_disabled_types',
  NEVER: 'kishield_never_terms',
};

const $ = (id) => document.getElementById(id);
const body = () => $('access-body');
const esc = (s) => String(s == null ? '' : s).replace(/[&<>"']/g, (c) => ({
  '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
}[c]));

function fb(msg, kind) {
  const el = $('access-feedback');
  el.textContent = msg || '';
  el.className = 'feedback' + (kind ? ' ' + kind : '');
}
function settingsFeedback(msg, kind) {
  const el = $('settings-feedback');
  el.textContent = msg || '';
  el.className = 'feedback' + (kind ? ' ' + kind : '');
}
async function get(keys) {
  try { return await chrome.storage.local.get(keys); } catch (_) { return {}; }
}
async function set(obj) {
  await chrome.storage.local.set(obj);
}
async function del(keys) {
  await chrome.storage.local.remove(keys);
}

async function deviceId() {
  const result = await get(K.DEVICE);
  if (result[K.DEVICE]) return result[K.DEVICE];
  const id = (crypto.randomUUID && crypto.randomUUID()) ||
    [...crypto.getRandomValues(new Uint8Array(16))]
      .map((byte) => byte.toString(16).padStart(2, '0')).join('');
  await set({ [K.DEVICE]: id });
  return id;
}

async function api(path, method, bodyObj) {
  const ctrl = new AbortController();
  const timeout = setTimeout(() => ctrl.abort(), 10000);
  try {
    const options = {
      method,
      headers: { 'Content-Type': 'application/json' },
      signal: ctrl.signal,
    };
    if (bodyObj) options.body = JSON.stringify(bodyObj);
    const response = await fetch(LIC + path, options);
    let data;
    try { data = await response.json(); }
    catch (_) { data = { ok: false, reason: 'bad_response' }; }
    if (!response.ok && data && data.ok !== true) {
      return { ...data, ok: false, reason: data.reason || `http_${response.status}` };
    }
    return data;
  } finally {
    clearTimeout(timeout);
  }
}

function fmtDate(iso) {
  if (!iso) return '';
  const date = new Date(iso);
  return Number.isNaN(date.getTime())
    ? iso
    : date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
}
function daysLeft(iso) {
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) return null;
  return Math.max(0, Math.ceil((date.getTime() - Date.now()) / 86400000));
}

const REASONS = {
  invalid_email: 'Bitte eine gültige E-Mail-Adresse eingeben.',
  disposable_email: 'Wegwerf-Adressen sind nicht zulässig.',
  avv_required: 'Bitte dem Auftragsverarbeitungsvertrag zustimmen.',
  trial_used: 'Für diese E-Mail wurde die Testphase bereits genutzt.',
  device_limit: 'Auf diesem Gerät wurden bereits mehrere Testphasen gestartet.',
  ip_limit: 'Zu viele Testphasen aus diesem Netzwerk. Bitte später erneut versuchen.',
  no_pending: 'Keine offene Anfrage. Bitte erneut starten.',
  code_expired: 'Der Code ist abgelaufen. Bitte neu anfordern.',
  too_many_attempts: 'Zu viele Fehlversuche. Bitte neu anfordern.',
  wrong_code: 'Der Code stimmt nicht.',
  provisioning_failed: 'Zugang konnte nicht eingerichtet werden.',
  checkout_failed: 'Bezahlseite konnte nicht geöffnet werden.',
  bad_response: 'Der Server hat unerwartet geantwortet.',
  unknown: 'Zugang nicht gefunden.',
};
const reasonText = (reason) => REASONS[reason] || `Fehler: ${reason || 'unbekannt'}`;

// ─── Zugang ───────────────────────────────────────────────────────────
function viewOnboard(prefill) {
  $('access-title').textContent = 'Kostenlos testen';
  body().innerHTML = `
    <div class="hint" style="margin-top:0">14 Tage kostenlos, keine Zahlungsdaten nötig.</div>
    <label for="ob-email">E-Mail *</label>
    <input type="email" id="ob-email" placeholder="name@kanzlei.de" autocomplete="email" value="${esc(prefill?.email)}">
    <label for="ob-name">Name *</label>
    <input type="text" id="ob-name" placeholder="Vor- und Nachname" value="${esc(prefill?.name)}">
    <label for="ob-company">Firma <span>(optional)</span></label>
    <input type="text" id="ob-company" placeholder="nur bei geschäftlicher Nutzung" value="${esc(prefill?.company)}">
    <label class="avv-row"><input type="checkbox" id="ob-avv">
      <span>Ich stimme dem <a href="${LIC}/avv" target="_blank" rel="noopener">AVV</a> zu.</span></label>
    <div class="button-row"><button class="primary" id="ob-start">14 Tage kostenlos testen</button></div>`;
  $('ob-start').addEventListener('click', doOnboard);
}

function viewCode(email) {
  $('access-title').textContent = 'Zugang bestätigen';
  body().innerHTML = `
    <div class="hint" style="margin-top:0">6-stelligen Code für <b>${esc(email)}</b> eingeben. Pilotcodes werden direkt bereitgestellt.</div>
    <label for="code-input">Bestätigungscode</label>
    <input type="text" id="code-input" inputmode="numeric" maxlength="6" placeholder="000000" autocomplete="one-time-code">
    <div class="button-row"><button class="primary" id="code-verify">Bestätigen</button></div>
    <div class="button-row"><button id="code-resend">Code erneut anfordern</button><button id="code-back">Andere E-Mail</button></div>`;
  $('code-verify').addEventListener('click', doVerify);
  $('code-resend').addEventListener('click', doOnboard);
  $('code-back').addEventListener('click', async () => { await del(K.PENDING); fb(''); renderAccess(); });
  $('code-input').addEventListener('keydown', (event) => { if (event.key === 'Enter') doVerify(); });
  $('code-input').focus();
}

function planHtml() {
  return `
    <div class="plan" data-plan="monthly">
      <div class="plan-head"><span class="plan-name">Monatlich</span><span class="plan-price">9,99 €</span></div>
      <div class="plan-sub">pro Monat · inkl. MwSt · monatlich kündbar</div>
    </div>
    <div class="plan" data-plan="yearly">
      <div class="plan-head"><span class="plan-name">Jährlich</span><span class="plan-price">99 €</span></div>
      <div class="plan-sub">pro Jahr · inkl. MwSt · etwa 2 Monate günstiger</div>
    </div>`;
}
function bindPlans() {
  body().querySelectorAll('.plan').forEach((element) => {
    element.addEventListener('click', () => doCheckout(element.getAttribute('data-plan')));
  });
}

function logoutButton() {
  return '<div class="button-row"><button id="acc-logout">Auf diesem Gerät abmelden</button></div>';
}
function bindLogout() {
  $('acc-logout').addEventListener('click', doLogout);
}

function viewTrial(trialEnd) {
  $('access-title').textContent = 'Testphase';
  const left = daysLeft(trialEnd);
  const leftText = left == null ? 'Enddatum wird geprüft' : `Noch ${left} Tag${left === 1 ? '' : 'e'}`;
  body().innerHTML = `
    <div><span class="badge trial">Testphase aktiv</span></div>
    <div class="hint">${leftText} · gültig bis ${fmtDate(trialEnd)}.</div>
    <div class="section-title" style="margin-top:12px">Danach weiter</div>
    ${planHtml()}${logoutButton()}`;
  bindPlans();
  bindLogout();
}

function viewPaywall() {
  $('access-title').textContent = 'Abo abschließen';
  body().innerHTML = `
    <div><span class="badge expired">Zugang abgelaufen</span></div>
    <div class="hint">Schließe ein Abo ab, um den Schutz weiter zu nutzen.</div>
    ${planHtml()}${logoutButton()}`;
  bindPlans();
  bindLogout();
}

function viewActive(validUntil, subStatus, plan) {
  const isPilot = plan === 'pilot';
  const canceled = subStatus === 'canceled';
  $('access-title').textContent = isPilot ? 'Pilotzugang' : 'Abo';
  const badge = isPilot
    ? '<span class="badge pilot">Pilotzugang aktiv</span>'
    : '<span class="badge active">Abo aktiv</span>';
  const hint = isPilot
    ? `Kostenfreier Pilotzugang bis ${fmtDate(validUntil)}. Keine Abbuchung.`
    : canceled
      ? `Gekündigt — Zugang bleibt bis ${fmtDate(validUntil)} aktiv.`
      : validUntil
        ? `Verlängert sich automatisch · nächste Abbuchung um ${fmtDate(validUntil)}.`
        : 'Abo aktiv.';
  body().innerHTML = `
    <div>${badge}</div><div class="hint">${hint}</div>
    <div class="button-row">
      ${(!isPilot && !canceled) ? '<button id="acc-cancel">Abo kündigen</button>' : ''}
      <button id="acc-logout">Auf diesem Gerät abmelden</button>
    </div>`;
  if (!isPilot && !canceled) {
    let confirming = false;
    $('acc-cancel').addEventListener('click', () => {
      if (!confirming) {
        confirming = true;
        $('acc-cancel').textContent = 'Wirklich kündigen?';
        $('acc-cancel').classList.add('danger');
        return;
      }
      doCancel();
    });
  }
  bindLogout();
}

async function doOnboard() {
  const button = $('ob-start') || $('code-resend');
  const current = await get(K.PENDING);
  const pending = current[K.PENDING] || {};
  const email = ($('ob-email') ? $('ob-email').value : pending.email || '').trim();
  const name = ($('ob-name') ? $('ob-name').value : pending.name || '').trim();
  const company = ($('ob-company') ? $('ob-company').value : pending.company || '').trim();
  if ($('ob-email')) {
    if (!email) return fb('Bitte E-Mail eingeben.', 'err');
    if (!name) return fb('Bitte Namen eingeben.', 'err');
    if (!$('ob-avv').checked) return fb('Bitte dem AVV zustimmen.', 'err');
  }
  if (button) button.disabled = true;
  fb('Fordere Bestätigungscode an…');
  try {
    const result = await api('/onboard', 'POST', {
      email, name, company, avv_accepted: true, device_id: await deviceId(),
    });
    if (!result.ok) {
      fb(reasonText(result.reason), 'err');
      if (button) button.disabled = false;
      return;
    }
    await set({ [K.PENDING]: { email, name, company } });
    fb('Code bereit.', 'ok');
    renderAccess();
  } catch (_) {
    fb('Verbindung fehlgeschlagen. Bitte erneut versuchen.', 'err');
    if (button) button.disabled = false;
  }
}

async function doVerify() {
  const code = ($('code-input').value || '').trim();
  if (!/^\d{6}$/.test(code)) return fb('Bitte den 6-stelligen Code eingeben.', 'err');
  const current = await get(K.PENDING);
  const email = current[K.PENDING]?.email;
  if (!email) return renderAccess();
  $('code-verify').disabled = true;
  fb('Prüfe Zugang…');
  try {
    const result = await api('/verify', 'POST', { email, code, device_id: await deviceId() });
    if (!result.ok || !result.kp_key) {
      fb(reasonText(result.reason), 'err');
      $('code-verify').disabled = false;
      return;
    }
    await set({
      [K.API]: result.kp_key,
      [K.TOKEN]: result.customer_token,
      [K.EMAIL]: email,
      [K.TRIAL_END]: result.trial_end || null,
      [K.STATUS]: result.status,
    });
    await del(K.PENDING);
    fb(result.status === 'active' ? 'Zugang wiederhergestellt.' : 'Testphase aktiviert.', 'ok');
    renderAccess();
    refreshProtectionStatus();
  } catch (_) {
    fb('Verbindung fehlgeschlagen. Bitte erneut versuchen.', 'err');
    $('code-verify').disabled = false;
  }
}

async function doCheckout(plan) {
  const state = await get(K.TOKEN);
  if (!state[K.TOKEN]) return;
  fb('Öffne Bezahlseite…');
  try {
    const result = await api('/checkout', 'POST', { customer_token: state[K.TOKEN], plan });
    if (result.ok && result.checkout_url) {
      await chrome.tabs.create({ url: result.checkout_url });
      fb('Bezahlseite in neuem Tab geöffnet.', 'ok');
    } else fb(reasonText(result.reason), 'err');
  } catch (_) { fb('Verbindung fehlgeschlagen.', 'err'); }
}

async function doCancel() {
  const state = await get(K.TOKEN);
  if (!state[K.TOKEN]) return;
  const button = $('acc-cancel');
  if (button) button.disabled = true;
  fb('Kündige Abo…');
  try {
    const result = await api('/cancel', 'POST', { customer_token: state[K.TOKEN] });
    if (!result.ok) {
      fb(reasonText(result.reason), 'err');
      if (button) { button.disabled = false; button.textContent = 'Abo kündigen'; }
      return;
    }
    await set({ [K.VALID_UNTIL]: result.valid_until || null, [K.SUB_STATUS]: 'canceled' });
    fb(`Abo gekündigt. Zugang bleibt bis ${fmtDate(result.valid_until)}.`, 'ok');
    refreshMember();
  } catch (_) {
    fb('Verbindung fehlgeschlagen.', 'err');
    if (button) { button.disabled = false; button.textContent = 'Abo kündigen'; }
  }
}

async function doLogout() {
  await del([
    K.API, K.TOKEN, K.EMAIL, K.PENDING, K.TRIAL_END, K.VALID_UNTIL,
    K.STATUS, K.SUB_STATUS, K.PLAN,
  ]);
  fb('');
  await renderAccess();
  await refreshProtectionStatus();
}

async function refreshMember() {
  const state = await get(K.TOKEN);
  try {
    const result = await api(`/status?token=${encodeURIComponent(state[K.TOKEN])}`, 'GET');
    if (!result.ok) {
      if (result.reason === 'unknown') await doLogout();
      return;
    }
    if (result.kp_key) await set({ [K.API]: result.kp_key });
    await set({
      [K.STATUS]: result.status,
      [K.TRIAL_END]: result.trial_end || null,
      [K.VALID_UNTIL]: result.valid_until || null,
      [K.SUB_STATUS]: result.subscription_status || null,
      [K.PLAN]: result.plan || null,
    });
    if (result.status === 'active') viewActive(result.valid_until, result.subscription_status, result.plan);
    else if (result.status === 'trial') viewTrial(result.trial_end);
    else viewPaywall();
  } catch (_) {
    const cached = await get([K.STATUS, K.TRIAL_END, K.VALID_UNTIL, K.SUB_STATUS, K.PLAN]);
    if (cached[K.STATUS] === 'active') viewActive(cached[K.VALID_UNTIL], cached[K.SUB_STATUS], cached[K.PLAN]);
    else if (cached[K.STATUS] === 'trial') viewTrial(cached[K.TRIAL_END]);
    else viewPaywall();
  }
}

async function renderAccess() {
  const state = await get([K.TOKEN, K.PENDING]);
  if (state[K.TOKEN]) return refreshMember();
  if (state[K.PENDING]?.email) return viewCode(state[K.PENDING].email);
  return viewOnboard(null);
}

// ─── Schutzstatus ─────────────────────────────────────────────────────
function platformFromUrl(url) {
  try {
    const parsed = new URL(url);
    return Core.platformHost(parsed.hostname) ? parsed.hostname : null;
  } catch (_) { return null; }
}

async function activeSupportedTab() {
  try {
    const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
    const tab = tabs[0];
    return tab && platformFromUrl(tab.url) ? tab : null;
  } catch (_) { return null; }
}

function pingContent(tabId) {
  return new Promise((resolve) => {
    let settled = false;
    const finish = (value) => {
      if (!settled) { settled = true; resolve(value); }
    };
    try {
      chrome.tabs.sendMessage(tabId, { type: 'kishield-health-query' }, (response) => {
        if (chrome.runtime.lastError) return finish(null);
        finish(response || null);
      });
    } catch (_) { finish(null); }
    setTimeout(() => finish(null), 1600);
  });
}

let pauseConfirmUntil = 0;
async function toggleProtection() {
  const state = await get(K.ENABLED);
  const enabled = Core.protectionEnabled(state[K.ENABLED]);
  const button = $('protection-toggle');
  if (enabled && Date.now() > pauseConfirmUntil) {
    pauseConfirmUntil = Date.now() + 5000;
    const armedUntil = pauseConfirmUntil;
    button.classList.add('danger');
    $('pause-warning').textContent = 'Noch einmal klicken: Schutz pausieren und Klartext ungeschützt senden.';
    $('pause-warning').classList.add('visible');
    setTimeout(() => {
      if (pauseConfirmUntil !== armedUntil || Date.now() <= pauseConfirmUntil) return;
      pauseConfirmUntil = 0;
      refreshProtectionStatus();
    }, 5100);
    return;
  }
  pauseConfirmUntil = 0;
  await set({ [K.ENABLED]: !enabled });
  await refreshProtectionStatus();
}

async function refreshProtectionStatus() {
  const state = await get([K.ENABLED, K.API, K.DISABLED]);
  const enabled = Core.protectionEnabled(state[K.ENABLED]);
  const disabledTypes = Core.sanitizeDisabledTypes(state[K.DISABLED]);
  const count = Core.enabledPiiCount(disabledTypes);
  const button = $('protection-toggle');
  button.setAttribute('aria-checked', String(enabled));
  button.classList.toggle('danger', !enabled);
  $('protection-count').textContent = `${count} von 42 Schutzarten aktiv`;
  $('protection-count').style.background = count === 42 ? '#e6f6ef' : '#fff1d7';
  $('protection-count').style.color = count === 42 ? '#086344' : '#8a5600';

  const tab = await activeSupportedTab();
  const dot = $('active-dot');
  const text = $('active-text');
  const detail = $('health-detail');
  const pause = $('pause-warning');

  if (!enabled) {
    dot.className = 'dot red';
    text.textContent = 'Schutz pausiert';
    detail.textContent = 'Zugang und Einstellungen bleiben erhalten.';
    pause.textContent = 'Schutz pausiert: Chattexte werden unverändert an den KI-Anbieter übertragen.';
    pause.classList.add('visible');
    return;
  }

  button.classList.remove('danger');
  pause.classList.remove('visible');
  if (!state[K.API]) {
    dot.className = 'dot amber';
    text.textContent = 'Zugang fehlt';
    detail.textContent = 'Anmelden oder Code eingeben; Chat-Sends bleiben bis dahin gesperrt.';
    return;
  }
  if (!tab) {
    // Vorsicht: "kein unterstützter Tab" heißt NICHT zwingend, dass der Nutzer
    // auf einer harmlosen Seite ist. Im Safari-Build bleibt die Tab-URL für die
    // Erweiterung unsichtbar, solange der Website-Zugriff nicht erteilt wurde —
    // der Nutzer kann also mitten auf claude.ai sitzen, während hier nur "kein
    // Tab" ankommt. Grün wäre dann eine falsche Sicherheitszusage.
    const hint = (typeof window.KIShieldPlatformHint === 'function')
      ? await window.KIShieldPlatformHint(null)
      : null;
    if (hint) {
      dot.className = 'dot red';
      text.textContent = hint.title;
      detail.textContent = hint.detail;
      return;
    }
    dot.className = 'dot green';
    text.textContent = 'Schutz ist eingeschaltet';
    detail.textContent = 'Öffne Claude, ChatGPT oder Gemini für den vollständigen Ebenentest.';
    return;
  }

  const health = await pingContent(tab.id);
  if (health && health.content && health.network) {
    const diagnostics = health.networkDiagnostics;
    const blocks = diagnostics && Number(diagnostics.approvalBlocks || 0);
    const maskDiagnostics = health.maskDiagnostics;
    if (maskDiagnostics) {
      dot.className = 'dot amber';
      text.textContent = 'Schutz aktiv – Mapping sicher blockiert';
      detail.textContent =
        `Schutzabbildung ${maskDiagnostics.source || 'unbekannt'} inkonsistent` +
        ` · ${maskDiagnostics.entityCount || 0} Entitäten/${maskDiagnostics.mappingCount || 0} Mappings` +
        ` · ${maskDiagnostics.inputLength || 0}→${maskDiagnostics.outputLength || 0} Zeichen.` +
        ' Inhalte werden nicht protokolliert.';
    } else if (blocks > 0) {
      const last = diagnostics.lastBlock || {};
      dot.className = 'dot amber';
      text.textContent = 'Schutz aktiv – Send-Blockade erkannt';
      detail.textContent =
        `UI- und Netzwerk-Schutz antworten · ${blocks} Blockade${blocks === 1 ? '' : 'n'} seit Seitenladen` +
        ` · zuletzt ${last.platform || 'Plattform'}/${last.transport || 'Netzwerk'}, ${last.bodyLength || 0} Zeichen.` +
        ' Inhalte werden nicht protokolliert.';
    } else {
      dot.className = 'dot green';
      text.textContent = `Geschützt auf ${platformFromUrl(tab.url)}`;
      detail.textContent =
        `UI- und Netzwerk-Schutz antworten · ${health.mappings || 0} lokale Mappings aktiv` +
        ' · Selbstdiagnose ohne Send-Blockade.';
    }
  } else {
    // Im Safari-Build kann der Grund ein fehlender Website-Zugriff sein — dann
    // ist "Seite neu laden" die falsche Auskunft, weil ein Reload nichts
    // ändert. Der Hinweisgeber wird nur im Safari-Payload mitgeliefert; in
    // Chrome bleibt es unverändert bei der Reload-Meldung.
    const hint = (typeof window.KIShieldPlatformHint === 'function')
      ? await window.KIShieldPlatformHint(tab)
      : null;
    dot.className = hint ? 'dot red' : 'dot amber';
    text.textContent = hint ? hint.title : 'Seite neu laden';
    detail.textContent = hint
      ? hint.detail
      : 'Mindestens eine Schutzebene antwortet noch nicht. Bitte F5/⌘R drücken.';
  }
}

// ─── Kategorien ───────────────────────────────────────────────────────
let disabled = new Set();
let pendingDisableType = null;
let pendingDisableTimer = null;

async function loadDisabled() {
  const result = await get(K.DISABLED);
  const clean = Core.sanitizeDisabledTypes(result[K.DISABLED]);
  disabled = new Set(clean);
  if (JSON.stringify(result[K.DISABLED] || []) !== JSON.stringify(clean)) {
    await set({ [K.DISABLED]: clean });
  }
}
async function saveDisabled() {
  const clean = Core.sanitizeDisabledTypes([...disabled]);
  disabled = new Set(clean);
  await set({ [K.DISABLED]: clean });
  updateCategorySummary();
}
function updateCategorySummary() {
  const count = Core.enabledPiiCount([...disabled]);
  $('protection-count').textContent = `${count} von 42 Schutzarten aktiv`;
  const warning = $('categories-warning');
  if (count === 42) {
    warning.classList.remove('visible');
    warning.textContent = '';
  } else {
    warning.classList.add('visible');
    warning.textContent = `${42 - count} Kategorien sind deaktiviert. Diese Daten können unverändert an den KI-Anbieter gehen.`;
  }
}
async function enableAllPii() {
  disabled = new Set(Core.enableAllPii());
  await set({ [K.DISABLED]: [] });
  renderCategories();
  updateCategorySummary();
  settingsFeedback('Vollschutz wiederhergestellt: 42 von 42 Schutzarten aktiv.', 'ok');
}
function requestCategoryToggle(type, button) {
  if (disabled.has(type)) {
    disabled.delete(type);
    saveDisabled().then(renderCategories);
    return;
  }
  if (pendingDisableType !== type) {
    pendingDisableType = type;
    clearTimeout(pendingDisableTimer);
    pendingDisableTimer = setTimeout(() => {
      pendingDisableType = null;
      renderCategories();
      updateCategorySummary();
    }, 5000);
    button.classList.add('danger');
    $('categories-warning').textContent =
      `${Core.PII_NAMES[type]} wirklich deaktivieren? Noch einmal denselben Schalter anklicken.`;
    $('categories-warning').classList.add('visible');
    return;
  }
  pendingDisableType = null;
  disabled.add(type);
  saveDisabled().then(renderCategories);
}
function renderCategories() {
  const container = $('pii-categories');
  container.innerHTML = '';
  Core.PII_GROUPS.forEach((group) => {
    const title = document.createElement('div');
    title.className = 'cat-group-title';
    title.textContent = group.label;
    container.appendChild(title);
    group.types.forEach((type) => {
      const row = document.createElement('div');
      row.className = 'cat-row';
      const label = document.createElement('span');
      label.className = 'cat-label';
      label.textContent = Core.PII_NAMES[type] || type;
      const button = document.createElement('button');
      button.className = 'switch-button mini-switch';
      button.setAttribute('role', 'switch');
      button.setAttribute('aria-label', `${label.textContent} aktivieren oder deaktivieren`);
      button.setAttribute('aria-checked', String(!disabled.has(type)));
      if (pendingDisableType === type) button.classList.add('danger');
      button.addEventListener('click', () => requestCategoryToggle(type, button));
      row.append(label, button);
      container.appendChild(row);
    });
  });
  updateCategorySummary();
}

async function toggleLiveDetection() {
  const state = await get(K.LIVE);
  const next = !Core.liveDetectionEnabled(state[K.LIVE]);
  await set({ [K.LIVE]: next });
  $('live-detection-toggle').setAttribute('aria-checked', String(next));
  settingsFeedback(
    next
      ? 'Live-Erkennung aktiv: Entwürfe werden nach Tipp-Pausen an deinen KIShieldGuard-Proxy gesendet.'
      : 'Live-Erkennung aus: Prüfung erfolgt erst beim Senden.',
    next ? '' : 'ok'
  );
}

// ─── Ausnahmen und lokale Daten ───────────────────────────────────────
async function renderNeverTerms() {
  const result = await get(K.NEVER);
  const terms = Core.sanitizeNeverTerms(result[K.NEVER]);
  const container = $('never-terms');
  container.innerHTML = '';
  if (terms.length === 0) {
    const empty = document.createElement('div');
    empty.className = 'empty';
    empty.textContent = 'Keine „Nie als PII“-Ausnahmen gespeichert.';
    container.appendChild(empty);
    $('clear-never').disabled = true;
    return;
  }
  $('clear-never').disabled = false;
  terms.forEach((term) => {
    const row = document.createElement('div');
    row.className = 'term-row';
    const value = document.createElement('span');
    value.className = 'term-value';
    value.textContent = term;
    value.title = term;
    const remove = document.createElement('button');
    remove.className = 'compact remove-never';
    remove.textContent = 'Löschen';
    remove.addEventListener('click', async () => {
      await set({ [K.NEVER]: terms.filter((item) => item !== term) });
      await renderNeverTerms();
      settingsFeedback('Ausnahme gelöscht.', 'ok');
    });
    row.append(value, remove);
    container.appendChild(row);
  });
}

async function addNeverTerm() {
  const input = $('never-term-input');
  const rawTerm = input.value;
  const result = await get(K.NEVER);
  const before = Core.sanitizeNeverTerms(result[K.NEVER]);
  const next = Core.addNeverTerm(before, rawTerm);
  if (next.length === before.length) {
    settingsFeedback(rawTerm.trim() ? 'Begriff ist bereits vorhanden oder zu lang.' : 'Bitte einen Begriff eingeben.', 'err');
    return;
  }
  await set({ [K.NEVER]: next });
  input.value = '';
  await renderNeverTerms();
  settingsFeedback('Begriff lokal zur Whitelist hinzugefügt.', 'ok');
}

let clearNeverArmed = false;
async function clearNever() {
  if (!clearNeverArmed) {
    clearNeverArmed = true;
    $('clear-never').textContent = 'Wirklich alle löschen?';
    return;
  }
  await set({ [K.NEVER]: [] });
  clearNeverArmed = false;
  $('clear-never').textContent = 'Alle Ausnahmen löschen';
  await renderNeverTerms();
  settingsFeedback('Alle Ausnahmen gelöscht.', 'ok');
}

let clearMappingsArmed = false;
async function clearMappings() {
  if (!clearMappingsArmed) {
    clearMappingsArmed = true;
    $('clear-mappings').textContent = 'Wirklich Mappings löschen?';
    return;
  }
  const all = await get(null);
  const keys = Object.keys(all || {}).filter((key) => key.startsWith('mappings_'));
  if (keys.length) await del(keys);
  clearMappingsArmed = false;
  $('clear-mappings').textContent = 'Mappings löschen';
  settingsFeedback(`${keys.length} lokale Conversation-Mappings gelöscht. Offene Chatseiten bitte neu laden.`, 'ok');
  refreshProtectionStatus();
}

async function initSettings() {
  const state = await get([K.LIVE]);
  $('live-detection-toggle').setAttribute('aria-checked', String(Core.liveDetectionEnabled(state[K.LIVE])));
  await loadDisabled();
  renderCategories();
  await renderNeverTerms();
}

$('protection-toggle').addEventListener('click', toggleProtection);
$('enable-all-pii').addEventListener('click', enableAllPii);
$('live-detection-toggle').addEventListener('click', toggleLiveDetection);
$('add-never').addEventListener('click', addNeverTerm);
$('never-term-input').addEventListener('keydown', (event) => {
  if (event.key === 'Enter') addNeverTerm();
});
$('clear-never').addEventListener('click', clearNever);
$('clear-mappings').addEventListener('click', clearMappings);
$('runtime-version').textContent = `v${chrome.runtime.getManifest().version} · Proxy`;

chrome.storage.onChanged.addListener((changes, area) => {
  if (area !== 'local') return;
  if (changes[K.DISABLED]) {
    disabled = new Set(Core.sanitizeDisabledTypes(changes[K.DISABLED].newValue));
    renderCategories();
  }
  if (changes[K.NEVER]) renderNeverTerms();
  if (changes[K.ENABLED] || changes[K.API]) refreshProtectionStatus();
});

(async () => {
  await Promise.all([renderAccess(), initSettings()]);
  await refreshProtectionStatus();
})();
