'use strict';

// ════════════════════════════════════════════════════════════════════════════
// shared.js — Gemeinsame Bibliothek für confirmSend.js und popup.js
// ════════════════════════════════════════════════════════════════════════════

// ── Gemeinsame UI-Lokalisierung ─────────────────────────────────────────────
// Wird von popup.html und confirmSend.html geladen. Inline-Skripte sind in
// Extension-Seiten wegen der CSP nicht zuverlässig erlaubt.
PPP_SHARED = window.PPP_SHARED = window.PPP_SHARED || {};

PPP_SHARED.applyI18n = function (root = document) {
  root.querySelectorAll('[data-i18n]').forEach((el) => {
    const key = el.getAttribute('data-i18n');
    const msg = browser.i18n.getMessage(key);
    if (msg) el.textContent = msg;
  });

  root.querySelectorAll('[data-i18n-placeholder]').forEach((el) => {
    const key = el.getAttribute('data-i18n-placeholder');
    const msg = browser.i18n.getMessage(key);
    if (msg) el.setAttribute('placeholder', msg);
  });
};

// Statisches Fallback in HTML bleibt erhalten; tatsächlich verwendete
// UI-Sprache wird hier korrekt auf dem Dokument gesetzt.
document.documentElement.lang = browser.i18n.getUILanguage();

PPP_SHARED.applyI18n();
window.PPP_SHARED = window.PPP_SHARED || {};

PPP_SHARED.resizeAlgorithms = [];
PPP_SHARED.profileSettings = {};

PPP_SHARED.formatBytes = function (n) {
  if (n < 1024) return n + ' ' + browser.i18n.getMessage('unit_bytes');
  if (n < 1048576) return (n / 1024).toFixed(1) + ' ' + browser.i18n.getMessage('unit_kilobytes');
  return (n / 1048576).toFixed(1) + ' ' + browser.i18n.getMessage('unit_megabytes');
};

PPP_SHARED.$ = function (id) {
  return document.getElementById(id);
};

PPP_SHARED.loading = document.getElementById('loading');
PPP_SHARED.summary = document.getElementById('summary');
PPP_SHARED.emptyState = document.getElementById('empty-state');
PPP_SHARED.error = document.getElementById('error');
PPP_SHARED.content = document.getElementById('content');
PPP_SHARED.imageList = document.getElementById('image-list');

PPP_SHARED.sendMessage = function (msg) {
  return browser.runtime.sendMessage(msg);
};

PPP_SHARED.showLoading = function (text) {
  PPP_SHARED.loading.textContent = text || browser.i18n.getMessage('text_processing');
  PPP_SHARED.loading.style.display = 'flex';
};

PPP_SHARED.hideLoading = function () {
  PPP_SHARED.loading.style.display = 'none';
};

PPP_SHARED.showProgress = function (current, total, name) {
  const pct = total > 0 ? Math.round((current / total) * 100) : 0;
  const el = PPP_SHARED.loading;
  el.innerHTML = '';
  const text = document.createElement('div');
  text.textContent = `${current}/${total} — ${name}`;
  const bar = document.createElement('div');
  bar.style.cssText = 'width:80%;height:6px;background:#e0e0e0;border-radius:3px;margin-top:8px;overflow:hidden;';
  const fill = document.createElement('div');
  fill.style.cssText = `width:${pct}%;height:100%;background:#4a90d9;transition:width 0.2s;`;
  bar.appendChild(fill);
  el.appendChild(text);
  el.appendChild(bar);
  el.style.display = 'flex';
  el.style.flexDirection = 'column';
  el.style.alignItems = 'center';
};

PPP_SHARED.getCategoryLabel = function (cat) {
  const catKey = { attach: 'cat_attachment', inline: 'cat_inline', quote: 'cat_quote' }[cat];
  return catKey ? browser.i18n.getMessage(catKey) : cat;
};

PPP_SHARED.getCurrentSize = function (img) {
  if (img.state === 'removed' || img.state === 'excluded') return 0;
  if (img.state === 'compressed' && img.compressedSize != null) return img.compressedSize;
  return img.size;
};

PPP_SHARED.populateResizeAlgorithms = function (selectEl, currentValue) {
  selectEl.innerHTML = '';
  for (const { value, label } of PPP_SHARED.resizeAlgorithms) {
    const opt = document.createElement('option');
    opt.value = value;
    opt.textContent = label;
    selectEl.appendChild(opt);
  }
  const valid = PPP_SHARED.resizeAlgorithms.some((a) => a.value === currentValue);
  selectEl.value = valid ? currentValue : 'auto';
};

