// Shared utilities and small visual components
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// --- format helpers ---
function timeAgo(ts, now) {
  const diff = now - ts;
  const m = Math.floor(diff / 60000);
  const h = Math.floor(diff / 3600000);
  const d = Math.floor(diff / 86400000);
  if (m < 1) return 'just now';
  if (m < 60) return m + 'm ago';
  if (h < 24) return h + 'h ago';
  if (d < 7) return d + 'd ago';
  if (d < 60) return Math.floor(d/7) + 'w ago';
  return Math.floor(d/30) + 'mo ago';
}

function pad2(n) { return n < 10 ? '0' + n : '' + n; }

// "42:11" → 2531  ; "1:02:33" → 3753
function parseDur(s) {
  const parts = String(s || '0:00').split(':').map(n => parseInt(n, 10) || 0);
  let sec = 0; for (const p of parts) sec = sec * 60 + p;
  return sec;
}
// 2531 → "42:11"
function formatDur(sec) {
  sec = Math.max(0, Math.floor(sec));
  const h = Math.floor(sec / 3600);
  const m = Math.floor((sec % 3600) / 60);
  const s = sec % 60;
  if (h > 0) return h + ':' + pad2(m) + ':' + pad2(s);
  return pad2(m) + ':' + pad2(s);
}

// Local-date key for the attention-budget watch log, e.g. "2026-06-11"
function dayKey(d = new Date()) {
  return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate());
}

// Minimal CSV parser (handles quoted fields with commas/escaped quotes).
// Returns an array of rows, each an array of cell strings.
function parseCsv(text) {
  const rows = [];
  let row = [], cell = '', inQ = false;
  for (let i = 0; i < text.length; i++) {
    const ch = text[i];
    if (inQ) {
      if (ch === '"') {
        if (text[i + 1] === '"') { cell += '"'; i++; }
        else inQ = false;
      } else cell += ch;
    } else if (ch === '"') inQ = true;
    else if (ch === ',') { row.push(cell); cell = ''; }
    else if (ch === '\n' || ch === '\r') {
      if (ch === '\r' && text[i + 1] === '\n') i++;
      row.push(cell); cell = '';
      if (row.length > 1 || row[0] !== '') rows.push(row);
      row = [];
    } else cell += ch;
  }
  row.push(cell);
  if (row.length > 1 || row[0] !== '') rows.push(row);
  return rows;
}

// Parse a Google Takeout "subscriptions.csv" into [{ id, url, title }].
// Takeout columns: Channel Id, Channel Url, Channel Title (header row first).
function parseTakeoutSubscriptions(text) {
  const rows = parseCsv(String(text || ''));
  if (!rows.length) return [];
  const head = rows[0].map(h => h.trim().toLowerCase());
  let idIdx = head.findIndex(h => h.includes('channel id'));
  let urlIdx = head.findIndex(h => h.includes('channel url'));
  let titleIdx = head.findIndex(h => h.includes('channel title'));
  let start = 1;
  if (idIdx === -1) { idIdx = 0; urlIdx = 1; titleIdx = 2; start = /^UC[\w-]{20,}/.test(rows[0][0] || '') ? 0 : 1; }
  const out = [];
  for (let i = start; i < rows.length; i++) {
    const r = rows[i];
    const id = (r[idIdx] || '').trim();
    if (!/^UC[\w-]{20,}$/.test(id)) continue;
    out.push({
      id,
      url: (r[urlIdx] || '').trim() || `https://www.youtube.com/channel/${id}`,
      title: (r[titleIdx] || '').trim() || id,
    });
  }
  return out;
}

window.SoloUtils = { timeAgo, pad2, parseDur, formatDur, dayKey, parseCsv, parseTakeoutSubscriptions };

