// add-modal.jsx — Add / Import with sourceUrl metadata fetch
// Flow: paste URL → Fetch → Review (editable) → Save

const { useState } = React;

function AddModal({ open, onClose, uid, items, casts, categories, studios, accent, onSaved, onOpenVideo, onOpenCategory, onOpenStudio }) {
  const [step, setStep] = useState('url');     // url | review | duplicate
  const [sourceUrl, setSourceUrl] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [form, setForm] = useState(null);
  const [fetchStatus, setFetchStatus] = useState('manual');
  const [duplicates, setDuplicates] = useState([]);   // {kind, item, reason}

  React.useEffect(() => {
    if (!open) {
      setStep('url'); setSourceUrl(''); setErr(''); setForm(null);
      setBusy(false); setDuplicates([]);
    }
  }, [open]);

  if (!open) return null;

  const blankForm = (url = '') => ({
    code: '', title: '', actresses: [], studio: '', director: '',
    releaseDate: '', coverUrl: '', sourceUrl: url, sourceName: '', sourceDomain: '',
    missavUrl: '',
    categories: [], series: '', durationMinutes: 0,
    contentId: '', castAliases: {}, studioSlug: '', studioSourceUrl: '', categorySlugs: [],
    aliases: [], jpTitle: '', zhTitle: '', trailerUrl: '', trailerType: '', trailerPoster: '',
    gallery: [], importProvider: '', importVersion: '', importConfidence: 'low',
    importWarnings: [], parsedAt: null,
    tags: [], rating: 0, status: 'want_to_watch', note: '',
    bookmarkLevel: 'normal', priority: 'normal', sourceQuality: 'unknown',
    reminderAt: null, watchedAt: null,
  });

  const fetchMeta = async () => {
    setBusy(true); setErr('');
    try {
      const meta = await window.VHMetadata.fetchMetadata(sourceUrl, { uid });
      setForm({ ...blankForm(sourceUrl), ...meta, sourceUrl, missavUrl: buildMissavUrl(meta.code || '') });
      setFetchStatus(meta.fetchStatus || 'success');
      setStep('review');
    } catch (e) {
      const msg = /[฀-๿]/.test(e.message)
        ? e.message
        : window.VHMetadata.getImportErrorMessage(e);
      setErr(msg);
      setFetchStatus('failed');
    } finally { setBusy(false); }
  };

  const skipToManual = () => {
    setForm(blankForm(sourceUrl));
    setFetchStatus('manual');
    setStep('review');
    setErr('');
  };

  // Run duplicate-detection across the user's items.
  const detectDuplicates = () => {
    const found = [];
    const codeNorm = window.VHCast.normalizeCode(form.code);
    const url = (form.sourceUrl || '').trim();
    const formCasts = new Set((form.actresses || []).map(window.VHCast.normalizeCastName));
    for (const v of (items || [])) {
      if (codeNorm && window.VHCast.normalizeCode(v.code || '') === codeNorm) {
        found.push({ kind: 'code', item: v, reason: `Same code "${v.code}"` });
        continue;
      }
      if (url && v.sourceUrl === url) {
        found.push({ kind: 'sourceUrl', item: v, reason: 'Same source URL' });
        continue;
      }
      // fuzzy: similar title + overlapping cast
      const sim = window.VHCast.titleSimilarity(form.title, v.title);
      if (sim >= 0.55) {
        const vc = new Set((v.actresses || []).map(window.VHCast.normalizeCastName));
        const overlap = [...formCasts].some(x => vc.has(x));
        if (overlap) {
          found.push({ kind: 'fuzzy', item: v, reason: `Similar title + same cast (~${Math.round(sim*100)}%)` });
        }
      }
    }
    return found;
  };

  const save = async (force = false) => {
    if (!form.code.trim()) { setErr('กรุณากรอกรหัสไอเทม (Code)'); return; }
    setBusy(true); setErr('');
    try {
      if (!force) {
        const dups = detectDuplicates();
        if (dups.length) {
          setDuplicates(dups);
          setStep('duplicate');
          setBusy(false);
          return;
        }
      }
      await window.VHFirebase.addVideo(uid, {
        ...form,
        code: form.code.trim().toUpperCase(),
        title: form.title.trim(),
        missavUrl: form.missavUrl || buildMissavUrl(form.code),
        actresses: window.VHCast.dedupeCastNames(form.actresses),
        cast: window.VHCast.dedupeCastNames(form.actresses),
        fetchStatus,
        fetchError: fetchStatus === 'failed' ? err : null,
        fetchedAt: fetchStatus === 'success' ? Date.now() : null,
      });
      onSaved?.();
      onClose();
    } catch (e) {
      const msg = String(e?.message || '');
      setErr(/permission|denied|missing or insufficient permissions/i.test(msg)
        ? 'บันทึกไม่สำเร็จ: Firestore rules ยังไม่อนุญาต collection ที่ใช้บันทึก'
        : 'บันทึกไม่สำเร็จ กรุณาตรวจสอบการเชื่อมต่อแล้วลองใหม่');
    } finally { setBusy(false); }
  };

  return (
    <ModalShell onClose={onClose}>
      <div style={{ padding: '20px 20px 28px' }}>
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14,
        }}>
          <div>
            <div style={{
              fontSize: 11, letterSpacing: 1.2, textTransform: 'uppercase',
              color: window.VH_DIM2, fontWeight: 700,
            }}>{step === 'url' ? 'ขั้นตอนที่ 1 / 2' : step === 'review' ? 'ขั้นตอนที่ 2 / 2' : 'พบรายการที่คล้ายกัน'}</div>
            <div style={{ fontSize: 19, fontWeight: 700, color: window.VH_TEXT, letterSpacing: -0.3 }}>
              {step === 'url' ? 'เพิ่มจาก Source URL' : step === 'review' ? 'ตรวจสอบและบันทึก' : 'มีรายการนี้ในไลบรารีแล้ว?'}
            </div>
          </div>
          <button onClick={onClose} style={{
            width: 32, height: 32, borderRadius: 99, border: 'none', cursor: 'pointer',
            background: 'rgba(255,255,255,0.07)', color: window.VH_TEXT,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <svg width="11" height="11" viewBox="0 0 11 11"><path d="M1 1l9 9M10 1L1 10" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>
          </button>
        </div>

        {step === 'url' && (
          <>
            <div style={{
              fontSize: 13, color: window.VH_DIM, lineHeight: 1.5, marginBottom: 14,
            }}>
              วาง URL ของรายการที่ต้องการ ระบบจะดึงข้อมูล (รหัส, ชื่อ, นักแสดง, หน้าปก) ผ่านเซิร์ฟเวอร์ คุณสามารถแก้ไขก่อนบันทึกได้
            </div>
            <Field label="Source URL" type="url" value={sourceUrl}
              onChange={v => { setSourceUrl(v); if (err) setErr(''); }}
              placeholder="https://missav.ws/en/abc-123"/>
            <div style={{
              marginTop: 8, fontSize: 11, color: window.VH_DIM2, lineHeight: 1.5,
            }}>
              โดเมนที่รองรับ: {window.VHMetadata.ALLOWLIST.join(', ')}
            </div>

            {err && <ErrorBox>{err}</ErrorBox>}

            <div style={{ display: 'flex', gap: 8, marginTop: 18 }}>
              <button onClick={skipToManual} className="vh-tap" style={secondaryBtn}>
                กรอกเอง
              </button>
              <button onClick={fetchMeta} disabled={busy || !sourceUrl.trim()}
                style={{
                  ...primaryBtn(accent),
                  opacity: busy || !sourceUrl.trim() ? 0.55 : 1,
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
                }}>
                {busy && <BtnSpinner/>}
                {busy ? 'กำลังดึงข้อมูล…' : 'ดึงข้อมูล'}
              </button>
            </div>
          </>
        )}

        {step === 'review' && form && (
          <>
            <FetchBanner status={fetchStatus} accent={accent}/>
            <ImportBadges form={form} fetchStatus={fetchStatus} accent={accent}/>

            {/* Warnings from parser (e.g. cast not found) */}
            {(form.warnings || []).map((w, i) => (
              <div key={i} style={{
                marginTop: 6, padding: '7px 10px', borderRadius: 8, fontSize: 12,
                background: 'rgba(245,158,11,0.10)', border: '0.5px solid rgba(245,158,11,0.35)',
                color: '#fcd34d', lineHeight: 1.45,
              }}>{w}</div>
            ))}

            {/* Cover preview */}
            <div style={{
              display: 'flex', gap: 12, padding: '14px 0 4px',
            }}>
              <div style={{ width: 76, flexShrink: 0 }}>
                {form.coverUrl ? (
                  <div style={{
                    aspectRatio: '2/3', borderRadius: 10, overflow: 'hidden',
                    background: window.VH_SURFACE_HI,
                    border: `0.5px solid ${window.VH_HAIRLINE}`,
                  }}>
                    <img src={form.coverUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                      onError={(e) => { e.target.style.display = 'none'; }}/>
                  </div>
                ) : (
                  <window.PosterTile code={form.code || 'NEW'} status={form.status} accent={accent}/>
                )}
              </div>
              <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
                <Field compact label="Code"
                  value={form.code} onChange={v => {
                    const nextCode = v.toUpperCase();
                    setForm({ ...form, code: nextCode, missavUrl: buildMissavUrl(nextCode) });
                  }}
                  placeholder="ABC-123"/>
                <Field compact label="Title"
                  value={form.title} onChange={v => setForm({ ...form, title: v })}
                  placeholder="Title"/>
              </div>
            </div>

            <Field label="นักแสดง (คั่นด้วยจุลภาค)"
              value={form.actresses.join(', ')}
              onChange={v => setForm({ ...form, actresses: v.split(',').map(s => s.trim()).filter(Boolean) })}
              placeholder="ชื่อ 1, ชื่อ 2"/>

            <Field label="MissAV" type="url"
              value={form.missavUrl || buildMissavUrl(form.code)}
              onChange={v => setForm({ ...form, missavUrl: v })}
              placeholder="https://missav.live/th/snos-250"/>
            <CastChipStrip names={form.actresses} casts={casts} items={items} accent={accent}/>

            {/* Studio chip + field */}
            <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
              <Field flex label="สตูดิโอ" value={form.studio} onChange={v => setForm({ ...form, studio: v })}/>
              <Field flex label="วันออกฉาย" value={form.releaseDate}
                onChange={v => setForm({ ...form, releaseDate: v })}
                placeholder="YYYY-MM-DD"/>
            </div>
            {form.studio && <StudioChip name={form.studio} studios={studios} items={items} accent={accent}
              onOpen={onOpenStudio ? (name) => { onClose(); onOpenStudio(name); } : null}/>}

            {/* Categories */}
            <Field label="หมวดหมู่ (คั่นด้วยจุลภาค)"
              value={(form.categories || []).join(', ')}
              onChange={v => setForm({ ...form, categories: v.split(',').map(s => s.trim()).filter(Boolean) })}
              placeholder="Creampie, Cosplay, 4K"/>
            <CategoryChipStrip names={form.categories || []} categories={categories} items={items} accent={accent}
              onOpen={onOpenCategory ? (name) => { onClose(); onOpenCategory(name); } : null}/>

            <GalleryPreview gallery={form.gallery || []}/>
            <TrailerPreview form={form} accent={accent}/>

            {/* Director */}
            {(form.director || fetchStatus === 'success') && (
              <Field label="ผู้กำกับ" value={form.director || ''}
                onChange={v => setForm({ ...form, director: v })}
                placeholder="Director name"/>
            )}

            {/* Series */}
            {(form.series || fetchStatus === 'success') && (
              <Field label="ซีรีส์" value={form.series || ''}
                onChange={v => setForm({ ...form, series: v })}
                placeholder="Series name"/>
            )}

            <Field label="URL รูปหน้าปก (ถ้ามี)" value={form.coverUrl}
              onChange={v => setForm({ ...form, coverUrl: v })}
              placeholder="https://… (ถ้าไม่มีจะใช้สีแทน)"/>

            <Field label="แท็ก (คั่นด้วยจุลภาค)"
              value={form.tags.join(', ')}
              onChange={v => setForm({ ...form, tags: v.split(',').map(s => s.trim()).filter(Boolean) })}
              placeholder="drama, urban"/>

            <div style={{ display: 'flex', gap: 8, marginTop: 10, alignItems: 'flex-end' }}>
              <SelectField label="Status" value={form.status}
                onChange={v => setForm({ ...form, status: v })}
                options={[
                  { v: 'want_to_watch', l: 'Want' },
                  { v: 'watched',       l: 'Watched' },
                  { v: 'favorite',      l: 'Favorite' },
                  { v: 'rewatch',       l: 'Rewatch' },
                  { v: 'skip',          l: 'Skip' },
                ]}/>
              <RatingField value={form.rating} onChange={v => setForm({ ...form, rating: v })} accent={accent}/>
            </div>

            <Field label="บันทึกส่วนตัว" value={form.note}
              onChange={v => setForm({ ...form, note: v })}
              placeholder="จดบันทึกส่วนตัว"
              textarea/>

            {err && <ErrorBox>{err}</ErrorBox>}

            <div style={{
              display: 'flex', gap: 8, marginTop: 16, position: 'sticky',
              bottom: 'max(0px, env(safe-area-inset-bottom))', paddingTop: 10,
              paddingBottom: 'max(0px, env(safe-area-inset-bottom))',
              background: 'linear-gradient(180deg, rgba(21,21,26,0), #15151a 35%)',
            }}>
              <button onClick={() => setStep('url')} className="vh-tap" style={secondaryBtn}>ย้อนกลับ</button>
              <button onClick={() => save(false)} disabled={busy} className="vh-tap"
                style={{
                  ...primaryBtn(accent),
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
                }}>
                {busy && <BtnSpinner/>}
                {busy ? 'กำลังบันทึก…' : 'บันทึกลงไลบรารี'}
              </button>
            </div>
          </>
        )}

        {step === 'duplicate' && (
          <DuplicateStep
            duplicates={duplicates}
            accent={accent}
            onCancel={() => { setStep('review'); setDuplicates([]); }}
            onOpenExisting={(id) => { onOpenVideo?.(id); onClose(); }}
            onSaveAnyway={() => save(true)}
            busy={busy}
          />
        )}
      </div>
    </ModalShell>
  );
}

const primaryBtn = (accent) => ({
  flex: 1, height: 46, borderRadius: 10, border: 'none', cursor: 'pointer',
  background: accent, color: '#fff', fontSize: 14, fontWeight: 600,
  boxShadow: `0 8px 22px ${accent}44, 0 1px 0 rgba(255,255,255,0.18) inset`,
  fontFamily: 'inherit',
});
const secondaryBtn = {
  flex: 1, height: 46, borderRadius: 10, cursor: 'pointer',
  background: 'rgba(255,255,255,0.06)', color: '#f5f5f7',
  border: '0.5px solid rgba(255,255,255,0.10)',
  fontSize: 14, fontWeight: 500, fontFamily: 'inherit',
};

function FetchBanner({ status, accent }) {
  const meta = {
    success: { bg: 'rgba(34,197,94,0.10)', bd: 'rgba(34,197,94,0.35)', fg: '#86efac', text: 'ดึงข้อมูลสำเร็จ — ตรวจสอบและแก้ไขก่อนบันทึก' },
    manual:  { bg: 'rgba(255,255,255,0.04)', bd: 'rgba(255,255,255,0.10)', fg: '#f5f5f7', text: 'กรอกข้อมูลด้วยตัวเอง — ใส่ข้อมูลที่ทราบ' },
    failed:  { bg: 'rgba(225,29,72,0.10)', bd: 'rgba(225,29,72,0.35)', fg: '#fca5b1', text: 'ดึงข้อมูลไม่สำเร็จ — กรอกข้อมูลด้วยตัวเองแทนได้' },
  }[status] || {};
  return (
    <div style={{
      padding: '9px 11px', borderRadius: 8, marginBottom: 4,
      background: meta.bg, border: `0.5px solid ${meta.bd}`,
      color: meta.fg, fontSize: 12.5, lineHeight: 1.45,
    }}>{meta.text}</div>
  );
}

function ImportBadges({ form, fetchStatus, accent }) {
  const confidence = (form.importConfidence || (fetchStatus === 'success' ? 'medium' : 'low')).toLowerCase();
  const confMeta = {
    high: { text: 'High', color: '#86efac', bg: 'rgba(34,197,94,0.12)', bd: 'rgba(34,197,94,0.35)' },
    medium: { text: 'Medium', color: '#fcd34d', bg: 'rgba(245,158,11,0.12)', bd: 'rgba(245,158,11,0.35)' },
    low: { text: 'Low', color: '#fca5a5', bg: 'rgba(239,68,68,0.12)', bd: 'rgba(239,68,68,0.35)' },
  }[confidence] || {};
  const badges = [
    { key: 'confidence', label: `Confidence: ${confMeta.text}`, ...confMeta },
    form.trailerUrl ? { key: 'trailer', label: 'Trailer found', color: accent, bg: `${accent}18`, bd: `${accent}55` } : null,
    form.importProvider ? { key: 'provider', label: form.importProvider, color: window.VH_DIM, bg: 'rgba(255,255,255,0.04)', bd: window.VH_HAIRLINE } : null,
  ].filter(Boolean);
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 8 }}>
      {badges.map(b => (
        <span key={b.key} style={{
          display: 'inline-flex', alignItems: 'center', height: 24,
          padding: '0 8px', borderRadius: 99,
          background: b.bg, border: `0.5px solid ${b.bd}`,
          color: b.color, fontSize: 11, fontWeight: 700,
          textTransform: b.key === 'provider' ? 'uppercase' : 'none',
        }}>{b.label}</span>
      ))}
    </div>
  );
}