PPP_SHARED.updateSummary = function () {
  const images = PPP_SHARED.images;
  let totalOriginal = 0;
  let totalCurrent = 0;
  let compressedCount = 0;
  let removedCount = 0;
  let excludedCount = 0;

  for (const img of images) {
    // Ausgenommene Bilder bleiben unverändert in der E-Mail und dürfen nicht
    // als Ersparnis zählen — komplett aus der Summenberechnung herausnehmen.
    if (img.state === 'excluded') {
      excludedCount++;
      continue;
    }
    totalOriginal += img.size;
    if (img.state === 'removed') {
      removedCount++;
    } else {
      totalCurrent += PPP_SHARED.getCurrentSize(img);
      if (img.state === 'compressed') compressedCount++;
    }
  }

  const savings = totalOriginal - totalCurrent;
  const pct = totalOriginal > 0 ? ((savings / totalOriginal) * 100).toFixed(1) : 0;
  const parts = [];
  if (compressedCount > 0) parts.push(`${compressedCount} ${browser.i18n.getMessage('summary_compressed')}`);
  if (removedCount > 0) parts.push(`${removedCount} ${browser.i18n.getMessage('summary_removed')}`);
  if (excludedCount > 0) parts.push(`${excludedCount} ${browser.i18n.getMessage('summary_excluded')}`);
  if (savings > 0) parts.push(`${PPP_SHARED.formatBytes(savings)} ${browser.i18n.getMessage('summary_saved')} (-${pct}%)`);
  PPP_SHARED.summary.textContent = parts.join(', ') || browser.i18n.getMessage('summary_noChanges');
};

PPP_SHARED.getInlineDims = function (img) {
  if (img.width && img.height) {
    return `${img.width}×${img.height}`;
  }
  return null;
};

PPP_SHARED.getNativeDims = function (img) {
  const nw = img.naturalWidth || img.width;
  const nh = img.naturalHeight || img.height;
  return (nw && nh) ? `${nw}×${nh}` : null;
};

// ── populateProfileDropdowns — gemeinsame Funktion für popup.js und confirmSend.js ──
PPP_SHARED.populateProfileDropdowns = function (profiles, defaults) {
  for (const selId of ['attachProfile', 'inlineProfile', 'quoteProfile']) {
    const sel = document.getElementById(selId);
    if (!sel) continue;
    sel.innerHTML = '';
    for (const p of profiles) {
      const opt = document.createElement('option');
      opt.value = p;
      opt.textContent = p;
      sel.appendChild(opt);
    }
    const cat = selId.replace('Profile', '');
    const cur = defaults[cat] || '2. quality optimized';
    if (profiles.includes(cur)) {
      sel.value = cur;
    } else {
      sel.value = profiles[0] || '2. quality optimized';
    }
  }
};

// ── syncCardProfiles — globale Profilauswahl auf einzelne Karten übertragen ──
PPP_SHARED.syncCardProfiles = function (category, profileName) {
  for (const img of PPP_SHARED.images) {
    if (img.category !== category) continue;
    if (img.state !== 'original') continue;
    const el = img._elements;
    if (!el || !el.profileSelect) continue;
    const opts = Array.from(el.profileSelect.options).map((o) => o.value);
    if (opts.includes(profileName)) {
      el.profileSelect.value = profileName;
      const ps = PPP_SHARED.profileSettings[profileName];
      if (ps && el.metaCheckbox) {
        el.metaCheckbox.checked = ps.preserveMeta;
        // 'shrink to inline' nur wirksam, wenn die Checkbox auch angezeigt wird
        if (el.setScaleMode) el.setScaleMode(!!ps.scale && el.scaleLabel.style.display !== 'none');
        if (el.updateKeepInline) el.updateKeepInline();
      }
    }
  }
};

// ── renderCard — Layout: Thumbnail links, Parameter-Tabelle rechts ──────────

