<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EmbyToolkit — User Favourites</title>
<link rel="stylesheet" href="/static/app-theme.css">
<style>
  :root {
    --bg: #0d0f12;
    --surface: #13171f;
    --surface2: #1a2030;
    --surface3: #222840;
    --border: #252d3d;
    --border-active: #14b8a6;
    --text: #e8ecf4;
    --text-2: #8892a8;
    --text-3: #4e5a70;
    --accent: #14b8a6;
    --accent-h: #2dd4bf;
    --accent-glow: rgba(20,184,166,0.12);
    --green: #34d399;
    --green-bg: rgba(52,211,153,0.08);
    --red: #f87171;
    --r: 10px;
    --r-lg: 14px;
  }

  * { box-sizing: border-box; }
  html, body { height: 100%; }
  body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: 'Inter', system-ui, sans-serif;
    display: grid;
    grid-template-columns: 240px 1fr;
    height: 100vh;
    height: 100svh;
    overflow: hidden;
  }

  /* ── Mobile top bar ── */
  .app-topbar {
    display: none;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 30;
  }
  .app-topbar-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; letter-spacing: -0.03em; }
  .icon-btn {
    width: 38px; height: 38px;
    display: inline-grid; place-items: center;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--surface2);
    color: var(--text);
    cursor: pointer;
  }
  .icon-btn:hover { background: var(--surface3); }
  .nav-scrim {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.55);
    z-index: 40; opacity: 0;
    transition: opacity 0.18s ease;
  }
  .nav-scrim.show { opacity: 1; }

  /* ── App Nav Sidebar ── */
  .app-nav { display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--border); overflow: hidden; }
  .app-nav-brand { padding: 18px 16px; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
  .app-nav-logo { width: 32px; height: 32px; background: var(--accent); border-radius: 8px; display: grid; place-items: center; color: #fff; flex-shrink: 0; }
  .app-nav-name { font-size: 15px; font-weight: 700; letter-spacing: -0.03em; }
  .app-nav-items { list-style: none; padding: 8px; flex: 1; overflow-y: auto; margin: 0; }
  .app-nav-item { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px; color: var(--text-2); text-decoration: none; font-size: 13px; font-weight: 500; transition: background 0.12s, color 0.12s; margin-bottom: 1px; }
  .app-nav-item:hover { background: var(--surface2); color: var(--text); }
  .app-nav-item.active { background: var(--accent); color: #fff; }
  .app-nav-footer { padding: 14px 16px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-2); flex-shrink: 0; }
  .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 6px var(--green); }
  .dot.off { background: var(--red); box-shadow: 0 0 6px var(--red); }

  /* ── Page ── */
  .page { padding: 24px clamp(16px, 2.4vw, 36px) 32px; overflow-y: auto; }
  .page-inner { width: 100%; max-width: 1680px; margin: 0 auto; }
  .hero { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 18px; }
  .hero-copy h2 { margin: 0 0 8px; font-size: clamp(24px, 3vw, 34px); line-height: 1; letter-spacing: -0.04em; }
  .hero-copy p { margin: 0; max-width: 820px; color: var(--text-2); font-size: 14px; line-height: 1.5; }

  /* ── Sections ── */
  .section { margin-bottom: 18px; }
  .section-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-3); margin: 0 0 10px 2px; }

  .user-picker { display: flex; flex-wrap: wrap; gap: 8px; }
  .user-chip {
    display: inline-flex; align-items: center; gap: 9px;
    padding: 8px 14px 8px 10px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface2);
    color: var(--text-2);
    font-family: inherit; font-size: 13px; font-weight: 600;
    cursor: pointer;
    transition: background 0.14s, border-color 0.14s, color 0.14s;
  }
  .user-chip:hover { background: var(--surface3); color: var(--text); }
  .user-chip .avatar { width: 26px; height: 26px; font-size: 10px; }
  .user-chip .count { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; }
  .user-chip.active { background: var(--accent-glow); border-color: var(--accent); color: var(--text); }
  .user-chip .pl-ico { display: inline-grid; place-items: center; color: var(--text-3); }
  .user-chip.active .pl-ico { color: var(--accent-h); }
  .chip-badge {
    font-size: 9px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    padding: 2px 7px; border-radius: 999px;
    background: var(--accent-glow); color: var(--accent-h); border: 1px solid rgba(54,214,224,0.3);
  }
  .actions-note {
    margin-top: 10px; font-size: 12px; color: #f6d98c;
    background: rgba(243,201,105,0.08); border: 1px solid rgba(243,201,105,0.25);
    border-radius: 10px; padding: 8px 12px;
  }

  .actions-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: linear-gradient(180deg, rgba(26,32,48,0.95), rgba(19,23,31,0.95));
  }
  .actions-group { display: flex; align-items: center; gap: 8px; }
  .actions-sep { width: 1px; align-self: stretch; background: var(--border); margin: 2px 4px; }
  .actions-spacer { flex: 1; }
  .target-field { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-2); }
  .target-field input {
    width: 64px; padding: 7px 9px; border-radius: 9px;
    border: 1px solid var(--border); background: var(--surface2); color: var(--text);
    font-family: inherit; font-size: 13px; font-variant-numeric: tabular-nums; text-align: center;
  }

  .btn {
    min-height: 36px; padding: 0 14px; border-radius: 10px;
    border: 1px solid var(--border); background: var(--surface2); color: var(--text);
    font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
    display: inline-flex; align-items: center; gap: 7px;
    transition: background 0.12s, border-color 0.12s, opacity 0.12s;
  }
  .btn:hover:not(:disabled) { background: var(--surface3); }
  .btn:disabled { opacity: 0.4; cursor: not-allowed; }
  .btn-primary { background: var(--accent); border-color: var(--accent); color: #04181b; }
  .btn-danger { color: #ffb4b2; border-color: rgba(240,114,111,0.35); background: rgba(240,114,111,0.12); }
  .btn-green { background: var(--green); border-color: var(--green); color: #08120e; }

  .stat-row { margin-bottom: 18px; }

  .panel {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: linear-gradient(180deg, rgba(19,23,31,0.98), rgba(13,15,18,0.98));
    overflow: hidden;
  }
  .panel-head {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 13px 16px; border-bottom: 1px solid var(--border);
  }
  .panel-head h3 { margin: 0; font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
  .panel-head .muted { font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; }
  .table-scroll { overflow-x: auto; }
  .item-id { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; font-family: ui-monospace, "SF Mono", Menlo, monospace; }

  .preview-card {
    margin-top: 14px;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    overflow: hidden;
  }
  .preview-head {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 16px; border-bottom: 1px solid var(--border);
  }
  .preview-head h4 { margin: 0; font-size: 13px; font-weight: 700; }
  .preview-flag {
    font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    padding: 3px 9px; border-radius: 999px;
  }
  .preview-flag.dry { background: var(--amber-bg, rgba(243,201,105,0.12)); border: 1px solid rgba(243,201,105,0.3); color: #f6d98c; }
  .preview-flag.applied { background: var(--green-bg); border: 1px solid rgba(70,217,154,0.3); color: #b7f0d4; }
  .preview-body { padding: 6px 8px 10px; max-height: 320px; overflow-y: auto; }
  .preview-list { list-style: none; margin: 0; padding: 0; }
  .preview-list li {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: 8px; font-size: 13px;
  }
  .preview-list li:hover { background: var(--surface2); }
  .preview-list .pl-title { flex: 1; min-width: 0; color: var(--text); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .preview-list .pl-score { font-size: 11px; color: var(--accent-h); font-variant-numeric: tabular-nums; }
  .preview-list .pl-id { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; }
  .preview-empty { padding: 24px; text-align: center; color: var(--text-3); font-size: 13px; }

  .empty {
    padding: 54px 20px; border: 1px dashed var(--border); border-radius: var(--r-lg);
    text-align: center; color: var(--text-3); font-size: 14px;
  }

  .toast {
    position: fixed; bottom: 20px; right: 20px;
    padding: 11px 18px; border-radius: 12px;
    font-size: 13px; font-weight: 500; z-index: 200;
    transform: translateY(80px); opacity: 0;
    transition: all 0.25s cubic-bezier(0.4,0,0.2,1); pointer-events: none;
  }
  .toast.show { transform: translateY(0); opacity: 1; }

  .spinner { width: 30px; height: 30px; border: 2.5px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; margin: 0 auto 12px; }
  @keyframes spin { to { transform: rotate(360deg); } }

  @media (max-width: 980px) {
    body { grid-template-columns: 1fr; }
    .app-topbar { display: flex; }
    .nav-scrim.show { display: block; }
    .app-nav {
      position: fixed; top: 0; bottom: 0; left: 0; width: 264px; z-index: 50;
      transform: translateX(-100%); transition: transform 0.2s ease;
    }
    .app-nav.open { transform: translateX(0); }
  }
</style>
</head>
<body>

<header class="app-topbar">
  <button class="icon-btn" id="navToggle" aria-label="Open navigation">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
  </button>
  <div class="app-topbar-brand"><span>EmbyToolkit</span></div>
</header>

<div class="nav-scrim" id="navScrim"></div>

<nav class="app-nav" id="appNav">
  <div class="app-nav-brand">
    <div class="app-nav-logo">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.29 7 12 12 20.71 7"/><line x1="12" y1="22" x2="12" y2="12"/></svg>
    </div>
    <span class="app-nav-name">EmbyToolkit</span>
  </div>
  <ul class="app-nav-items">
    <li><a class="app-nav-item" href="/">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>
      <span>Generator</span>
    </a></li>
    <li><a class="app-nav-item" href="/collections">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
      <span>Collections</span>
    </a></li>
    <li><a class="app-nav-item" href="/airing">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="15" rx="2"/><polyline points="17 2 12 7 7 2"/></svg>
      <span>Airing</span>
    </a></li>
    <li><a class="app-nav-item" href="/bulk-assign">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg>
      <span>Bulk Assign</span>
    </a></li>
    <li><a class="app-nav-item active" href="/favorites">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17.27 18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
      <span>User Favourites</span>
    </a></li>
  </ul>
  <div class="app-nav-foot">
    <div class="app-nav-status">
      <span class="dot" id="statusDot"></span>
      <span id="statusText">Checking…</span>
    </div>
    <div class="app-nav-version">EmbyToolkit · v1.0</div>
  </div>
</nav>

<main class="page">
 <div class="page-inner">

  <section class="hero">
    <div class="hero-copy">
      <h2>User Favourites</h2>
      <p>Browse any Emby collection and view it as a given user. For a user's own <strong>"{Name} Favorites"</strong> collection you can also remove items they have already watched and top it back up with recommendations built only from that user's watch history. Destructive and bulk actions preview first; nothing changes until you apply.</p>
    </div>
  </section>

  <section class="section">
    <p class="section-label">Collection</p>
    <div class="user-picker" id="collectionPicker">
      <div style="color:var(--text-3);font-size:13px;padding:6px 2px">Loading collections…</div>
    </div>
  </section>

  <section class="section">
    <p class="section-label">Watched status for</p>
    <div class="user-picker" id="userPicker"></div>
  </section>

  <div id="workspace" style="display:none">

    <section class="section stat-row" id="statRow">
      <div class="stat-card">
        <div class="stat-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg></div>
        <div class="stat-meta"><span class="stat-value" id="statCurrent">—</span><span class="stat-label">Current collection count</span></div>
      </div>
      <div class="stat-card">
        <div class="stat-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg></div>
        <div class="stat-meta"><span class="stat-value" id="statWatched">—</span><span class="stat-label">Watched items found</span></div>
      </div>
      <div class="stat-card">
        <div class="stat-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17.27 18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg></div>
        <div class="stat-meta"><span class="stat-value" id="statRecommended">—</span><span class="stat-label">Items recommended</span></div>
      </div>
      <div class="stat-card">
        <div class="stat-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></div>
        <div class="stat-meta"><span class="stat-value" id="statFinal">—</span><span class="stat-label">Final count after apply</span></div>
      </div>
    </section>

    <section class="section">
      <div class="actions-bar">
        <div class="actions-group">
          <button class="btn" id="btnPreviewCleanup">Preview cleanup</button>
          <button class="btn btn-danger" id="btnApplyCleanup" disabled>Apply cleanup</button>
        </div>
        <div class="actions-sep"></div>
        <div class="actions-group">
          <label class="target-field">Target size
            <input type="number" id="targetSize" min="1" max="200" value="25">
          </label>
          <button class="btn" id="btnPreviewRegen">Preview regenerate</button>
          <button class="btn btn-green" id="btnApplyRegen" disabled>Apply regenerate</button>
        </div>
        <div class="actions-spacer"></div>
        <button class="btn" id="btnRefresh">Refresh</button>
      </div>
      <div class="actions-note" id="actionsNote" style="display:none"></div>
      <div id="actionResult"></div>
    </section>

    <section class="section">
      <div class="panel">
        <div class="panel-head">
          <h3 id="playlistTitle">Collection</h3>
          <span class="muted" id="playlistMeta"></span>
        </div>
        <div class="table-scroll">
          <table class="data-table">
            <thead>
              <tr>
                <th>Title</th>
                <th>Type</th>
                <th>Year</th>
                <th>Runtime</th>
                <th>Watched</th>
                <th>Emby item id</th>
              </tr>
            </thead>
            <tbody id="playlistBody"></tbody>
          </table>
        </div>
        <div id="playlistEmpty" class="preview-empty" style="display:none">This collection has no items.</div>
      </div>
    </section>

  </div>

  <div id="noUsers" class="empty" style="display:none">
    No collections found. Create a collection named "<strong>{User} Favorites</strong>" in Emby (for example "Matt Favorites") and refresh.
  </div>

 </div>
</main>

<div class="toast" id="toast"></div>

<script>
const $ = s => document.querySelector(s);
const state = { collections: [], users: [], selectedCollectionId: null, selectedUserId: null, view: null };

/* ── nav (mobile) ── */
const appNav = $('#appNav'), navScrim = $('#navScrim'), navToggle = $('#navToggle');
function closeNav() { appNav.classList.remove('open'); navScrim.classList.remove('show'); }
if (navToggle) navToggle.addEventListener('click', () => { appNav.classList.add('open'); navScrim.classList.add('show'); });
if (navScrim) navScrim.addEventListener('click', closeNav);

/* ── helpers ── */
function toast(msg, ok = true) {
  const t = $('#toast');
  t.textContent = msg;
  t.style.background = ok ? 'rgba(70,217,154,0.12)' : 'rgba(240,114,111,0.12)';
  t.style.border = '1px solid ' + (ok ? 'rgba(70,217,154,0.4)' : 'rgba(240,114,111,0.4)');
  t.style.color = ok ? '#b7f0d4' : '#ffb4b2';
  t.classList.add('show');
  clearTimeout(toast._t);
  toast._t = setTimeout(() => t.classList.remove('show'), 3200);
}
function initials(name) { return (name || '?').trim().slice(0, 2).toUpperCase(); }
function escapeHtml(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;' }[c])); }
function fmtRuntime(min) { if (!min) return '—'; const h = Math.floor(min / 60), m = min % 60; return h ? `${h}h ${m}m` : `${m}m`; }

async function getJSON(url) {
  const r = await fetch(url);
  if (!r.ok) { const d = await r.json().catch(() => ({})); throw new Error(d.detail || `Request failed (${r.status})`); }
  return r.json();
}
async function postJSON(url, body) {
  const r = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
  if (!r.ok) { const d = await r.json().catch(() => ({})); throw new Error(d.detail || `Request failed (${r.status})`); }
  return r.json();
}

/* ── data ── */
const ICON_FAV = '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27 18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>';
const ICON_COL = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>';

async function loadOverview() {
  try {
    const data = await getJSON('/api/favorites/collections');
    state.collections = data.collections || [];
    state.users = data.users || [];
    if (!state.collections.length) {
      $('#collectionPicker').innerHTML = '<div style="color:var(--text-3);font-size:13px;padding:6px 2px">No collections found in Emby.</div>';
      $('#noUsers').style.display = 'block';
      return;
    }
    $('#noUsers').style.display = 'none';
    const def = state.collections.find(c => c.is_favorites) || state.collections[0];
    renderCollections();
    renderUsers();
    selectCollection(def.collection_id);
  } catch (e) {
    $('#collectionPicker').innerHTML = `<div style="color:#ffb4b2;font-size:13px">${escapeHtml(e.message)}</div>`;
  }
}

function currentCollection() { return state.collections.find(c => c.collection_id === state.selectedCollectionId); }
function actionsReady() { return !!(state.selectedUserId && state.view && state.view.actions_enabled); }

function renderCollections() {
  const picker = $('#collectionPicker');
  picker.innerHTML = state.collections.map(c => `
    <button class="user-chip${c.collection_id === state.selectedCollectionId ? ' active' : ''}" data-id="${escapeHtml(c.collection_id)}">
      <span class="pl-ico">${c.is_favorites ? ICON_FAV : ICON_COL}</span>
      <span>${escapeHtml(c.collection_name)}</span>
      ${c.is_favorites ? '<span class="chip-badge">Favourites</span>' : ''}
      ${c.item_count != null ? `<span class="count">${c.item_count}</span>` : ''}
    </button>`).join('');
  picker.querySelectorAll('.user-chip').forEach(btn =>
    btn.addEventListener('click', () => selectCollection(btn.dataset.id)));
}

function renderUsers() {
  const picker = $('#userPicker');
  picker.innerHTML = state.users.map(u => `
    <button class="user-chip${u.id === state.selectedUserId ? ' active' : ''}" data-id="${escapeHtml(u.id)}">
      <span class="avatar">${escapeHtml(initials(u.name))}</span>
      <span>${escapeHtml(u.name)}</span>
    </button>`).join('');
  picker.querySelectorAll('.user-chip').forEach(btn =>
    btn.addEventListener('click', () => selectUser(btn.dataset.id)));
}

function selectCollection(id) {
  state.selectedCollectionId = id;
  const col = currentCollection();
  if (col && col.owner_user_id) state.selectedUserId = col.owner_user_id;
  else if (!state.selectedUserId && state.users.length) state.selectedUserId = state.users[0].id;
  renderCollections();
  renderUsers();
  $('#actionResult').innerHTML = '';
  loadView();
}

function selectUser(id) {
  state.selectedUserId = id;
  renderUsers();
  $('#actionResult').innerHTML = '';
  loadView();
}

async function loadView() {
  if (!state.selectedCollectionId || !state.selectedUserId) return;
  $('#workspace').style.display = 'block';
  $('#playlistBody').innerHTML = `<tr><td colspan="6"><div class="spinner"></div></td></tr>`;
  try {
    const view = await getJSON(`/api/favorites/collection/${state.selectedCollectionId}?user_id=${encodeURIComponent(state.selectedUserId)}`);
    state.view = view;
    renderItems();
    renderSummary({ current: view.summary.current_count, watched: view.summary.watched_count, recommended: '—', final: '—' });
    gateActions(view);
  } catch (e) {
    $('#playlistBody').innerHTML = `<tr><td colspan="6" style="color:#ffb4b2">${escapeHtml(e.message)}</td></tr>`;
    toast(e.message, false);
  }
}

function gateActions(view) {
  const on = !!view.actions_enabled;
  $('#btnPreviewCleanup').disabled = !on;
  $('#btnPreviewRegen').disabled = !on;
  if (!on) { $('#btnApplyCleanup').disabled = true; $('#btnApplyRegen').disabled = true; }
  const note = $('#actionsNote');
  if (on) { note.style.display = 'none'; note.textContent = ''; }
  else { note.style.display = 'block'; note.textContent = view.actions_reason || 'Cleanup and regenerate are unavailable for this collection.'; }
}

function renderItems() {
  const v = state.view;
  $('#playlistTitle').textContent = v.collection_name;
  $('#playlistMeta').textContent = `${v.summary.current_count} items · ${v.summary.watched_count} watched · as ${v.user_name}`;
  const body = $('#playlistBody');
  const empty = $('#playlistEmpty');
  if (!v.items.length) {
    body.innerHTML = '';
    empty.style.display = 'block';
    return;
  }
  empty.style.display = 'none';
  body.innerHTML = v.items.map(it => `
    <tr>
      <td class="cell-strong">${escapeHtml(it.title)}${it.series_name ? `<div class="cell-sub">${escapeHtml(it.series_name)}</div>` : ''}</td>
      <td>${escapeHtml(it.type || it.media_type || '—')}</td>
      <td class="cell-num">${it.year || '—'}</td>
      <td class="cell-num">${fmtRuntime(it.runtime_minutes)}</td>
      <td>${it.watched
        ? '<span class="badge badge-watched">Watched</span>'
        : '<span class="badge">Unwatched</span>'}</td>
      <td><span class="item-id">${escapeHtml(it.id)}</span></td>
    </tr>`).join('');
}

async function refreshCounts() {
  try {
    const data = await getJSON('/api/favorites/collections');
    state.collections = data.collections || [];
    renderCollections();
  } catch { /* keep stale counts on failure */ }
}

function renderSummary({ current, watched, recommended, final }) {
  $('#statCurrent').textContent = current;
  $('#statWatched').textContent = watched;
  $('#statRecommended').textContent = recommended;
  $('#statFinal').textContent = final;
}

/* ── action result panel ── */
function renderActionResult(kind, payload) {
  const dry = payload.dry_run;
  const isCleanup = kind === 'cleanup';
  const list = isCleanup ? (payload.removed || []) : (payload.recommended || []);
  const verb = isCleanup ? (dry ? 'would be removed' : 'removed') : (dry ? 'would be added' : 'added');
  const heading = isCleanup ? 'Watched-item cleanup' : 'Recommended replacements';
  const rows = list.length ? `<ul class="preview-list">${list.map(i => `
      <li>
        <span class="pl-title">${escapeHtml(i.title)}</span>
        ${i.score != null ? `<span class="pl-score">score ${i.score}</span>` : ''}
        <span class="pl-id">${escapeHtml(i.item_id)}</span>
      </li>`).join('')}</ul>`
    : `<div class="preview-empty">${payload.message ? escapeHtml(payload.message) : `Nothing ${verb}.`}</div>`;

  $('#actionResult').innerHTML = `
    <div class="preview-card">
      <div class="preview-head">
        <h4>${heading}</h4>
        <span class="preview-flag ${dry ? 'dry' : 'applied'}">${dry ? 'Dry run · preview' : 'Applied'}</span>
        <span style="margin-left:auto;font-size:12px;color:var(--text-3)">${list.length} ${list.length === 1 ? 'item' : 'items'} ${verb}</span>
      </div>
      <div class="preview-body">${rows}</div>
    </div>`;
}

/* ── cleanup ── */
async function previewCleanup() {
  if (!actionsReady()) return;
  try {
    const res = await postJSON(`/api/favorites/collection/${state.selectedCollectionId}/cleanup`, { userId: state.selectedUserId, dryRun: true });
    renderActionResult('cleanup', res);
    renderSummary({
      current: res.summary.current_count, watched: res.summary.watched_count,
      recommended: $('#statRecommended').textContent,
      final: res.summary.current_count - res.summary.watched_count,
    });
    $('#btnApplyCleanup').disabled = res.summary.watched_count === 0;
    toast(`${res.watched_found} watched item${res.watched_found === 1 ? '' : 's'} found`);
  } catch (e) { toast(e.message, false); }
}
async function applyCleanup() {
  if (!actionsReady()) return;
  const who = (state.view && state.view.user_name) || 'this user';
  const where = (state.view && state.view.collection_name) || 'this collection';
  if (!confirm(`Remove items ${who} has watched from "${where}"? Collections are shared, so removed items leave the collection for everyone. This cannot be undone.`)) return;
  try {
    const res = await postJSON(`/api/favorites/collection/${state.selectedCollectionId}/cleanup`, { userId: state.selectedUserId, dryRun: false });
    renderActionResult('cleanup', res);
    toast(`Removed ${res.summary.removed_count} watched item${res.summary.removed_count === 1 ? '' : 's'}`);
    $('#btnApplyCleanup').disabled = true;
    await refreshCounts();
    await loadView();
  } catch (e) { toast(e.message, false); }
}

/* ── regenerate ── */
function targetSize() { return Math.max(1, parseInt($('#targetSize').value, 10) || 25); }
async function previewRegen() {
  if (!actionsReady()) return;
  try {
    const res = await postJSON(`/api/favorites/collection/${state.selectedCollectionId}/regenerate`, { userId: state.selectedUserId, dryRun: true, targetSize: targetSize() });
    renderActionResult('regenerate', res);
    renderSummary({
      current: res.summary.current_count, watched: $('#statWatched').textContent,
      recommended: res.summary.recommended_count, final: res.summary.final_count,
    });
    $('#btnApplyRegen').disabled = res.summary.recommended_count === 0;
    toast(res.message ? res.message : `${res.summary.recommended_count} recommendation${res.summary.recommended_count === 1 ? '' : 's'} ready`, !res.message || res.summary.recommended_count > 0);
  } catch (e) { toast(e.message, false); }
}
async function applyRegen() {
  if (!actionsReady()) return;
  const where = (state.view && state.view.collection_name) || 'this collection';
  if (!confirm(`Add recommendations to "${where}" (target ${targetSize()} items)? Collections are shared with all users.`)) return;
  try {
    const res = await postJSON(`/api/favorites/collection/${state.selectedCollectionId}/regenerate`, { userId: state.selectedUserId, dryRun: false, targetSize: targetSize() });
    renderActionResult('regenerate', res);
    toast(`Added ${res.summary.added_count} item${res.summary.added_count === 1 ? '' : 's'}`);
    $('#btnApplyRegen').disabled = true;
    await refreshCounts();
    await loadView();
  } catch (e) { toast(e.message, false); }
}

$('#btnPreviewCleanup').addEventListener('click', previewCleanup);
$('#btnApplyCleanup').addEventListener('click', applyCleanup);
$('#btnPreviewRegen').addEventListener('click', previewRegen);
$('#btnApplyRegen').addEventListener('click', applyRegen);
$('#btnRefresh').addEventListener('click', loadView);

/* ── status ── */
(async () => {
  try {
    const config = await (await fetch('/api/config')).json();
    if (config.connected) { $('#statusText').textContent = 'Connected to Emby'; }
    else { $('#statusDot').classList.add('off'); $('#statusText').textContent = 'No API key'; }
  } catch {
    $('#statusDot').classList.add('off'); $('#statusText').textContent = 'Connection error';
  }
})();

loadOverview();
</script>
</body>
</html>