function GalleryPreview({ gallery }) {
  if (!gallery || gallery.length === 0) return null;
  return (
    <div style={{ marginTop: 12 }}>
      <div style={{
        fontSize: 10.5, letterSpacing: 0.8, textTransform: 'uppercase',
        color: window.VH_DIM2, fontWeight: 600, marginBottom: 6,
      }}>Gallery preview</div>
      <div style={{
        display: 'grid', gridAutoFlow: 'column', gridAutoColumns: 92,
        gap: 8, overflowX: 'auto', scrollbarWidth: 'none',
        minHeight: 62,
      }}>
        {gallery.slice(0, 12).map((url, idx) => (
          <div key={url + idx} style={{
            width: 92, aspectRatio: '16/10', borderRadius: 8, overflow: 'hidden',
            background: window.VH_SURFACE_HI, border: `0.5px solid ${window.VH_HAIRLINE}`,
          }}>
            <img src={url} alt="" loading="lazy" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}/>
          </div>
        ))}
      </div>
    </div>
  );
}

function TrailerPreview({ form, accent }) {
  const [open, setOpen] = React.useState(false);
  const [failed, setFailed] = React.useState(false);
  const videoRef = React.useRef(null);

  React.useEffect(() => {
    if (!open || !form.trailerUrl || !videoRef.current) return;
    let hls = null;
    const video = videoRef.current;
    const attach = async () => {
      try {
        if (form.trailerType === 'hls' && !video.canPlayType('application/vnd.apple.mpegurl')) {
          if (!window.Hls) {
            await new Promise((resolve, reject) => {
              const s = document.createElement('script');
              s.src = 'https://cdn.jsdelivr.net/npm/hls.js@1.5.18/dist/hls.min.js';
              s.onload = resolve; s.onerror = reject;
              document.head.appendChild(s);
            });
          }
          if (!window.Hls?.isSupported()) throw new Error('HLS unsupported');
          hls = new window.Hls({ enableWorker: true });
          hls.loadSource(form.trailerUrl);
          hls.attachMedia(video);
        } else {
          video.src = form.trailerUrl;
        }
      } catch {
        setFailed(true);
      }
    };
    attach();
    return () => { if (hls) hls.destroy(); video.removeAttribute('src'); video.load(); };
  }, [open, form.trailerUrl, form.trailerType]);

  if (!form.trailerUrl) return null;
  return (
    <div style={{ marginTop: 12 }}>
      <button onClick={() => setOpen(v => !v)} className="vh-tap" style={{
        width: '100%', height: 40, borderRadius: 10, cursor: 'pointer',
        background: `${accent}18`, color: accent, border: `0.5px solid ${accent}55`,
        fontSize: 13, fontWeight: 700, fontFamily: 'inherit',
      }}>{open ? 'Hide Trailer' : 'Watch Trailer'}</button>
      {open && !failed && (
        <div style={{ marginTop: 8, borderRadius: 10, overflow: 'hidden', background: '#000', aspectRatio: '16/9' }}>
          <video ref={videoRef} controls muted playsInline preload="none" poster={form.trailerPoster || form.coverUrl || ''}
            onError={() => setFailed(true)}
            style={{ width: '100%', height: '100%', display: 'block' }}/>
        </div>
      )}
      {open && failed && form.sourceUrl && (
        <a href={form.sourceUrl} target="_blank" rel="noreferrer" style={{
          display: 'block', marginTop: 8, padding: 10, borderRadius: 10,
          background: 'rgba(255,255,255,0.04)', border: `0.5px solid ${window.VH_HAIRLINE}`,
          color: window.VH_TEXT, textDecoration: 'none', fontSize: 12.5, textAlign: 'center',
        }}>Open source page</a>
      )}
    </div>
  );
}