PPP_SHARED.renderCard = function (img) {
  const card = document.createElement('div');
  card.className = 'image-card';
  card.dataset.imageId = img.id;

  if (img.preview) {
    const thumb = document.createElement('img');
    thumb.className = 'thumbnail'; thumb.width = 120; thumb.height = 120;
    thumb.src = img.preview;
    card.appendChild(thumb);
  } else {
    const placeholder = document.createElement('div');
    placeholder.className = 'thumbnail-placeholder';
    placeholder.textContent = img.state === 'removed' ? '🗑' : '';
    card.appendChild(placeholder);
  }

  const controls = document.createElement('div');
  controls.className = 'controls';

  const header = document.createElement('div');
  header.className = 'card-header';
  const name = document.createElement('span');
  name.className = 'card-name';
  name.title = img.name;
  const catBadge = document.createElement('span');
  catBadge.className = 'card-category';
  catBadge.textContent = PPP_SHARED.getCategoryLabel(img.category);
  header.appendChild(name);
  header.appendChild(catBadge);
  controls.appendChild(header);

  const table = document.createElement('table');
  table.className = 'param-table';
  const thead = document.createElement('thead');
  const headRow = document.createElement('tr');
  ['col_type', 'col_size', 'col_resolution', 'col_inlineResolution', 'col_savings']
    .map((k) => browser.i18n.getMessage(k)).forEach((label) => {
    const th = document.createElement('th');
    th.textContent = label;
    headRow.appendChild(th);
  });
  thead.appendChild(headRow);
  table.appendChild(thead);
  const tbody = document.createElement('tbody');
  const beforeRow = document.createElement('tr');
  beforeRow.className = 'row-before';
  tbody.appendChild(beforeRow);
  const afterRow = document.createElement('tr');
  afterRow.className = 'row-after';
  tbody.appendChild(afterRow);
  table.appendChild(tbody);
  controls.appendChild(table);

  // ── Controls-Row: Resize-Slider + Profil-Selector nebeneinander ───────────
  const controlsRow = document.createElement('div');
  controlsRow.className = 'controls-row';

  const resizeGroup = document.createElement('div');
  resizeGroup.className = 'resize-group';
  const resizeLabel = document.createElement('span');
  resizeLabel.className = 'control-label';
  resizeLabel.textContent = browser.i18n.getMessage('label_resize');
  const resizeSlider = document.createElement('input');
  resizeSlider.type = 'range';
  resizeSlider.min = 5;
  resizeSlider.max = 100;
  resizeSlider.step = 5;
  resizeSlider.value = 100;
  const resizeValue = document.createElement('span');
  resizeValue.className = 'range-value';
  resizeValue.textContent = '100';
  resizeGroup.appendChild(resizeLabel);
  resizeGroup.appendChild(resizeSlider);
  resizeGroup.appendChild(resizeValue);
  controlsRow.appendChild(resizeGroup);

  const profileGroup = document.createElement('div');
  profileGroup.className = 'profile-group';
  const profileLabel = document.createElement('span');
  profileLabel.className = 'control-label';
  profileLabel.textContent = browser.i18n.getMessage('label_profile');
  const profileSelect = document.createElement('select');
  for (const p of PPP_SHARED.profiles) {
    const opt = document.createElement('option');
    opt.value = p;
    opt.textContent = p;
    profileSelect.appendChild(opt);
  }
  const defaultProfile = PPP_SHARED.defaults[img.category] || PPP_SHARED.profiles[0] || '2. quality optimized';
  if (PPP_SHARED.profiles.includes(defaultProfile)) {
    profileSelect.value = defaultProfile;
  } else {
    profileSelect.value = PPP_SHARED.profiles[0] || '2. quality optimized';
  }
  profileGroup.appendChild(profileLabel);
  profileGroup.appendChild(profileSelect);
  controlsRow.appendChild(profileGroup);
  controls.appendChild(controlsRow);

  // ── Checkbox-Row: alle Checkboxen nebeneinander ──
  const checkboxRow = document.createElement('div');
  checkboxRow.className = 'button-row';
  checkboxRow.style.flexWrap = 'wrap';

  const cbStyle = 'display:flex;align-items:center;gap:3px;font-size:10px;line-height:14px;white-space:nowrap;flex-shrink:0;color:#666;cursor:pointer;';
  const inputStyle = 'margin:0;width:13px;height:13px;';

  // force png
  const forcePngLabel = document.createElement('label');
  forcePngLabel.style.cssText = cbStyle;
  const forcePngCheckbox = document.createElement('input');
  forcePngCheckbox.type = 'checkbox';
  forcePngCheckbox.style.cssText = inputStyle;
  forcePngCheckbox.title = 'Bild zwangsweise als PNG speichern (JPEG→PNG; PNG bleibt PNG, kein JPEG-Versuch) — Ergebnis wird auch ohne Verkleinerung übernommen';
  forcePngLabel.appendChild(forcePngCheckbox);
  forcePngLabel.appendChild(document.createTextNode(browser.i18n.getMessage('card_forcePng')));
  checkboxRow.appendChild(forcePngLabel);

  // keep meta
  const metaLabel = document.createElement('label');
  metaLabel.style.cssText = cbStyle + 'margin-left:8px;';
  const metaCheckbox = document.createElement('input');
  metaCheckbox.type = 'checkbox';
  metaCheckbox.style.cssText = inputStyle;
  metaCheckbox.title = browser.i18n.getMessage('title_keepMeta');
  metaLabel.appendChild(metaCheckbox);
  metaLabel.appendChild(document.createTextNode(browser.i18n.getMessage('card_keepMeta')));
  // Keep meta nur anzeigen, wenn das Bild Metadaten enthält (img.hasMeta aus background.js)
  if (!img.hasMeta) metaLabel.style.display = 'none';
  checkboxRow.appendChild(metaLabel);

  // shrink to inline — nur sichtbar wenn Inline-Auflösung ≠ native Auflösung
  const inlineDiffersFromNative =
    img.width !== img.naturalWidth || img.height !== img.naturalHeight;
  const scaleLabel = document.createElement('label');
  scaleLabel.style.cssText = cbStyle;
  const scaleCheckbox = document.createElement('input');
  scaleCheckbox.type = 'checkbox';
  scaleCheckbox.style.cssText = inputStyle;
  scaleCheckbox.title = browser.i18n.getMessage('title_shrinkToInline');
  scaleLabel.appendChild(scaleCheckbox);
  scaleLabel.appendChild(document.createTextNode(browser.i18n.getMessage('card_shrinkToInline')));
  scaleLabel.style.display = (img.category === 'attach' || !inlineDiffersFromNative) ? 'none' : '';
  checkboxRow.appendChild(scaleLabel);

  // keep dimension
  const keepInlineLabel = document.createElement('label');
  keepInlineLabel.style.cssText = cbStyle;
  const keepInlineCheckbox = document.createElement('input');
  keepInlineCheckbox.type = 'checkbox';
  keepInlineCheckbox.style.cssText = inputStyle;
  keepInlineCheckbox.title = browser.i18n.getMessage('title_keepDimension');
  keepInlineLabel.appendChild(keepInlineCheckbox);
  keepInlineLabel.appendChild(document.createTextNode(browser.i18n.getMessage('card_keepDimension')));
  keepInlineLabel.style.display = 'none'; // wird eingeblendet, sobald die Auflösung verändert wird
  checkboxRow.appendChild(keepInlineLabel);

  controls.appendChild(checkboxRow);

  // ── Resize-Slider Dual-Mode: Prozent / Antialias-Faktor ──────────────────
  function updateResizeDisplay() {
    if (scaleCheckbox.checked) {
      resizeLabel.textContent = browser.i18n.getMessage('label_antiAlias');
      const factor = parseFloat(resizeSlider.value);
      const inlineW = img.width || 0;
      const inlineH = img.height || 0;
      const nativeW = img.naturalWidth || inlineW;
      const nativeH = img.naturalHeight || inlineH;
      let targetW = Math.round(inlineW * factor);
      let targetH = Math.round(inlineH * factor);
      targetW = Math.min(targetW, nativeW);
      targetH = Math.min(targetH, nativeH);
      resizeValue.textContent = `${targetW}×${targetH}`;
    } else {
      resizeLabel.textContent = browser.i18n.getMessage('label_resize');
      resizeValue.textContent = resizeSlider.value + '%';
    }
  }

  // ── keep dimension: erst einblenden, wenn die Auflösung verändert wird ──
  // Sichtbar + angehakt bei 'shrink to inline' oder Resize < 100 %.
  // Beim ersten Einblenden automatisch anhaken; eine manuelle Abwahl
  // bleibt bestehen, bis der Resize zurückgenommen und erneut aktiviert wird.
  function updateKeepInline() {
    if (img.category === 'attach') return;
    const resizeActive = scaleCheckbox.checked
      || (resizeSlider.max === '100' && parseInt(resizeSlider.value, 10) < 100);
    const wasHidden = keepInlineLabel.style.display === 'none';
    keepInlineLabel.style.display = resizeActive ? '' : 'none';
    if (resizeActive && wasHidden) keepInlineCheckbox.checked = true;
  }

  resizeSlider.addEventListener('input', () => { updateResizeDisplay(); updateKeepInline(); });

  // Zentraler Moduswechsel: Checkbox UND Slider (Antialias 1.0–2.0 ↔ Resize 5–100 %)
  // immer paarweise setzen — sonst bleibt ein Antialias-Wert als Prozentwert stehen.
  function setScaleMode(on) {
    scaleCheckbox.checked = on;
    if (on) {
      resizeSlider.min = '1.0';
      resizeSlider.max = '2.0';
      resizeSlider.step = '0.1';
      const ps = PPP_SHARED.profileSettings?.[profileSelect.value];
      resizeSlider.value = ps?.antialias ?? 1.0;
    } else {
      resizeSlider.min = '5';
      resizeSlider.max = '100';
      resizeSlider.step = '5';
      resizeSlider.value = '100';
    }
    updateResizeDisplay();
    updateKeepInline();
  }

  scaleCheckbox.addEventListener('change', () => setScaleMode(scaleCheckbox.checked));

  // Checkboxen aus Profil initialisieren
  const initPs = PPP_SHARED.profileSettings[profileSelect.value];
  if (initPs) {
    metaCheckbox.checked = initPs.preserveMeta;
    // 'shrink to inline' nur wirksam, wenn die Checkbox auch angezeigt wird
    scaleCheckbox.checked = initPs.scale && scaleLabel.style.display !== 'none';
    if (initPs.scale && scaleLabel.style.display !== 'none') {
      resizeSlider.min = '1.0';
      resizeSlider.max = '2.0';
      resizeSlider.step = '0.1';
      resizeSlider.value = initPs.antialias ?? 1.0;
    }
  }
  updateResizeDisplay();
  updateKeepInline();

  // Bei Profilwechsel Checkboxen nachziehen
  profileSelect.addEventListener('change', () => {
    const ps = PPP_SHARED.profileSettings[profileSelect.value];
    if (ps) {
      metaCheckbox.checked = ps.preserveMeta;
      // 'shrink to inline' nur wirksam, wenn die Checkbox auch angezeigt wird
      setScaleMode(!!ps.scale && scaleLabel.style.display !== 'none');
    }
  });

  const buttonRow = document.createElement('div');
  buttonRow.className = 'button-row';
  controls.appendChild(buttonRow);

  card.appendChild(controls);

  img._elements = {
    card, table, tbody, beforeRow, afterRow, buttonRow, checkboxRow,
    resizeSlider, resizeValue, profileSelect, controlsRow,
    resizeGroup, profileGroup, name,
    metaCheckbox, scaleCheckbox, metaLabel, scaleLabel,
    keepInlineCheckbox, keepInlineLabel,
    updateKeepInline, setScaleMode,
    forcePngCheckbox, forcePngLabel,
  };

  PPP_SHARED.updateCard(img);
  return card;
};

