const DEFAULTS = {
  enabled: true,
  threshold: 0.65,
  minLength: 5,
  // 動画サムネサイズの既定（既定ON＋既定値、統合リスト）
  videoThumbCheckEnabled: true,
  videoThumbAllowedSizes: [ { w: 251, h: 137 }, { w: 138, h: 250 } ],
  videoThumbTolerance: 0,
  videoThumbAction: 'dim',
  // 動画時間チェック
  videoDurationCheckEnabled: false,
  videoDurationMinSec: 0,
  videoDurationMaxSec: 0,
  videoDurationAction: 'dim',
  videoDurationAllowedRanges: [],
  selectors: [
    'article', '.post', '.message', '.comment', '.res', '.reply', '.thread .row', '.body', '.postMessage', '.post-content', '.postBody'
  ],
  action: 'hide',
  templates: [],
  // ignoreRegexes はUIからは操作しない
  ignoreRegexes: [],
  mediaExtLimits: { '.mp4': 100000, '.jpg': 1000, '.png': 1000, '.webp': 100000 }, // legacy
  mediaSkipEnabled: true,
  mediaCapacityRules: [],
  allowedPhrases: [],
  ignoreInsertChars: '',
};

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

function load() {
  chrome.storage.local.get(DEFAULTS, async (cfg) => {
    $('enabled').checked = !!cfg.enabled;
    try { applyCpFeatureDisabledStyles(); } catch {}
    // 対象要素セレクタはUI非表示のため設定しない
    // 無視する行の正規表現はUI非表示
    $('minLength').value = cfg.minLength ?? DEFAULTS.minLength;
    if (document.getElementById('ignoreInsertChars')) {
      document.getElementById('ignoreInsertChars').value = cfg.ignoreInsertChars || '';
    }
    // 一致度は固定(1.00)のためUIからの設定は行わない
    // Map deprecated 'collapse' to 'dim'
    const act = (cfg.action === 'collapse') ? 'dim' : (cfg.action || DEFAULTS.action);
    $('action').value = act;
    renderAllowed(cfg.allowedPhrases || []);
    // reference urls are not persisted separately; leave textarea empty
    const { externalTemplates = [] } = await chrome.storage.local.get({ externalTemplates: [] });
    renderExternalTemplates(externalTemplates);
    const { shortTemplates = [] } = await chrome.storage.local.get({ shortTemplates: [] });
    renderShortTemplates(shortTemplates);
    // Media capacity rules (migrate from legacy mediaExtLimits)
    try {
      const got = await chrome.storage.local.get({ mediaCapacityRules: null, mediaExtLimits: null, mediaSkipEnabled: null, mediaCapacityAction: null, videoThumbLandscape: null, videoThumbPortrait: null, videoThumbLandscapeSizes: null, videoThumbPortraitSizes: null });
      let rules = got.mediaCapacityRules;
      if (!Array.isArray(rules) || !rules.length) {
        // Only migrate if legacy mediaExtLimits actually exists in storage
        const limits = (got.mediaExtLimits && typeof got.mediaExtLimits === 'object') ? got.mediaExtLimits : null;
        if (limits) {
          const tmp = [];
          for (const [ext, n] of Object.entries(limits)) {
            const v = Number(n) || 0; if (v>0) tmp.push({ ext, mode: 'ge', a: v, enabled: true });
          }
          rules = tmp;
          await chrome.storage.local.set({ mediaCapacityRules: rules });
        } else {
          rules = [];
        }
      }
      // master toggle
      const en = ('mediaSkipEnabled' in cfg) ? !!cfg.mediaSkipEnabled : true;
      const el = document.getElementById('mediaSkipEnabled'); if (el) el.checked = en;
      try { applyOthersFeatureDisabledStyles(); } catch {}
      const actSel = document.getElementById('mediaCapacityAction'); if (actSel) actSel.value = (got.mediaCapacityAction === 'hide') ? 'hide' : 'dim';
      renderMediaCapacityList(rules);
      // Cleanup legacy keys that are no longer used
      try {
        const toRemove = ['mediaExtLimits', 'videoThumbLandscape', 'videoThumbPortrait', 'videoThumbLandscapeSizes', 'videoThumbPortraitSizes'];
        chrome.storage.local.remove(toRemove, () => {});
      } catch {}
    } catch { renderMediaCapacityList([]); }
    // 動画サムネサイズ（統合リスト）
    try {
      const en = !!cfg.videoThumbCheckEnabled; const act = (cfg.videoThumbAction === 'hide') ? 'hide' : 'dim';
      document.getElementById('videoThumbCheckEnabled').checked = en;
      document.getElementById('videoThumbAction').value = act;
      // migration: if unified list missing, merge legacy lists and persist
      const lists = await chrome.storage.local.get({ videoThumbAllowedSizes: null, videoThumbLandscapeSizes: [], videoThumbPortraitSizes: [] });
      let allowed = lists.videoThumbAllowedSizes;
      if (!Array.isArray(allowed) || !allowed.length) {
        allowed = uniqSizes([...(lists.videoThumbLandscapeSizes||[]), ...(lists.videoThumbPortraitSizes||[])]);
        if (!allowed.length) allowed = DEFAULTS.videoThumbAllowedSizes;
        await chrome.storage.local.set({ videoThumbAllowedSizes: allowed });
      }
      renderVideoSizeList('videoThumbAllowedList', allowed);
      // tolerance
      const tol = Number(cfg.videoThumbTolerance)||0;
      document.getElementById('videoThumbTolerance').value = tol;
      // video duration
      const vdEn = document.getElementById('videoDurationCheckEnabled'); if (vdEn) vdEn.checked = !!cfg.videoDurationCheckEnabled;
      const vdAct = document.getElementById('videoDurationAction'); if (vdAct) vdAct.value = (cfg.videoDurationAction === 'hide') ? 'hide' : 'dim';
      // 単一最小/最大はUIから撤去（互換のため値は保持）
      const gotVD = await chrome.storage.local.get({ videoDurationAllowedRanges: [] });
      renderVideoDurationRanges(gotVD.videoDurationAllowedRanges || []);
      // Apply disabled styles
      try { applyVideoFeatureDisabledStyles(); } catch {}
    } catch {}
    // 初期表示: エラー・警告（タイムスタンプ抽出失敗ドメイン）
    try {
      const gotWarn = await chrome.storage.local.get({ acpsTsErrors: {} });
      renderWarnings(gotWarn.acpsTsErrors || {});
    } catch { renderWarnings({}); }
  });
}