function Field({ label, type = 'text', value, onChange, placeholder, textarea, compact, flex }) {
  const inputStyle = {
    width: '100%', padding: '10px 12px', borderRadius: 10,
    background: 'rgba(255,255,255,0.04)', color: window.VH_TEXT,
    border: `0.5px solid ${window.VH_HAIRLINE}`,
    fontSize: 14, fontFamily: 'inherit', outline: 'none',
    boxSizing: 'border-box',
    minHeight: textarea ? 64 : (compact ? 34 : 42),
    resize: 'vertical',
  };
  return (
    <label style={{ display: 'block', flex: flex ? 1 : undefined, marginTop: compact ? 0 : 10 }}>
      <div style={{
        fontSize: 10.5, letterSpacing: 0.8, textTransform: 'uppercase',
        color: window.VH_DIM2, fontWeight: 600, marginBottom: 4,
      }}>{label}</div>
      {textarea
        ? <textarea value={value || ''} onChange={e => onChange(e.target.value)} placeholder={placeholder} style={inputStyle}/>
        : <input type={type} value={value || ''} onChange={e => onChange(e.target.value)} placeholder={placeholder} style={inputStyle}/>
      }
    </label>
  );
}

function SelectField({ label, value, onChange, options }) {
  return (
    <label style={{ display: 'block', flex: 1, marginTop: 10 }}>
      <div style={{
        fontSize: 10.5, letterSpacing: 0.8, textTransform: 'uppercase',
        color: window.VH_DIM2, fontWeight: 600, marginBottom: 4,
      }}>{label}</div>
      <select value={value} onChange={e => onChange(e.target.value)} style={{
        width: '100%', height: 42, padding: '0 30px 0 12px', borderRadius: 10,
        background: 'rgba(255,255,255,0.04)', color: window.VH_TEXT,
        border: `0.5px solid ${window.VH_HAIRLINE}`,
        fontSize: 14, fontFamily: 'inherit', outline: 'none', appearance: 'none',
        backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='rgba(245,245,247,.55)' d='M0 0h10L5 6z'/></svg>")`,
        backgroundRepeat: 'no-repeat', backgroundPosition: 'right 12px center',
      }}>
        {options.map(o => <option key={o.v} value={o.v} style={{ background: '#15151a' }}>{o.l}</option>)}
      </select>
    </label>
  );
}