// ── updateCard ───────────────────────────────────────────────────────────────

PPP_SHARED.updateCard = function (img) {
  const el = img._elements;
  if (!el) return;

  el.card.classList.toggle('removed', img.state === 'removed' || img.state === 'excluded');

  el.name.textContent = img.name;
  el.name.querySelectorAll('.incompressible-badge, .excluded-badge').forEach((b) => b.remove());
  if (img.state === 'incompressible') {
    const badge = document.createElement('span');
    badge.className = 'incompressible-badge';
    badge.textContent = browser.i18n.getMessage('badge_incompressible');
    el.name.appendChild(badge);
  } else if (img.state === 'excluded') {
    const badge = document.createElement('span');
    badge.className = 'excluded-badge';
    badge.textContent = browser.i18n.getMessage('badge_excluded');
    el.name.appendChild(badge);
  }

  el.beforeRow.innerHTML = '';

  const beforeType = document.createElement('td');
  beforeType.textContent = (img.format || 'png').toUpperCase();

  const beforeSize = document.createElement('td');
  beforeSize.textContent = PPP_SHARED.formatBytes(img.size);

  const beforeRes = document.createElement('td');
  const nativeDims = PPP_SHARED.getNativeDims(img);
  beforeRes.textContent = nativeDims ? nativeDims.replace('×', ' x ') : ' - ';

  const beforeInline = document.createElement('td');
  const inlineDims = PPP_SHARED.getInlineDims(img);
  beforeInline.textContent = inlineDims ? inlineDims.replace('×', ' x ') : ' - ';

  const beforeSavings = document.createElement('td');
  beforeSavings.textContent = ' - ';

  el.beforeRow.appendChild(beforeType);
  el.beforeRow.appendChild(beforeSize);
  el.beforeRow.appendChild(beforeRes);
  el.beforeRow.appendChild(beforeInline);
  el.beforeRow.appendChild(beforeSavings);

  el.afterRow.innerHTML = '';
  el.afterRow.classList.remove('has-result');

  const afterType = document.createElement('td');
  const afterSize = document.createElement('td');
  const afterRes = document.createElement('td');
  const afterInline = document.createElement('td');
  const afterSavings = document.createElement('td');

  if (img.state === 'removed') {
    afterType.textContent = ' - ';
    afterSize.textContent = ' - ';
    afterRes.textContent = ' - ';
    afterInline.textContent = ' - ';
    afterSavings.textContent = browser.i18n.getMessage('state_removed');
    afterSavings.style.color = '#c44';
    afterSavings.style.fontWeight = 'bold';
  } else if (img.state === 'excluded') {
    afterType.textContent = ' - ';
    afterSize.textContent = ' - ';
    afterRes.textContent = ' - ';
    afterInline.textContent = ' - ';
    afterSavings.textContent = browser.i18n.getMessage('state_excluded');
    afterSavings.style.color = '#c44';
    afterSavings.style.fontWeight = 'bold';
  } else if (img.state === 'compressed' && img.compressedSize != null) {
    el.afterRow.classList.add('has-result');
    afterType.textContent = (img.compressedFormat || img.format || 'png').toUpperCase();
    afterSize.textContent = PPP_SHARED.formatBytes(img.compressedSize);

    const resW = img.compressedWidth || img.naturalWidth || img.width;
    const resH = img.compressedHeight || img.naturalHeight || img.height;
    afterRes.textContent = (resW && resH) ? `${resW} x ${resH}` : ' - ';

    const inlineW = img.compressedInlineWidth || img.width;
    const inlineH = img.compressedInlineHeight || img.height;
    afterInline.textContent = (inlineW && inlineH) ? `${inlineW} x ${inlineH}` : ' - ';

    if (img.compressedSize < img.size) {
      const saved = img.size - img.compressedSize;
      const pct = ((saved / img.size) * 100).toFixed(1);
      afterSavings.textContent = `-${pct}%`;
    } else {
      afterSavings.textContent = ' - ';
    }
  } else {
    afterType.textContent = ' - ';
    afterSize.textContent = ' - ';
    afterRes.textContent = ' - ';
    afterInline.textContent = ' - ';
    afterSavings.textContent = ' - ';
  }

  el.afterRow.appendChild(afterType);
  el.afterRow.appendChild(afterSize);
  el.afterRow.appendChild(afterRes);
  el.afterRow.appendChild(afterInline);
  el.afterRow.appendChild(afterSavings);

  const isExcluded = img.state === 'excluded';
  el.controlsRow.style.display = isExcluded ? 'none' : '';
  el.checkboxRow.style.display = isExcluded ? 'none' : '';
  el.buttonRow.querySelectorAll('button').forEach((b) => b.remove());

  if (img.state === 'excluded') {
    const unexcludeBtn = document.createElement('button');
    unexcludeBtn.textContent = browser.i18n.getMessage('btn_removeExclusion');
    unexcludeBtn.addEventListener('click', () => PPP_SHARED.unexcludeImage(img.id));
    el.buttonRow.appendChild(unexcludeBtn);
  } else if (img.state === 'removed') {
    const restoreBtn = document.createElement('button');
    restoreBtn.textContent = browser.i18n.getMessage('btn_restore');
    restoreBtn.className = 'restore-btn';
    restoreBtn.addEventListener('click', () => PPP_SHARED.undoImage(img.id));
    el.buttonRow.appendChild(restoreBtn);
  } else if (img.state === 'compressed') {
    const undoBtn = document.createElement('button');
    undoBtn.textContent = browser.i18n.getMessage('btn_undo');
    undoBtn.className = 'danger';
    undoBtn.addEventListener('click', () => PPP_SHARED.undoImage(img.id));
    el.buttonRow.appendChild(undoBtn);
  } else {
    const applyBtn = document.createElement('button');
    applyBtn.textContent = browser.i18n.getMessage('btn_apply');
    applyBtn.className = 'success';
    applyBtn.addEventListener('click', () => PPP_SHARED.applyCompression(img.id));
    el.buttonRow.appendChild(applyBtn);
  }

  if (img.state !== 'removed' && img.state !== 'excluded' && img.hash) {
    const excludeBtn = document.createElement('button');
    excludeBtn.textContent = browser.i18n.getMessage('btn_exclude');
    excludeBtn.className = 'exclude-btn';
    excludeBtn.title = browser.i18n.getMessage('title_exclude');
    excludeBtn.addEventListener('click', () => PPP_SHARED.excludeImage(img.id));
    el.buttonRow.appendChild(excludeBtn);
  }

  // ── Download-Button (Icon, klein) ──────────────────────────────────────────
  if (img.state !== 'excluded') {
    const downloadBtn = document.createElement('button');
    downloadBtn.className = 'download-btn';
    downloadBtn.title = browser.i18n.getMessage('title_download');
    downloadBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
    downloadBtn.addEventListener('click', () => PPP_SHARED.downloadImage(img.id));
    el.buttonRow.appendChild(downloadBtn);
  }

  const isInteractive = img.state !== 'excluded';
  el.resizeSlider.disabled = !isInteractive;
  el.profileSelect.disabled = !isInteractive;
  el.metaCheckbox.disabled = !isInteractive;
  el.scaleCheckbox.disabled = !isInteractive;
  el.keepInlineCheckbox.disabled = !isInteractive;
  el.forcePngCheckbox.disabled = !isInteractive;
};