// 手動ブロックテンプレ表示は廃止

function renderAllowed(list) {
  const box = document.getElementById('allowedList');
  if (!box) return;
  box.innerHTML = '';
  (list || []).forEach((t, idx) => {
    const div = document.createElement('div');
    div.className = 'tpl';
    div.textContent = t;
    const btn = document.createElement('button');
    btn.textContent = '削除';
    btn.style.marginTop = '6px';
    btn.addEventListener('click', async () => {
      const { allowedPhrases = [] } = await chrome.storage.local.get({ allowedPhrases: [] });
      allowedPhrases.splice(idx, 1);
      await chrome.storage.local.set({ allowedPhrases });
      renderAllowed(allowedPhrases);
    });
    div.appendChild(document.createElement('br'));
    div.appendChild(btn);
    box.appendChild(div);
  });
}

function renderExternalTemplates(list) {
  const box = $('extTemplates');
  const cnt = $('extCount');
  const srcSel = $('extSourceSelect');
  if (!box || !cnt) return;
  const items = (list || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((o) => o && o.text);
  // Load stats for display
  let stats = {};
  try { stats = (window.__acps_stats_cache_ext) || {}; } catch {}
  (async () => {
    try {
      const got = await chrome.storage.local.get({ acpsStats: {} });
      stats = got.acpsStats || {};
      window.__acps_stats_cache_ext = stats;
      // Re-render with stats
      doRender();
    } catch { doRender(); }
  })();

  function doRender() {
    cnt.textContent = `（${items.length}件）`;
    box.innerHTML = '';
    // Build source options
    if (srcSel) {
      const map = new Map(); // key: sourceUrl||'' -> {label, count}
      for (const o of items) {
        const key = o.sourceUrl || '';
        let entry = map.get(key);
        if (!entry) {
          let label = '';
          if (o.sourceUrl) {
            try {
              const u = new URL(o.sourceUrl);
              const seg = u.pathname.split('/').filter(Boolean).pop() || u.host;
              label = seg;
            } catch { label = o.sourceUrl; }
          } else {
            label = '(手動/不明)';
          }
          entry = { label, count: 0 };
          map.set(key, entry);
        }
        entry.count++;
      }
      srcSel.innerHTML = '';
      const ph = document.createElement('option');
      ph.value = '__none__';
      ph.textContent = '過去スレを選択…';
      srcSel.appendChild(ph);
      for (const [key, info] of map.entries()) {
        const opt = document.createElement('option');
        opt.value = key || '';
        opt.textContent = `${info.label}（${info.count}件）`;
        srcSel.appendChild(opt);
      }
    }
    items.forEach((o, idx) => {
      const div = document.createElement('div');
      div.className = 'tpl';
      const head = document.createElement('div');
      head.style.display = 'flex'; head.style.justifyContent = 'space-between';
      const src = document.createElement('span');
      let label = '';
      if (o.sourceUrl) {
        try { const u = new URL(o.sourceUrl); const seg = u.pathname.split('/').filter(Boolean).pop() || u.host; label = seg; } catch { label = o.sourceUrl; }
      }
      const stat = stats && stats[o.text] ? stats[o.text] : null;
      const hs = stat && typeof stat.hits === 'number' ? stat.hits : 0;
      const lu = stat && typeof stat.lastUsed === 'number' ? new Date(stat.lastUsed * 1000) : null;
      const luStr = lu ? `${lu.getFullYear()}/${String(lu.getMonth()+1).padStart(2,'0')}/${String(lu.getDate()).padStart(2,'0')} ${String(lu.getHours()).padStart(2,'0')}:${String(lu.getMinutes()).padStart(2,'0')}` : '';
      src.textContent = label ? `過去スレ: ${label}  ｜ hits:${hs}${luStr?` ｜ last:${luStr}`:''}` : `過去スレ: (手動/不明)  ｜ hits:${hs}${luStr?` ｜ last:${luStr}`:''}`;
      const btn = document.createElement('button');
      btn.textContent = '削除';
      btn.addEventListener('click', async () => {
        const { externalTemplates = [] } = await chrome.storage.local.get({ externalTemplates: [] });
        const items2 = (externalTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((x) => x && x.text);
        items2.splice(idx, 1);
        await chrome.storage.local.set({ externalTemplates: items2 });
        renderExternalTemplates(items2);
      });
      head.appendChild(src); head.appendChild(btn);
      const body = document.createElement('div');
      body.textContent = o.text;
      div.appendChild(head);
      div.appendChild(body);
      box.appendChild(div);
    });
  }
}


function renderShortTemplates(list) {
  const box = document.getElementById('shortTemplates');
  const cnt = document.getElementById('shortCount');
  const srcSel = document.getElementById('shortSourceSelect');
  if (!box || !cnt) return;
  const items = (list || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((o) => o && o.text);
  // Load stats for display (hits/last)
  let stats = {};
  (async () => {
    try {
      const got = await chrome.storage.local.get({ acpsStats: {} });
      stats = got.acpsStats || {};
    } catch { stats = {}; }
    doRender();
  })();

  function doRender() {
    cnt.textContent = `（${items.length}件）`;
    box.innerHTML = '';
    if (srcSel) {
      const map = new Map();
      for (const o of items) {
        const key = o.sourceUrl || '';
        let entry = map.get(key);
        if (!entry) {
          let label = '';
          if (o.sourceUrl) {
            try {
              const u = new URL(o.sourceUrl);
              const seg = u.pathname.split('/').filter(Boolean).pop() || u.host;
              label = seg;
            } catch { label = o.sourceUrl; }
          } else { label = '(手動/不明)'; }
          entry = { label, count: 0 };
          map.set(key, entry);
        }
        entry.count++;
      }
      srcSel.innerHTML = '';
      const ph = document.createElement('option');
      ph.value = '__none__';
      ph.textContent = '過去スレを選択…';
      srcSel.appendChild(ph);
      for (const [key, info] of map.entries()) {
        const opt = document.createElement('option');
        opt.value = key || '';
        opt.textContent = `${info.label}（${info.count}件）`;
        srcSel.appendChild(opt);
      }
    }
    items.forEach((o, idx) => {
      const div = document.createElement('div');
      div.className = 'tpl';
      const head = document.createElement('div');
      head.style.display = 'flex'; head.style.justifyContent = 'space-between';
      const src = document.createElement('span');
      let label = '';
      if (o.sourceUrl) {
        try { const u = new URL(o.sourceUrl); const seg = u.pathname.split('/').filter(Boolean).pop() || u.host; label = seg; } catch { label = o.sourceUrl; }
      }
      const stat = stats && stats[o.text] ? stats[o.text] : null;
      const hs = stat && typeof stat.hits === 'number' ? stat.hits : 0;
      const lu = stat && typeof stat.lastUsed === 'number' ? new Date(stat.lastUsed * 1000) : null;
      const luStr = lu ? `${lu.getFullYear()}/${String(lu.getMonth()+1).padStart(2,'0')}/${String(lu.getDate()).padStart(2,'0')} ${String(lu.getHours()).padStart(2,'0')}:${String(lu.getMinutes()).padStart(2,'0')}` : '';
      src.textContent = label ? `過去スレ: ${label}  ｜ hits:${hs}${luStr?` ｜ last:${luStr}`:''}` : `過去スレ: (手動/不明)  ｜ hits:${hs}${luStr?` ｜ last:${luStr}`:''}`;
      const btn = document.createElement('button');
      btn.textContent = '削除';
      btn.addEventListener('click', async () => {
        const got = await chrome.storage.local.get({ shortTemplates: [] });
        const items2 = (got.shortTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((x) => x && x.text);
        items2.splice(idx, 1);
        await chrome.storage.local.set({ shortTemplates: items2 });
        renderShortTemplates(items2);
      });
      head.appendChild(src); head.appendChild(btn);
      const body = document.createElement('div');
      body.textContent = o.text;
      div.appendChild(head);
      div.appendChild(body);
      box.appendChild(div);
    });
  }
}

function save() {
  // 無視する行の正規表現はUIから保存しない
  const payload = {
    enabled: $('enabled').checked,
    // ignoreRegexes は保存しない
    minLength: parseInt($('minLength').value, 10) || DEFAULTS.minLength,
    action: $('action').value,
    mediaSkipEnabled: !!document.getElementById('mediaSkipEnabled')?.checked,
    mediaCapacityAction: (document.getElementById('mediaCapacityAction')?.value === 'hide') ? 'hide' : 'dim',
    ignoreInsertChars: String(document.getElementById('ignoreInsertChars')?.value || ''),
    // 動画サムネサイズ: チェック設定のみ保存（サイズリストは個別操作で即時保存）
    videoThumbCheckEnabled: !!document.getElementById('videoThumbCheckEnabled')?.checked,
    videoThumbTolerance: Number(document.getElementById('videoThumbTolerance')?.value || 0) || 0,
    videoThumbAction: (document.getElementById('videoThumbAction')?.value === 'hide') ? 'hide' : 'dim',
    // 動画時間
    videoDurationCheckEnabled: !!document.getElementById('videoDurationCheckEnabled')?.checked,
    videoDurationAction: (document.getElementById('videoDurationAction')?.value === 'hide') ? 'hide' : 'dim',
    videoDurationMinSec: Number(document.getElementById('videoDurationMinSec')?.value || 0) || 0,
    videoDurationMaxSec: Number(document.getElementById('videoDurationMaxSec')?.value || 0) || 0,
  };
  chrome.storage.local.set(payload, () => {
    $('status').textContent = '保存しました';
    setTimeout(() => { $('status').textContent = ''; }, 1200);
  });
}

function parseSizeToBytes(str) {
  if (!str) return null;
  const m = String(str).trim().match(/([\d.,]+)\s*(B|KB|MB|GB)?/i);
  if (!m) return null;
  let n = parseFloat(m[1].replace(/,/g, ''));
  if (!isFinite(n)) return null;
  const unit = (m[2] || 'B').toUpperCase();
  const mult = unit === 'GB' ? 1024*1024*1024 : unit === 'MB' ? 1024*1024 : unit === 'KB' ? 1024 : 1;
  return Math.round(n * mult);
}

function bytesToHuman(n) {
  if (n == null) return '';
  const units = ['B','KB','MB','GB'];
  let i = 0; let v = Number(n);
  while (v >= 1024 && i < units.length-1) { v /= 1024; i++; }
  return (v % 1 ? v.toFixed(1) : String(v)) + units[i];
}

function textToLimits(text) {
  const out = {};
  (text || '').split(/\r?\n/).forEach(line => {
    const s = line.trim();
    if (!s) return;
    const m = s.match(/^\.?([a-z0-9]+)\s*=\s*([^\s]+)$/i);
    if (!m) return;
    const ext = '.' + m[1].toLowerCase();
    const bytes = parseSizeToBytes(m[2]);
    if (bytes != null) out[ext] = bytes;
  });
  return out;
}

function limitsToText(obj) {
  const lines = [];
  for (const [ext, bytes] of Object.entries(obj || {})) {
    lines.push(`${ext}=${bytesToHuman(bytes)}`);
  }
  return lines.join('\n');
}

document.addEventListener('DOMContentLoaded', () => {
  load();
  $('save').addEventListener('click', save);
  const clearExt = async () => {
    if (!confirm('外部テンプレをすべて削除します。よろしいですか？')) return;
    await chrome.storage.local.set({ externalTemplates: [] });
    const s = document.getElementById('extStatus');
    if (s) { s.textContent = '外部テンプレを削除しました'; setTimeout(() => { s.textContent = ''; }, 1200); }
    renderExternalTemplates([]);
  };
  const btnClearList = $('clearExtList');
  if (btnClearList) btnClearList.addEventListener('click', clearExt);
  const btnDelSrc = $('deleteSource');
  if (btnDelSrc) btnDelSrc.addEventListener('click', async () => {
    const sel = $('extSourceSelect');
    if (!sel || sel.value === '__none__') return;
    const target = sel.value; // full sourceUrl or '' for manual/unknown
    const labelText = sel.options[sel.selectedIndex]?.textContent || '';
    if (!confirm(`選択した過去スレを全削除します。\n${labelText}\nよろしいですか？`)) return;
    const { externalTemplates = [] } = await chrome.storage.local.get({ externalTemplates: [] });
    const items = (externalTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((o) => o && o.text);
    const filtered = items.filter((o) => (o.sourceUrl || '') !== target);
    await chrome.storage.local.set({ externalTemplates: filtered });
    renderExternalTemplates(filtered);
  });
  // Import via file picker
  // 統一バックアップ: エクスポート/インポート
  const backupBtn = document.getElementById('backupExport');
  const backupImpBtn = document.getElementById('backupImport');
  const backupFile = document.getElementById('backupFile');
  if (backupBtn) {
    backupBtn.addEventListener('click', async () => {
      const all = await chrome.storage.local.get(null);
      const settings = {
        enabled: !!all.enabled,
        minLength: all.minLength,
        action: all.action,
        // legacy for互換
        mediaExtLimits: all.mediaExtLimits,
        ignoreInsertChars: all.ignoreInsertChars || '',
        // video thumb
        videoThumbCheckEnabled: !!all.videoThumbCheckEnabled,
        videoThumbAllowedSizes: Array.isArray(all.videoThumbAllowedSizes) ? all.videoThumbAllowedSizes : [],
        videoThumbTolerance: Number(all.videoThumbTolerance) || 0,
        videoThumbAction: all.videoThumbAction || 'dim',
        // video duration
        videoDurationCheckEnabled: !!all.videoDurationCheckEnabled,
        videoDurationAllowedRanges: Array.isArray(all.videoDurationAllowedRanges) ? all.videoDurationAllowedRanges : [],
        videoDurationAction: all.videoDurationAction || 'dim',
        // media capacity regulation
        mediaSkipEnabled: ('mediaSkipEnabled' in all) ? !!all.mediaSkipEnabled : true,
        mediaCapacityRules: Array.isArray(all.mediaCapacityRules) ? all.mediaCapacityRules : [],
        mediaCapacityAction: all.mediaCapacityAction || 'dim',
      };
      const payload = {
        version: 1,
        settings,
        externalTemplates: Array.isArray(all.externalTemplates) ? all.externalTemplates : [],
        shortTemplates: Array.isArray(all.shortTemplates) ? all.shortTemplates : [],
        allowedPhrases: Array.isArray(all.allowedPhrases) ? all.allowedPhrases : [],
      };
      const text = JSON.stringify(payload, null, 2);
      const blob = new Blob([text], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      const ts = new Date();
      const pad = (n) => String(n).padStart(2, '0');
      const name = `acps-backup-${ts.getFullYear()}${pad(ts.getMonth()+1)}${pad(ts.getDate())}-${pad(ts.getHours())}${pad(ts.getMinutes())}${pad(ts.getSeconds())}.json`;
      a.href = url; a.download = name; document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
      const s = document.getElementById('backupStatus');
      if (s) s.textContent = `エクスポート完了: ${name} を保存しました`;
    });
  }
  if (backupImpBtn && backupFile) {
    backupImpBtn.addEventListener('click', () => backupFile.click());
    backupFile.addEventListener('change', async () => {
      const f = backupFile.files && backupFile.files[0];
      if (!f) return;
      let addedExt = 0, addedShort = 0, addedAllowed = 0; let appliedSettings = false;
      try {
        const text = await f.text();
        let data = JSON.parse(text);
        if (Array.isArray(data)) {
          // 旧形式: 通常リスト
          const { externalTemplates = [] } = await chrome.storage.local.get({ externalTemplates: [] });
          const existing = (externalTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((o) => o && o.text);
          const map = new Map(existing.map((o) => [o.text, o]));
          for (const it of data) {
            const o = (typeof it === 'string') ? { text: it } : (it && typeof it.text === 'string' ? { text: it.text, sourceUrl: it.sourceUrl || '' } : null);
            if (!o || !o.text) continue;
            if (!map.has(o.text)) { map.set(o.text, o); addedExt++; }
          }
          await chrome.storage.local.set({ externalTemplates: Array.from(map.values()) });
        } else if (data && typeof data === 'object') {
          // まとめ形式
          const all = await chrome.storage.local.get({ externalTemplates: [], shortTemplates: [], allowedPhrases: [] });
          // externalTemplates
          const exMap = new Map((all.externalTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter(Boolean).map((o) => [o.text, o]));
          for (const it of (data.externalTemplates || [])) {
            const o = (typeof it === 'string') ? { text: it } : (it && typeof it.text === 'string' ? { text: it.text, sourceUrl: it.sourceUrl || '' } : null);
            if (!o || !o.text) continue;
            if (!exMap.has(o.text)) { exMap.set(o.text, o); addedExt++; }
          }
          // shortTemplates
          const shMap = new Map((all.shortTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter(Boolean).map((o) => [o.text, o]));
          for (const it of (data.shortTemplates || [])) {
            const o = (typeof it === 'string') ? { text: it } : (it && typeof it.text === 'string' ? { text: it.text, sourceUrl: it.sourceUrl || '' } : null);
            if (!o || !o.text) continue;
            if (!shMap.has(o.text)) { shMap.set(o.text, o); addedShort++; }
          }
          // allowedPhrases
          const allowSet = new Set((all.allowedPhrases || []).map((s) => String(s)));
          for (const it of (data.allowedPhrases || [])) {
            const v = typeof it === 'string' ? it.trim() : '';
            if (v && !allowSet.has(v)) { allowSet.add(v); addedAllowed++; }
          }
          // settings
          if (data.settings && typeof data.settings === 'object') {
            const s = data.settings;
            const next = {};
            if ('enabled' in s) next.enabled = !!s.enabled;
            if ('minLength' in s) next.minLength = Number(s.minLength) || 0;
            if ('action' in s) next.action = String(s.action);
            if ('mediaExtLimits' in s && s.mediaExtLimits && typeof s.mediaExtLimits === 'object') next.mediaExtLimits = s.mediaExtLimits;
            if ('ignoreInsertChars' in s) next.ignoreInsertChars = String(s.ignoreInsertChars || '');
            // video thumb
            if ('videoThumbCheckEnabled' in s) next.videoThumbCheckEnabled = !!s.videoThumbCheckEnabled;
            if ('videoThumbAllowedSizes' in s && Array.isArray(s.videoThumbAllowedSizes)) next.videoThumbAllowedSizes = s.videoThumbAllowedSizes;
            if ('videoThumbTolerance' in s) next.videoThumbTolerance = Number(s.videoThumbTolerance) || 0;
            if ('videoThumbAction' in s) next.videoThumbAction = String(s.videoThumbAction || 'dim');
            // video duration
            if ('videoDurationCheckEnabled' in s) next.videoDurationCheckEnabled = !!s.videoDurationCheckEnabled;
            if ('videoDurationAllowedRanges' in s && Array.isArray(s.videoDurationAllowedRanges)) next.videoDurationAllowedRanges = s.videoDurationAllowedRanges;
            if ('videoDurationAction' in s) next.videoDurationAction = String(s.videoDurationAction || 'dim');
            // media capacity
            if ('mediaSkipEnabled' in s) next.mediaSkipEnabled = !!s.mediaSkipEnabled;
            if ('mediaCapacityRules' in s && Array.isArray(s.mediaCapacityRules)) next.mediaCapacityRules = s.mediaCapacityRules;
            if ('mediaCapacityAction' in s) next.mediaCapacityAction = String(s.mediaCapacityAction || 'dim');
            if (Object.keys(next).length) { await chrome.storage.local.set(next); appliedSettings = true; }
          }
          await chrome.storage.local.set({
            externalTemplates: Array.from(exMap.values()),
            shortTemplates: Array.from(shMap.values()),
            allowedPhrases: Array.from(allowSet.values()),
          });
        } else {
          throw new Error('不正なバックアップ形式です');
        }
        const st = document.getElementById('backupStatus');
        if (st) st.textContent = `インポート完了: 通常+${addedExt}件, 短文+${addedShort}件, 重複許可+${addedAllowed}件${appliedSettings ? ', 設定を適用' : ''}`;
      } catch (e) {
        const st = document.getElementById('backupStatus');
        if (st) st.textContent = `インポート失敗: ${e && e.message ? e.message : '不正なファイル'}`;
      } finally {
        backupFile.value = '';
      }
    });
  }

  // 1行テンプレ追加（Enterで追加）
  const addInput = document.getElementById('addTplText');
  const addBtn = document.getElementById('addTplBtn');
  async function addAllowedPhraseFromInput() {
    const v = (addInput?.value || '').trim();
    if (!v) return;
    const { allowedPhrases = [] } = await chrome.storage.local.get({ allowedPhrases: [] });
    const next = [...allowedPhrases, v];
    await chrome.storage.local.set({ allowedPhrases: next });
    renderAllowed(next);
    if (addInput) addInput.value = '';
    const s = document.getElementById('extStatus');
    if (s) { s.textContent = '重複許可リストに追加しました'; setTimeout(() => { s.textContent = ''; }, 1200); }
  }
  if (addBtn) addBtn.addEventListener('click', addAllowedPhraseFromInput);

  // 重複許可リスト（allowedPhrases）は統一バックアップに一本化（個別操作は削除）

  const clearAllowedBtn = document.getElementById('clearAllowed');
  if (clearAllowedBtn) {
    clearAllowedBtn.addEventListener('click', async () => {
      if (!confirm('重複許可リストをすべて削除します。よろしいですか？')) return;
      await chrome.storage.local.set({ allowedPhrases: [] });
      renderAllowed([]);
    });
  }
  // externalTemplates 個別エクスポートは廃止（統一バックアップへ）

  // Auto-update lists when storage changes in other tabs/pages
  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== 'local') return;
    if (Object.prototype.hasOwnProperty.call(changes, 'externalTemplates')) {
      const next = changes.externalTemplates?.newValue || [];
      try { renderExternalTemplates(next); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'shortTemplates')) {
      const next = changes.shortTemplates?.newValue || [];
      try { renderShortTemplates(next); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'allowedPhrases')) {
      const next = changes.allowedPhrases?.newValue || [];
      try { renderAllowed(next); } catch {}
    }
    // Live reflect stats updates as hits accumulate in background/content
    if (Object.prototype.hasOwnProperty.call(changes, 'acpsStats')) {
      chrome.storage.local.get({ externalTemplates: [], shortTemplates: [] }, (got) => {
        try { renderExternalTemplates(got.externalTemplates || []); } catch {}
        try { renderShortTemplates(got.shortTemplates || []); } catch {}
      });
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'acpsTsErrors')) {
      const next = changes.acpsTsErrors?.newValue || {};
      try { renderWarnings(next); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'videoThumbAllowedSizes')) {
      const next = changes.videoThumbAllowedSizes?.newValue || [];
      try { renderVideoSizeList('videoThumbAllowedList', next); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'videoThumbTolerance')) {
      try { document.getElementById('videoThumbTolerance').value = Number(changes.videoThumbTolerance.newValue)||0; } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'videoDurationAllowedRanges')) {
      const next = changes.videoDurationAllowedRanges?.newValue || [];
      try { renderVideoDurationRanges(next); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'mediaCapacityRules')) {
      const list = changes.mediaCapacityRules?.newValue || [];
      try { renderMediaCapacityList(list); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'videoThumbCheckEnabled')) {
      try { document.getElementById('videoThumbCheckEnabled').checked = !!changes.videoThumbCheckEnabled.newValue; } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'videoThumbAction')) {
      try { document.getElementById('videoThumbAction').value = (changes.videoThumbAction.newValue === 'hide') ? 'hide' : 'dim'; } catch {}
    }
  });

  // 短文リスト: 出典ごと削除 / 全削除
  const btnDelShort = $('deleteShortSource');
  if (btnDelShort) btnDelShort.addEventListener('click', async () => {
    const sel = $('shortSourceSelect');
    if (!sel || sel.value === '__none__') return;
    const target = sel.value;
    const labelText = sel.options[sel.selectedIndex]?.textContent || '';
    if (!confirm(`選択した過去スレを全削除します。\n${labelText}\nよろしいですか？`)) return;
    const got = await chrome.storage.local.get({ shortTemplates: [] });
    const items = (got.shortTemplates || []).map((v) => (typeof v === 'string' ? { text: v } : v)).filter((o) => o && o.text);
    const filtered = items.filter((o) => (o.sourceUrl || '') !== target);
    await chrome.storage.local.set({ shortTemplates: filtered });
    renderShortTemplates(filtered);
  });
  const btnClearShort = $('clearShortList');
  if (btnClearShort) btnClearShort.addEventListener('click', async () => {
    if (!confirm('短文リストをすべて削除します。よろしいですか？')) return;
    await chrome.storage.local.set({ shortTemplates: [] });
    renderShortTemplates([]);
    const s = document.getElementById('shortStatus');
    if (s) { s.textContent = '短文リストを削除しました'; setTimeout(() => { s.textContent = ''; }, 1200); }
  });

  // 対象ドメイン追加機能は無効化（UI撤去）
  // エラー・警告: 全クリア
  const btnClearWarn = document.getElementById('clearWarnings');
  if (btnClearWarn) btnClearWarn.addEventListener('click', async () => {
    await chrome.storage.local.set({ acpsTsErrors: {} });
    renderWarnings({});
    const s = document.getElementById('warnStatus');
    if (s) { s.textContent = '警告をクリアしました'; setTimeout(() => { s.textContent = ''; }, 1200); }
  });

  // 開発者向け: ヒット統計のリセット（全エントリの hits/lastUsed を 0 ）
  const btnResetHits = document.getElementById('resetHits');
  if (btnResetHits) btnResetHits.addEventListener('click', async () => {
    if (!confirm('ヒット統計（hits/last）を全て0にします。よろしいですか？')) return;
    try {
      const got = await chrome.storage.local.get({ acpsStats: {} });
      const stats = got.acpsStats && typeof got.acpsStats === 'object' ? got.acpsStats : {};
      const next = {};
      for (const k of Object.keys(stats)) next[k] = { hits: 0, lastUsed: 0 };
      await chrome.storage.local.set({ acpsStats: next });
      const s = document.getElementById('devStatus');
      if (s) s.textContent = 'ヒット統計をリセットしました';
      // 再描画（onChangedでも反映されるが即時反映）
      const lists = await chrome.storage.local.get({ externalTemplates: [], shortTemplates: [] });
      try { renderExternalTemplates(lists.externalTemplates || []); } catch {}
      try { renderShortTemplates(lists.shortTemplates || []); } catch {}
      setTimeout(() => { if (s) s.textContent = ''; }, 1200);
    } catch (e) {
      const s = document.getElementById('devStatus');
      if (s) s.textContent = 'リセットに失敗しました';
    }
  });
});

// ---- Media capacity rules helpers ----
function normalizeExt(s) {
  s = String(s || '').trim().toLowerCase();
  if (!s) return '';
  if (!s.startsWith('.')) s = '.' + s;
  return s;
}

function normalizeMediaRules(list) {
  const map = new Map();
  for (const r of (list || [])) {
    if (!r) continue;
    const ext = normalizeExt(r.ext);
    if (!ext) continue;
    const mode = String(r.mode || '').toLowerCase();
    if (!['lt','ge','range'].includes(mode)) continue;
    const a = Math.max(0, Number(r.a) || 0);
    const b = Math.max(0, Number(r.b) || 0);
    const key = mode === 'range' ? `${ext}|${mode}|${a}-${b}` : `${ext}|${mode}|${a}`;
    const enabled = !!(r.enabled !== false);
    map.set(key, { ext, mode, a, b, enabled });
  }
  const arr = Array.from(map.values());
  // sort by ext, then mode, then value string lexicographically
  arr.sort((x,y) => {
    const ex = x.ext.localeCompare(y.ext);
    if (ex) return ex;
    const mx = x.mode.localeCompare(y.mode); if (mx) return mx;
    const vx = x.mode === 'range' ? `${x.a}-${x.b}` : `${x.a}`;
    const vy = y.mode === 'range' ? `${y.a}-${y.b}` : `${y.a}`;
    return vx.localeCompare(vy);
  });
  return arr;
}

function renderMediaCapacityList(list) {
  const box = document.getElementById('mediaCapacityList');
  if (!box) return;
  const items = normalizeMediaRules(list);
  box.innerHTML = '';
  if (!items.length) {
    const p = document.createElement('div'); p.className = 'tpl'; p.textContent = '（未登録）'; box.appendChild(p); return;
  }
  items.forEach((o) => {
    const div = document.createElement('div'); div.className = 'tpl';
    const head = document.createElement('div'); head.style.display = 'flex'; head.style.justifyContent = 'space-between';
    const left = document.createElement('label'); left.style.display = 'inline-flex'; left.style.alignItems = 'center'; left.style.gap = '8px';
    const chk = document.createElement('input'); chk.type = 'checkbox'; chk.checked = (o.enabled !== false);
    chk.addEventListener('change', async () => {
      const got = await chrome.storage.local.get({ mediaCapacityRules: [] });
      const arr = normalizeMediaRules(got.mediaCapacityRules || []);
      const next = arr.map(x => ({ ...x }));
      const idx = next.findIndex(x => (x.ext === o.ext && x.mode === o.mode && x.a === o.a && x.b === o.b));
      if (idx >= 0) next[idx].enabled = !!chk.checked;
      await chrome.storage.local.set({ mediaCapacityRules: next });
      renderMediaCapacityList(next);
    });
    const modeLabel = o.mode === 'lt' ? '未満' : (o.mode === 'ge' ? '以上' : 'から〜まで');
    const valueStr = o.mode === 'range' ? `${o.a}〜${o.b}B` : `${o.a}B`;
    const cap = document.createElement('span'); cap.textContent = `${o.ext} ${modeLabel} ${valueStr}`;
    left.appendChild(chk); left.appendChild(cap);
    const del = document.createElement('button'); del.textContent = '削除';
    del.addEventListener('click', async () => {
      const got = await chrome.storage.local.get({ mediaCapacityRules: [] });
      const arr = normalizeMediaRules(got.mediaCapacityRules || []);
      const filtered = arr.filter(x => !(x.ext === o.ext && x.mode === o.mode && x.a === o.a && x.b === o.b));
      await chrome.storage.local.set({ mediaCapacityRules: filtered });
      renderMediaCapacityList(filtered);
    });
    head.appendChild(left); head.appendChild(del);
    div.appendChild(head); box.appendChild(div);
  });
}

// Add media capacity rule
document.addEventListener('DOMContentLoaded', () => {
  const btn = document.getElementById('mediaCapAdd');
  if (btn) btn.addEventListener('click', async () => {
    const ext = normalizeExt(document.getElementById('mediaCapExtAdd')?.value || '');
    const mode = String(document.getElementById('mediaCapModeAdd')?.value || 'ge');
    const a = Math.max(0, Number(document.getElementById('mediaCapAAdd')?.value || 0));
    const b = Math.max(0, Number(document.getElementById('mediaCapBAdd')?.value || 0));
    if (!ext) return;
    if ((mode === 'lt' || mode === 'ge') && a === 0) return;
    if (mode === 'range' && a === 0 && b === 0) return;
    const got = await chrome.storage.local.get({ mediaCapacityRules: [] });
    const arr = normalizeMediaRules([...(got.mediaCapacityRules || []), { ext, mode, a, b, enabled: true }]);
    await chrome.storage.local.set({ mediaCapacityRules: arr });
    renderMediaCapacityList(arr);
    try {
      document.getElementById('mediaCapExtAdd').value = '';
      document.getElementById('mediaCapAAdd').value = '';
      document.getElementById('mediaCapBAdd').value = '';
    } catch {}
  });
});

function uniqSizes(list) {
  const map = new Map();
  for (const o of (list || [])) {
    const w = Number(o && o.w)||0; const h = Number(o && o.h)||0;
    if (!(w>0 && h>0)) continue;
    const key = `${w}x${h}`;
    const enabled = !!(o && (o.enabled !== false));
    map.set(key, { w, h, enabled });
  }
  const out = Array.from(map.values());
  out.sort((a, b) => (a.w - b.w) || (a.h - b.h));
  return out;
}

function renderVideoSizeList(containerId, list) {
  const box = document.getElementById(containerId);
  if (!box) return;
  const items = uniqSizes(list);
  box.innerHTML = '';
  if (!items.length) {
    const p = document.createElement('div');
    p.className = 'tpl';
    p.textContent = '（未登録）';
    box.appendChild(p);
  }
  items.forEach((o, idx) => {
    const div = document.createElement('div');
    div.className = 'tpl';
    const head = document.createElement('div');
    head.style.display = 'flex'; head.style.justifyContent = 'space-between';
    const left = document.createElement('label');
    left.style.display = 'inline-flex'; left.style.alignItems = 'center'; left.style.gap = '8px';
    const chk = document.createElement('input'); chk.type = 'checkbox'; chk.checked = (o.enabled !== false);
    chk.addEventListener('change', async () => {
      const got = await chrome.storage.local.get({ videoThumbAllowedSizes: [] });
      const arr = Array.isArray(got.videoThumbAllowedSizes) ? got.videoThumbAllowedSizes.slice() : [];
      const key = `${o.w}x${o.h}`;
      const next = uniqSizes(arr).map(x => ({ w: x.w, h: x.h, enabled: x.enabled }));
      const i = next.findIndex(x => `${x.w}x${x.h}` === key);
      if (i >= 0) next[i].enabled = !!chk.checked;
      await chrome.storage.local.set({ videoThumbAllowedSizes: next });
      renderVideoSizeList(containerId, next);
    });
    const cap = document.createElement('span'); cap.textContent = `${o.w} x ${o.h}`;
    left.appendChild(chk); left.appendChild(cap);
    const btn = document.createElement('button');
    btn.textContent = '削除';
    btn.addEventListener('click', async () => {
      const got = await chrome.storage.local.get({ videoThumbAllowedSizes: [] });
      const arr = Array.isArray(got.videoThumbAllowedSizes) ? got.videoThumbAllowedSizes : [];
      const filtered = uniqSizes(arr).filter(x => !(x.w === o.w && x.h === o.h));
      await chrome.storage.local.set({ videoThumbAllowedSizes: filtered });
      renderVideoSizeList(containerId, filtered);
    });
    head.appendChild(left); head.appendChild(btn);
    div.appendChild(head);
    box.appendChild(div);
  });
}

document.addEventListener('DOMContentLoaded', () => {
  // Tabs
  try {
    const buttons = Array.from(document.querySelectorAll('.tab-btn'));
    const panes = new Map(Array.from(document.querySelectorAll('.tab-pane')).map(p => [p.id, p]));
    const activate = (id) => {
      buttons.forEach(b => b.classList.toggle('active', b.getAttribute('data-tab') === id));
      panes.forEach((el, pid) => el.classList.toggle('active', pid === id));
      try { localStorage.setItem('acps_opts_tab', id); } catch {}
    };
    buttons.forEach(b => b.addEventListener('click', () => activate(b.getAttribute('data-tab'))));
    const last = (() => { try { return localStorage.getItem('acps_opts_tab'); } catch { return null; } })();
    if (last && panes.has(last)) activate(last);
  } catch {}

  // Add allowed size
  const add = document.getElementById('videoThumbAdd');
  if (add) add.addEventListener('click', async () => {
    const w = Number(document.getElementById('videoThumbWAdd')?.value || 0);
    const h = Number(document.getElementById('videoThumbHAdd')?.value || 0);
    if (!(w>0 && h>0)) return;
    const got = await chrome.storage.local.get({ videoThumbAllowedSizes: [] });
    const arr = uniqSizes([...(got.videoThumbAllowedSizes||[]), { w, h }]);
    await chrome.storage.local.set({ videoThumbAllowedSizes: arr });
    renderVideoSizeList('videoThumbAllowedList', arr);
    try { document.getElementById('videoThumbWAdd').value = ''; document.getElementById('videoThumbHAdd').value = ''; } catch {}
  });

  // Save from video tab
  const saveVideo = document.getElementById('saveVideo');
  if (saveVideo) saveVideo.addEventListener('click', () => {
    try { save(); const s = document.getElementById('statusVideo'); if (s) { s.textContent = '保存しました'; setTimeout(() => { s.textContent = ''; }, 1200); } } catch {}
  });
  // Greyout feature controls when master toggles are OFF
  try {
    const chkThumb = document.getElementById('videoThumbCheckEnabled');
    const chkDur = document.getElementById('videoDurationCheckEnabled');
    if (chkThumb) chkThumb.addEventListener('change', () => { try { applyVideoFeatureDisabledStyles(); } catch {} });
    if (chkDur) chkDur.addEventListener('change', () => { try { applyVideoFeatureDisabledStyles(); } catch {} });
    applyVideoFeatureDisabledStyles();
  } catch {}

  // Greyout CP controls when extension enabled is OFF
  try {
    const chkEn = document.getElementById('enabled');
    if (chkEn) chkEn.addEventListener('change', () => { try { applyCpFeatureDisabledStyles(); } catch {} });
    applyCpFeatureDisabledStyles();
  } catch {}

  // Greyout Others controls based on mediaSkipEnabled
  try {
    const chkMedia = document.getElementById('mediaSkipEnabled');
    if (chkMedia) chkMedia.addEventListener('change', () => { try { applyOthersFeatureDisabledStyles(); } catch {} });
    applyOthersFeatureDisabledStyles();
  } catch {}

  // Media capacity: clear all
  try {
    const btn = document.getElementById('mediaCapClear');
    if (btn) btn.addEventListener('click', async () => {
      await chrome.storage.local.set({ mediaCapacityRules: [] });
      renderMediaCapacityList([]);
    });
  } catch {}
});

// Apply/clear disabled style for video feature control blocks
function applyVideoFeatureDisabledStyles() {
  const thumbCtl = document.getElementById('videoThumbControls');
  const durCtl = document.getElementById('videoDurationControls');
  const enThumb = !!document.getElementById('videoThumbCheckEnabled')?.checked;
  const enDur = !!document.getElementById('videoDurationCheckEnabled')?.checked;
  if (thumbCtl) thumbCtl.classList.toggle('section-disabled', !enThumb);
  if (durCtl) durCtl.classList.toggle('section-disabled', !enDur);
}

function applyCpFeatureDisabledStyles() {
  const cpCtl = document.getElementById('cpControls');
  const en = !!document.getElementById('enabled')?.checked;
  if (cpCtl) cpCtl.classList.toggle('section-disabled', !en);
}

function applyOthersFeatureDisabledStyles() {
  const ctl = document.getElementById('mediaSkipControls');
  const en = !!document.getElementById('mediaSkipEnabled')?.checked;
  if (ctl) ctl.classList.toggle('section-disabled', !en);
}

// Reflect remote changes too
try {
  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== 'local') return;
    if (Object.prototype.hasOwnProperty.call(changes, 'videoThumbCheckEnabled') || Object.prototype.hasOwnProperty.call(changes, 'videoDurationCheckEnabled')) {
      try { applyVideoFeatureDisabledStyles(); } catch {}
    }
    if (Object.prototype.hasOwnProperty.call(changes, 'enabled')) {
      try { applyCpFeatureDisabledStyles(); } catch {}
    }
  });
} catch {}