function RatingField({ value, onChange, accent }) {
  return (
    <div style={{ marginTop: 10 }}>
      <div style={{
        fontSize: 10.5, letterSpacing: 0.8, textTransform: 'uppercase',
        color: window.VH_DIM2, fontWeight: 600, marginBottom: 4,
      }}>Rating</div>
      <div style={{
        height: 42, padding: '0 10px', borderRadius: 10,
        background: 'rgba(255,255,255,0.04)',
        border: `0.5px solid ${window.VH_HAIRLINE}`,
        display: 'flex', alignItems: 'center', gap: 6,
      }}>
        {[1,2,3,4,5].map(i => (
          <button key={i} onClick={() => onChange(i === value ? 0 : i)} style={{
            border: 'none', background: 'transparent', padding: 4, cursor: 'pointer',
          }}>
            <div style={{
              width: 12, height: 12, borderRadius: 99,
              background: i <= value ? accent : 'rgba(255,255,255,0.18)',
            }}/>
          </button>
        ))}
      </div>
    </div>
  );
}

function ErrorBox({ children }) {
  return (
    <div style={{
      marginTop: 12, padding: '10px 12px', borderRadius: 9,
      background: 'rgba(225,29,72,0.10)', border: '0.5px solid rgba(225,29,72,0.35)',
      display: 'flex', alignItems: 'flex-start', gap: 8,
    }}>
      <svg width="14" height="14" viewBox="0 0 14 14" fill="none" style={{ flexShrink: 0, marginTop: 1 }}>
        <circle cx="7" cy="7" r="6.5" stroke="#f87171" strokeWidth="1.3"/>
        <path d="M7 4v3.5" stroke="#f87171" strokeWidth="1.4" strokeLinecap="round"/>
        <circle cx="7" cy="10" r="0.7" fill="#f87171"/>
      </svg>
      <span style={{ color: '#fca5b1', fontSize: 12.5, lineHeight: 1.5 }}>{children}</span>
    </div>
  );
}