// ── downloadImage ────────────────────────────────────────────────────────────

PPP_SHARED.downloadImage = async function (imageId) {
  const img = PPP_SHARED.images.find((i) => i.id === imageId);
  if (!img) return;

  try {
    const result = await PPP_SHARED.sendMessage({ type: 'dialog-download', tabId: PPP_SHARED.tabId, imageId });
    if (!result.ok) {
      console.error('[PPP] Download fehlgeschlagen:', result.error);
      return;
    }

    let url, ext, baseName;
    const format = result.format || img.format || 'png';
    ext = format === 'jpeg' ? 'jpg' : format;
    baseName = (result.name || img.name || 'image').replace(/\.(png|jpe?g)$/i, '');

    if (result.dataUrl) {
      url = result.dataUrl;
    } else if (result.bytes) {
      const bytes = new Uint8Array(result.bytes);
      const mime = format === 'jpeg' ? 'image/jpeg' : 'image/png';
      const blob = new Blob([bytes], { type: mime });
      url = URL.createObjectURL(blob);
    } else {
      return;
    }

    const a = document.createElement('a');
    a.href = url;
    a.download = `${baseName}.${ext}`;
    document.body.appendChild(a);
    a.click();
    a.remove();

    if (result.bytes) URL.revokeObjectURL(url);
  } catch (err) {
    console.error('[PPP] Download-Fehler:', err.message);
  }
};