// --- thumbnail ---
// Real YouTube videos carry `thumbUrl` — render the real image.
// Seed channels with no thumbUrl fall back to the procedural SVG below.
function Thumbnail({ video, channel, large=false, generated=false }) {
  const imgSrc = !generated && (video.thumbUrl || (video.youtubeId ? `https://img.youtube.com/vi/${video.youtubeId}/mqdefault.jpg` : null));
  if (imgSrc) {
    return (
      <div className="v-thumb-img v-thumb-real" style={{ background: channel?.accent || '#000' }}>
        <img src={imgSrc} alt="" loading="lazy" />
      </div>
    );
  }
  const seed = (video.thumbSeed * 9301 + 49297) % 233280;
  const r = (n) => (seed * (n+1) % 1000) / 1000;
  const accent = channel.accent;
  const titleSnip = (video.title || '').slice(0, 32);

  // pattern variants
  const variant = video.thumbSeed % 5;

  return (
    <svg className="v-thumb-img" viewBox="0 0 320 180" preserveAspectRatio="xMidYMid slice">
      <defs>
        <clipPath id={'clip-'+video.id}><rect width="320" height="180"/></clipPath>
      </defs>
      <g clipPath={`url(#clip-${video.id})`}>
        <rect width="320" height="180" fill={accent}/>
        {variant === 0 && (
          <>
            <rect x="0" y={40 + r(1)*40} width="320" height="2" fill="rgba(255,255,255,0.45)"/>
            <rect x="0" y={110 + r(2)*20} width="320" height="1" fill="rgba(255,255,255,0.25)"/>
            <circle cx={60+r(3)*200} cy={60+r(4)*60} r={28+r(5)*30} fill="rgba(255,255,255,0.08)"/>
          </>
        )}
        {variant === 1 && (
          <>
            {Array.from({length: 14}).map((_, i) => (
              <rect key={i} x={i*24} y={0} width="1" height="180" fill="rgba(255,255,255,0.15)"/>
            ))}
            <rect x={40 + r(1)*100} y="40" width={80+r(2)*120} height="80+r(3)*40" fill="rgba(0,0,0,0.3)"/>
          </>
        )}
        {variant === 2 && (
          <>
            <polygon points={`0,${60+r(1)*30} 320,${20+r(2)*40} 320,180 0,180`} fill="rgba(0,0,0,0.35)"/>
            <polygon points={`0,${110+r(3)*30} 320,${90+r(4)*30} 320,180 0,180`} fill="rgba(255,255,255,0.08)"/>
          </>
        )}
        {variant === 3 && (
          <>
            <rect x="0" y="0" width="160" height="180" fill="rgba(0,0,0,0.25)"/>
            <rect x={70+r(1)*40} y={40+r(2)*30} width={120+r(3)*30} height="2" fill="rgba(255,255,255,0.7)"/>
            <rect x={70+r(1)*40} y={50+r(2)*30} width="60" height="2" fill="rgba(255,255,255,0.5)"/>
            <rect x={70+r(1)*40} y={60+r(2)*30} width="80" height="2" fill="rgba(255,255,255,0.35)"/>
          </>
        )}
        {variant === 4 && (
          <>
            <circle cx="160" cy="90" r={50+r(1)*30} fill="none" stroke="rgba(255,255,255,0.35)" strokeWidth="1"/>
            <circle cx="160" cy="90" r={20+r(2)*20} fill="none" stroke="rgba(255,255,255,0.55)" strokeWidth="1"/>
            <line x1="0" y1="90" x2="320" y2="90" stroke="rgba(255,255,255,0.2)" strokeWidth="1"/>
            <line x1="160" y1="0" x2="160" y2="180" stroke="rgba(255,255,255,0.2)" strokeWidth="1"/>
          </>
        )}
        <text x="14" y="166" fontFamily="JetBrains Mono, monospace" fontSize="10" fill="rgba(255,255,255,0.6)" letterSpacing="1">
          {channel.handle.toUpperCase()}
        </text>
        {large && (
          <text x="14" y="40" fontFamily="Space Grotesk, sans-serif" fontSize="16" fontWeight="500" fill="white" letterSpacing="-0.3">
            {titleSnip}
          </text>
        )}
      </g>
    </svg>
  );
}

window.Thumbnail = Thumbnail;