function BtnSpinner({ color = 'rgba(255,255,255,0.85)' }) {
  return (
    <svg width="14" height="14" viewBox="0 0 16 16" style={{ animation: 'vhSpin 0.7s linear infinite', flexShrink: 0 }}>
      <circle cx="8" cy="8" r="6" fill="none" stroke="rgba(255,255,255,0.25)" strokeWidth="2"/>
      <path d="M8 2a6 6 0 016 6" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round"/>
    </svg>
  );
}

function buildMissavUrl(code) {
  const normalized = String(code || '')
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '')
    .replace(/_/g, '-');
  return normalized ? `https://missav.live/th/${normalized}` : '';
}

function CastChipStrip({ names, casts, items, accent }) {
  if (!names || names.length === 0) return null;
  const known = casts || [];
  const knownByKey = new Map(known.map(c => [c.normalizedName, c]));

  // Derive count fallback from items when casts collection is empty (legacy mode)
  const countFromItems = (key) =>
    (items || []).filter(v => (v.actresses || []).some(a => window.VHCast.normalizeCastName(a) === key)).length;

  return (
    <div style={{
      display: 'flex', flexWrap: 'wrap', gap: 6,
      marginTop: 6,
    }}>
      {names.map(name => {
        const key = window.VHCast.normalizeCastName(name);
        const cast = knownByKey.get(key);
        const count = cast?.videoCount ?? countFromItems(key);
        const isNew = !cast && count === 0;
        const hue = window.hashHue(key || name);
        return (
          <div key={name + key} style={{
            display: 'inline-flex', alignItems: 'center', gap: 7,
            padding: '5px 9px 5px 6px', borderRadius: 99,
            background: isNew ? 'rgba(255,255,255,0.04)' : `${accent}1a`,
            border: `0.5px solid ${isNew ? window.VH_HAIRLINE : accent + '55'}`,
            fontSize: 11.5, color: window.VH_TEXT, fontWeight: 500,
          }}>
            <div style={{
              width: 16, height: 16, borderRadius: 99,
              background: `radial-gradient(120% 90% at 25% 20%, oklch(0.55 0.18 ${hue}), oklch(0.18 0.06 ${(hue+30)%360}))`,
            }}/>
            <span style={{ maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{name}</span>
            <span style={{
              fontSize: 10, fontWeight: 600,
              padding: '1px 6px', borderRadius: 99,
              background: isNew ? 'rgba(34,197,94,0.18)' : `${accent}33`,
              color: isNew ? '#86efac' : accent,
              letterSpacing: 0.3, textTransform: 'uppercase',
            }}>
              {isNew ? 'New' : `${count} saved`}
            </span>
          </div>
        );
      })}
    </div>
  );
}

function CategoryChipStrip({ names, categories, items, accent, onOpen }) {
  if (!names || names.length === 0) return null;
  const known = categories || [];

  const normCat = (n) => window.VHCast.normalizeCollectionName(n);
  const knownByKey = new Map(known.map(c => [c.normalizedName, c]));

  const countFromItems = (norm) =>
    (items || []).filter(v => (v.categories || []).some(c => normCat(c) === norm)).length;

  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 6 }}>
      {names.map(name => {
        const key = normCat(name);
        const cat = knownByKey.get(key);
        const count = cat?.videoCount ?? countFromItems(key);
        const isNew = !cat && count === 0;
        const clickable = onOpen && !isNew;
        return (
          <div key={name + key}
            onClick={clickable ? () => onOpen(name) : undefined}
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 5,
              padding: '4px 8px 4px 7px', borderRadius: 99,
              background: isNew ? 'rgba(255,255,255,0.04)' : 'rgba(168,85,247,0.12)',
              border: `0.5px solid ${isNew ? window.VH_HAIRLINE : 'rgba(168,85,247,0.4)'}`,
              fontSize: 11.5, color: window.VH_TEXT, fontWeight: 500,
              cursor: clickable ? 'pointer' : 'default',
            }}>
            <span style={{
              width: 6, height: 6, borderRadius: 99, flexShrink: 0,
              background: isNew ? 'rgba(255,255,255,0.3)' : '#a855f7',
            }}/>
            <span style={{ maxWidth: 140, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{name}</span>
            <span style={{
              fontSize: 10, fontWeight: 600, padding: '1px 5px', borderRadius: 99,
              background: isNew ? 'rgba(34,197,94,0.18)' : 'rgba(168,85,247,0.25)',
              color: isNew ? '#86efac' : '#c084fc', letterSpacing: 0.3, textTransform: 'uppercase',
            }}>
              {isNew ? 'NEW' : `${count} saved`}
            </span>
          </div>
        );
      })}
    </div>
  );
}