// ── applyCompression ─────────────────────────────────────────────────────────

PPP_SHARED.applyCompression = async function (imageId) {
  const images = PPP_SHARED.images;
  const tabId = PPP_SHARED.tabId;
  const img = images.find((i) => i.id === imageId);
  if (!img) return;

  const el = img._elements;
  const profileName = el.profileSelect.value;
  const resizePercent = el.scaleCheckbox.checked
    ? Math.round(parseFloat(el.resizeSlider.value) * 100)
    : parseInt(el.resizeSlider.value, 10);
  const preserveMeta = el.metaCheckbox.checked;
  const scale = el.scaleCheckbox.checked;
  const keepInlineSize = el.keepInlineCheckbox.checked;
  const forcePng = el.forcePngCheckbox.checked;

  const actionLabel = browser.i18n.getMessage('loading_compressing', img.name);
  PPP_SHARED.showLoading(actionLabel);

  try {
    const result = await PPP_SHARED.sendMessage({ type: 'dialog-compress', tabId, imageId, profileName, resizePercent, preserveMeta, scale, keepInlineSize, forcePng });
    if (result.ok) {
      if (result.removed) {
        img.state = 'removed';
      } else if (result.incompressible) {
        img.state = 'incompressible';
      } else {
        img.state = 'compressed';
        img.compressedSize = result.newSize;
        img.compressedFormat = result.newFormat;
        img.compressedWidth = result.newWidth;
        img.compressedHeight = result.newHeight;
        img.compressedInlineWidth = keepInlineSize ? img.width : (result.newWidth || img.width);
        img.compressedInlineHeight = keepInlineSize ? img.height : (result.newHeight || img.height);
      }
      PPP_SHARED.updateCard(img);
    } else {
      alert(`Fehler bei ${img.name}: ${result.error || 'Unbekannt'}`);
    }
  } catch (err) {
    alert(`Fehler bei ${img.name}: ${err.message}`);
  } finally {
    PPP_SHARED.hideLoading();
    PPP_SHARED.updateSummary();
  }
};