function normRanges(list) {
  const map = new Map();
  for (const it of (list || [])) {
    const mi = Math.max(0, Number(it && it.min) || 0);
    const ma = Math.max(0, Number(it && it.max) || 0);
    const key = `${mi}-${ma}`;
    const enabled = !!(it && (it.enabled !== false));
    map.set(key, { min: mi, max: ma, enabled });
  }
  const out = Array.from(map.values());
  out.sort((a, b) => (a.min - b.min) || (a.max - b.max));
  return out;
}

function renderVideoDurationRanges(list) {
  const box = document.getElementById('videoDurationAllowedList');
  if (!box) return;
  const items = normRanges(list);
  box.innerHTML = '';
  if (!items.length) {
    const p = document.createElement('div');
    p.className = 'tpl';
    p.textContent = '（未登録）';
    box.appendChild(p);
    return;
  }
  items.forEach((o, idx) => {
    const div = document.createElement('div');
    div.className = 'tpl';
    const head = document.createElement('div');
    head.style.display = 'flex'; head.style.justifyContent = 'space-between';
    const left = document.createElement('label');
    left.style.display = 'inline-flex'; left.style.alignItems = 'center'; left.style.gap = '8px';
    const chk = document.createElement('input'); chk.type = 'checkbox'; chk.checked = (o.enabled !== false);
    chk.addEventListener('change', async () => {
      const got = await chrome.storage.local.get({ videoDurationAllowedRanges: [] });
      const arr = Array.isArray(got.videoDurationAllowedRanges) ? got.videoDurationAllowedRanges.slice() : [];
      const key = `${o.min}-${o.max}`;
      const next = normRanges(arr).map(x => ({ min: x.min, max: x.max, enabled: x.enabled }));
      const i = next.findIndex(x => `${x.min}-${x.max}` === key);
      if (i >= 0) next[i].enabled = !!chk.checked;
      await chrome.storage.local.set({ videoDurationAllowedRanges: next });
      renderVideoDurationRanges(next);
    });
    const cap = document.createElement('span'); cap.textContent = `${o.min || 0}s 〜 ${o.max || 0}s`;
    left.appendChild(chk); left.appendChild(cap);
    const btn = document.createElement('button');
    btn.textContent = '削除';
    btn.addEventListener('click', async () => {
      const got = await chrome.storage.local.get({ videoDurationAllowedRanges: [] });
      const arr = Array.isArray(got.videoDurationAllowedRanges) ? got.videoDurationAllowedRanges : [];
      const filtered = normRanges(arr).filter(x => !(x.min === o.min && x.max === o.max));
      await chrome.storage.local.set({ videoDurationAllowedRanges: filtered });
      renderVideoDurationRanges(filtered);
    });
    head.appendChild(left); head.appendChild(btn);
    div.appendChild(head);
    box.appendChild(div);
  });
}