function StudioChip({ name, studios, items, accent, onOpen }) {
  if (!name) return null;
  const norm = window.VHCast.normalizeCollectionName(name);
  const known = (studios || []).find(s => s.normalizedName === norm);
  const count = known?.videoCount ??
    (items || []).filter(v => v.studio && window.VHCast.normalizeCollectionName(v.studio) === norm).length;
  const isNew = !known && count === 0;
  const clickable = onOpen && !isNew;

  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 4 }}>
      <div
        onClick={clickable ? () => onOpen(name) : undefined}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 5,
          padding: '4px 8px 4px 7px', borderRadius: 99,
          background: isNew ? 'rgba(255,255,255,0.04)' : `${accent}12`,
          border: `0.5px solid ${isNew ? window.VH_HAIRLINE : accent + '44'}`,
          fontSize: 11.5, color: window.VH_TEXT, fontWeight: 500,
          cursor: clickable ? 'pointer' : 'default',
        }}>
        <svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ flexShrink: 0 }}>
          <rect x="1" y="4" width="8" height="5" rx="1" stroke={isNew ? 'rgba(255,255,255,0.3)' : accent} strokeWidth="1.2"/>
          <path d="M3 4V3a2 2 0 014 0v1" stroke={isNew ? 'rgba(255,255,255,0.3)' : accent} strokeWidth="1.2"/>
        </svg>
        <span>{name}</span>
        <span style={{
          fontSize: 10, fontWeight: 600, padding: '1px 5px', borderRadius: 99,
          background: isNew ? 'rgba(34,197,94,0.18)' : `${accent}30`,
          color: isNew ? '#86efac' : accent, letterSpacing: 0.3, textTransform: 'uppercase',
        }}>
          {isNew ? 'NEW' : `${count} saved`}
        </span>
      </div>
    </div>
  );
}