// ── undoImage ────────────────────────────────────────────────────────────────

PPP_SHARED.undoImage = async function (imageId) {
  const images = PPP_SHARED.images;
  const tabId = PPP_SHARED.tabId;
  const img = images.find((i) => i.id === imageId);
  if (!img) return;

  PPP_SHARED.showLoading(browser.i18n.getMessage('loading_restoring', img.name));

  try {
    const result = await PPP_SHARED.sendMessage({ type: 'dialog-undo', tabId, imageId });
    if (result.ok) {
      img.state = 'original';
      img.compressedSize = null;
      img.compressedFormat = null;
      img.compressedWidth = null;
      img.compressedHeight = null;
      img.compressedInlineWidth = null;
      img.compressedInlineHeight = null;
      PPP_SHARED.updateCard(img);
    } else {
      alert(`Fehler bei ${img.name}: ${result.error || 'Unbekannt'}`);
    }
  } catch (err) {
    alert(`Fehler bei ${img.name}: ${err.message}`);
  } finally {
    PPP_SHARED.hideLoading();
    PPP_SHARED.updateSummary();
  }
};

// ── excludeImage ─────────────────────────────────────────────────────────────

PPP_SHARED.excludeImage = async function (imageId) {
  const img = PPP_SHARED.images.find((i) => i.id === imageId);
  if (!img || !img.hash) return;

  try {
    await PPP_SHARED.sendMessage({ type: 'exclude-image', hash: img.hash, tabId: PPP_SHARED.tabId });
    img.state = 'excluded';
    PPP_SHARED.updateCard(img);
    PPP_SHARED.updateSummary();
  } catch (err) {
    console.error('[PPP] Fehler beim Speichern der Ausnahme:', err.message);
  }
};

// ── unexcludeImage ───────────────────────────────────────────────────────────

PPP_SHARED.unexcludeImage = async function (imageId) {
  const img = PPP_SHARED.images.find((i) => i.id === imageId);
  if (!img || !img.hash) return;

  try {
    await PPP_SHARED.sendMessage({ type: 'unexclude-image', hash: img.hash, tabId: PPP_SHARED.tabId });
    img.state = 'original';
    PPP_SHARED.updateCard(img);
    PPP_SHARED.updateSummary();
  } catch (err) {
    console.error('[PPP] Fehler beim Entfernen der Ausnahme:', err.message);
  }
};