document.addEventListener('DOMContentLoaded', () => {
  const btn = document.getElementById('videoDurationAdd');
  if (btn) btn.addEventListener('click', async () => {
    const mi = Math.max(0, Number(document.getElementById('videoDurationMinAdd')?.value || 0));
    const ma = Math.max(0, Number(document.getElementById('videoDurationMaxAdd')?.value || 0));
    const got = await chrome.storage.local.get({ videoDurationAllowedRanges: [] });
    const arr = normRanges([...(got.videoDurationAllowedRanges || []), { min: mi, max: ma }]);
    await chrome.storage.local.set({ videoDurationAllowedRanges: arr });
    renderVideoDurationRanges(arr);
    try { document.getElementById('videoDurationMinAdd').value = ''; document.getElementById('videoDurationMaxAdd').value = ''; } catch {}
  });
});

function renderWarnings(map) {
  const box = document.getElementById('warnDomains');
  if (!box) return;
  const list = [];
  try {
    for (const [host, ent] of Object.entries(map || {})) {
      const c = ent && typeof ent.count === 'number' ? ent.count : 0;
      const lu = ent && typeof ent.lastSeen === 'number' ? new Date(ent.lastSeen * 1000) : null;
      list.push({ host, count: c, lastMs: lu ? lu.getTime() : 0, luStr: lu ? `${lu.getFullYear()}/${String(lu.getMonth()+1).padStart(2,'0')}/${String(lu.getDate()).padStart(2,'0')} ${String(lu.getHours()).padStart(2,'0')}:${String(lu.getMinutes()).padStart(2,'0')}` : '' });
    }
  } catch {}
  list.sort((a,b) => b.lastMs - a.lastMs);
  box.innerHTML = '';
  if (!list.length) {
    const p = document.createElement('div');
    p.textContent = '警告はありません';
    box.appendChild(p);
    return;
  }
  list.forEach((e) => {
    const div = document.createElement('div');
    div.className = 'tpl';
    const head = document.createElement('div');
    head.style.display = 'flex'; head.style.justifyContent = 'space-between';
    const left = document.createElement('span');
    left.textContent = `${e.host} ｜ count:${e.count}${e.luStr?` ｜ last:${e.luStr}`:''}`;
    const btn = document.createElement('button');
    btn.textContent = '削除';
    btn.addEventListener('click', async () => {
      const got = await chrome.storage.local.get({ acpsTsErrors: {} });
      const m = got.acpsTsErrors || {};
      delete m[e.host];
      await chrome.storage.local.set({ acpsTsErrors: m });
      renderWarnings(m);
    });
    head.appendChild(left); head.appendChild(btn);
    div.appendChild(head);
    box.appendChild(div);
  });
}