function DuplicateStep({ duplicates, accent, onCancel, onOpenExisting, onSaveAnyway, busy }) {
  return (
    <div>
      <div style={{
        padding: '10px 12px', borderRadius: 10,
        background: 'rgba(245,158,11,0.10)', border: '0.5px solid rgba(245,158,11,0.35)',
        color: '#fcd34d', fontSize: 12.5, lineHeight: 1.5,
      }}>
        พบ {duplicates.length} รายการที่อาจซ้ำกัน กรุณาตรวจสอบก่อนบันทึก
      </div>

      <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>
        {duplicates.map(({ kind, item, reason }) => (
          <div key={item.id + kind} style={{
            display: 'flex', gap: 12, padding: '10px',
            borderRadius: 12, background: 'rgba(255,255,255,0.03)',
            border: `0.5px solid ${window.VH_HAIRLINE}`,
          }}>
            <div style={{ width: 44, flexShrink: 0 }}>
              <window.PosterTile code={item.code} status={item.status} accent={accent} ratio="2/3" coverUrl={item.coverUrl}/>
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{
                fontSize: 10.5, fontFamily: 'ui-monospace, "SF Mono", Menlo, monospace',
                color: window.VH_DIM, letterSpacing: 0.6, fontWeight: 600,
              }}>{item.code}</div>
              <div style={{
                fontSize: 13.5, fontWeight: 600, color: window.VH_TEXT, marginTop: 1,
                overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              }}>{item.title || 'Untitled'}</div>
              <div style={{
                fontSize: 11.5, color: accent, marginTop: 4, fontWeight: 500,
              }}>{reason}</div>
              <button onClick={() => onOpenExisting(item.id)} style={{
                marginTop: 6, padding: '5px 10px', borderRadius: 99, border: 'none', cursor: 'pointer',
                background: 'rgba(255,255,255,0.06)', color: window.VH_TEXT,
                fontSize: 11, fontWeight: 600, fontFamily: 'inherit',
              }}>
                Open existing →
              </button>
            </div>
          </div>
        ))}
      </div>

      <div style={{ display: 'flex', gap: 8, marginTop: 18 }}>
        <button onClick={onCancel} className="vh-tap" style={secondaryBtn}>แก้ไขต่อ</button>
        <button onClick={onSaveAnyway} disabled={busy} className="vh-tap" style={{
          ...primaryBtn(accent),
          background: 'rgba(225,29,72,0.18)',
          color: '#fca5b1',
          border: '0.5px solid rgba(225,29,72,0.40)',
          boxShadow: 'none',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
        }}>
          {busy && <BtnSpinner color="#fca5b1"/>}
          {busy ? 'กำลังบันทึก…' : 'บันทึกต่อไป'}
        </button>
      </div>
    </div>
  );
}

function ModalShell({ children, onClose }) {
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 100,
      background: 'rgba(0,0,0,0.55)',
      backdropFilter: 'blur(10px)', WebkitBackdropFilter: 'blur(10px)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
      animation: 'vhFadeIn 200ms ease-out',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '100%', maxWidth: 520, maxHeight: '92vh', overflowY: 'auto',
        background: '#15151a',
        borderTopLeftRadius: 22, borderTopRightRadius: 22,
        border: '0.5px solid rgba(255,255,255,0.08)', borderBottom: 'none',
        boxShadow: '0 -20px 60px rgba(0,0,0,0.6)',
        animation: 'vhSlideUp 280ms cubic-bezier(.2,.7,.2,1)',
      }}>
        <div style={{ display: 'flex', justifyContent: 'center', padding: '8px 0 0' }}>
          <div style={{ width: 36, height: 4, borderRadius: 99, background: 'rgba(255,255,255,0.18)' }}/>
        </div>
        {children}
      </div>
    </div>
  );
}

Object.assign(window, { AddModal });