// ── setupExcludedButton + showExcludedImages ─────────────────────────────────

PPP_SHARED.setupExcludedButton = function (excludedCount, container) {
  const existing = document.getElementById('btn-show-excluded');
  if (existing) existing.remove();
  if (!excludedCount || excludedCount === 0) return;
  const btn = document.createElement('button');
  btn.id = 'btn-show-excluded';
  btn.textContent = browser.i18n.getMessage('btn_showExcluded', String(excludedCount));
  btn.style.cssText = 'display:block;margin:6px 0;padding:4px 10px;font-size:11px;cursor:pointer;border:1px solid #ffc107;border-radius:3px;background:#fff3cd;color:#856404;';
  btn.addEventListener('click', () => PPP_SHARED.showExcludedImages());
  container.appendChild(btn);
};

PPP_SHARED.showExcludedImages = async function () {
  const btn = document.getElementById('btn-show-excluded');
  if (btn) btn.remove();
  PPP_SHARED.showLoading(browser.i18n.getMessage('loading_showingExclusions'));
  try {
    const result = await PPP_SHARED.sendMessage({ type: 'dialog-show-excluded', tabId: PPP_SHARED.tabId });
    if (result && result.length > 0) {
      PPP_SHARED.emptyState.style.display = 'none';
      for (const img of result) {
        PPP_SHARED.images.push(img);
        const card = PPP_SHARED.renderCard(img);
        PPP_SHARED.imageList.appendChild(card);
        PPP_SHARED.cardElements.set(img.id, card);
      }
      PPP_SHARED.updateSummary();
    }
  } catch (err) {
    console.error('[PPP] Fehler beim Einblenden der Ausnahmen:', err.message);
  } finally {
    PPP_SHARED.hideLoading();
  }
};

// ── compressAll ──────────────────────────────────────────────────────────────

PPP_SHARED.compressAll = async function () {
  const images = PPP_SHARED.images;
  const tabId = PPP_SHARED.tabId;
  const pending = images.filter((img) => img.state !== 'compressed' && img.state !== 'removed' && img.state !== 'excluded');
  const total = pending.length;
  if (total === 0) return;

  for (let i = 0; i < pending.length; i++) {
    const img = pending[i];
    PPP_SHARED.showProgress(i, total, img.name);
    const el = img._elements;
    const profileName = el ? el.profileSelect.value : (PPP_SHARED.defaults[img.category] || PPP_SHARED.profiles[0] || '2. quality optimized');
    const resizePercent = el
      ? (el.scaleCheckbox.checked
          ? Math.round(parseFloat(el.resizeSlider.value) * 100)
          : parseInt(el.resizeSlider.value, 10))
      : 100;
    const preserveMeta = el ? el.metaCheckbox.checked : true;
    const scale = el ? el.scaleCheckbox.checked : false;
    const keepInlineSize = el ? el.keepInlineCheckbox.checked : false;
    const forcePng = el ? el.forcePngCheckbox.checked : false;
    try {
      const result = await PPP_SHARED.sendMessage({ type: 'dialog-compress', tabId, imageId: img.id, profileName, resizePercent, preserveMeta, scale, keepInlineSize, forcePng });
      if (result.ok) {
        if (result.removed) {
          img.state = 'removed';
        } else if (result.incompressible) {
          img.state = 'incompressible';
        } else {
          img.state = 'compressed';
          img.compressedSize = result.newSize;
          img.compressedFormat = result.newFormat;
          img.compressedWidth = result.newWidth;
          img.compressedHeight = result.newHeight;
          img.compressedInlineWidth = keepInlineSize ? img.width : (result.newWidth || img.width);
          img.compressedInlineHeight = keepInlineSize ? img.height : (result.newHeight || img.height);
        }
        PPP_SHARED.updateCard(img);
      }
    } catch (err) {
      console.error(`[PPP] Fehler bei ${img.name}:`, err.message);
    }
  }

  PPP_SHARED.showProgress(total, total, browser.i18n.getMessage('progress_done'));
  PPP_SHARED.updateSummary();
  setTimeout(() => PPP_SHARED.hideLoading(), 500);
};

// ── Kein globaler input-Listener mehr für Range-Slider ──
// Entfernt: updateCard() bei jedem Slider-Tick erzeugte alle Buttons der Karte
// neu (DOM-Churn beim Ziehen), ohne dass sich Tabellenzeilen oder Zustände
// dadurch ändern. Die Slider-Anzeige aktualisiert updateResizeDisplay()
// direkt am Slider (siehe renderCard).
