/* ============================================================
   SPOTS — the multi-shoot layer for one commercial job.
   A project carries several SPOTS (films + a stills shoot); every
   scene is tagged to a spot. This screen is the home for that
   structure + the Requests inbox (client / photographer / director
   asks that hang off a scene). Reads/writes the SAME storyboard the
   Stripboard, Call Sheet and DPR use, so a request tagged here shows
   up everywhere the scene appears.
   ============================================================ */
const SPOT_COLORS = ['#F57F45', '#22D48A', '#5FB0E8', '#F7A8CF', '#FFB020', '#BD2555', '#A78BFA'];
const nextSpotCode = list => {
  const used = new Set(list.map(s => (s.code || '').toUpperCase()));
  for (const c of 'ABCDEFGHJKLMNPQRSTUVWXYZ') if (!used.has(c)) return c;
  return 'X';
};
const sd = s => (s.sched && s.sched.day) || 0;

/* ---- status pill that cycles requested → planned → captured → delivered ---- */
function AskStatus({ status, onCycle }) {
  const m = ASK_STATUS_META[status] || ASK_STATUS_META.requested;
  const tones = {
    amber:   'bg-amber/12 text-amber border-amber/30',
    electric:'bg-electric/12 text-electric-soft border-electric/30',
    proflow: 'bg-proflow/12 text-proflow-soft border-proflow/30',
    mint:    'bg-mint/12 text-mint border-mint/30',
  };
  return (
    <button onClick={onCycle} title="Click to advance status"
      className={cx('inline-flex items-center gap-1.5 whitespace-nowrap rounded-full border px-2 py-0.5 text-[10.5px] font-700 transition-colors hover:brightness-110', tones[m.tone])}>
      <Icon name={m.icon} size={11} />{m.label}
    </button>
  );
}

/* ---- one request row ---- */
function AskRow({ ask, onCycleSource, onCycleStatus, onText, onRemove }) {
  return (
    <div className="group/ask flex items-start gap-2.5 rounded-lg border border-line bg-ink-900 px-3 py-2">
      <button onClick={onCycleSource} title="Change who asked" className="mt-0.5 shrink-0 transition-transform hover:scale-105">
        <AskSourceTag source={ask.source} />
      </button>
      <div className="min-w-0 flex-1">
        <Edit value={ask.text} onChange={onText} multiline placeholder="Describe the request…"
          className="text-[12.5px] leading-snug text-zinc-200" />
      </div>
      <div className="flex shrink-0 items-center gap-1.5">
        <AskStatus status={ask.status} onCycle={onCycleStatus} />
        <button onClick={onRemove} title="Delete request"
          className="flex h-6 w-6 items-center justify-center rounded-md text-zinc-600 opacity-0 transition hover:bg-proflow/15 hover:text-proflow-soft group-hover/ask:opacity-100"><Icon name="Trash2" size={12} /></button>
      </div>
    </div>
  );
}

/* ---- compact dropdown to move a scene to another spot ---- */
function SpotMove({ current, spots, onPick }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return;
    const h = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const cur = spots.find(s => s.id === current);
  return (
    <span ref={ref} className="relative inline-block">
      <button onClick={e => { e.stopPropagation(); setOpen(o => !o); }} title="Move to another spot"
        className="inline-flex items-center gap-1 rounded-md border border-line bg-ink-900 px-1.5 py-1 font-mono text-[10px] font-700 text-zinc-300 transition-colors hover:border-ink-600">
        <span className="h-2 w-2 rounded-[3px]" style={{ background: cur ? cur.color : '#888' }} />
        {cur ? cur.code : '?'}<Icon name="ChevronDown" size={11} className="text-zinc-600" />
      </button>
      {open && (
        <div className="absolute right-0 z-30 mt-1 w-52 rounded-xl border border-line bg-ink-850 p-1.5 shadow-xl shadow-black/40">
          <div className="px-2 pb-1 pt-0.5 font-mono text-[9px] uppercase tracking-[0.12em] text-zinc-600">Move to spot</div>
          {spots.map(sp => (
            <button key={sp.id} onClick={() => { onPick(sp.id); setOpen(false); }}
              className={cx('flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-[12px] hover:bg-ink-800', sp.id === current ? 'font-700 text-zinc-100' : 'text-zinc-300')}>
              <span className="flex h-5 w-5 items-center justify-center rounded-md font-mono text-[10px] font-700" style={{ background: sp.color + '24', color: sp.color }}>{sp.code}</span>
              <span className="truncate">{sp.name}</span>
              {sp.id === current && <Icon name="Check" size={12} className="ml-auto text-mint" />}
            </button>
          ))}
        </div>
      )}
    </span>
  );
}

/* ---- a scene/look row with its requests tucked underneath ---- */
function SceneAsks({ sc, stills, spots, onMove, onAdd, onCycleSource, onCycleStatus, onText, onRemove }) {
  const asks = sc.asks || [];
  const day = sd(sc);
  return (
    <div className="rounded-xl border border-line bg-ink-850">
      <div className="flex flex-wrap items-center gap-2.5 px-3.5 py-2.5">
        <span className="flex h-7 min-w-[30px] items-center justify-center rounded-lg px-1.5 font-mono text-[12px] font-700"
          style={{ background: sc.color || '#2a2f3a', color: '#fff' }}>{sc.no}</span>
        <div className="min-w-0 flex-1">
          <div className="truncate text-[13px] font-700 text-head">{sc.title}</div>
          <div className="truncate font-mono text-[10px] text-zinc-500">{sc.loc}</div>
        </div>
        <span className={cx('inline-flex items-center gap-1 rounded-md px-2 py-0.5 font-mono text-[10px] font-700',
          day ? 'bg-ink-700 text-zinc-300' : 'bg-amber/12 text-amber')}>
          <Icon name={day ? 'CalendarCheck' : 'CalendarOff'} size={11} />{day ? (stills ? 'Stills day ' : 'Day ') + day : 'Unscheduled'}
        </span>
        {spots && spots.length > 1 && <SpotMove current={sceneSpot(sc)} spots={spots} onPick={id => onMove(sc.id, id)} />}
        <button onClick={onAdd}
          className="inline-flex items-center gap-1 rounded-lg border border-dashed border-ink-600 px-2 py-1 text-[11px] font-700 text-zinc-400 transition-colors hover:border-electric/50 hover:text-electric-soft">
          <Icon name="Plus" size={12} /> Request
        </button>
      </div>
      {asks.length > 0 && (
        <div className="space-y-1.5 border-t border-line/70 px-3.5 py-2.5">
          {asks.map(a => (
            <AskRow key={a.id} ask={a}
              onCycleSource={() => onCycleSource(sc.id, a.id)}
              onCycleStatus={() => onCycleStatus(sc.id, a.id)}
              onText={v => onText(sc.id, a.id, v)}
              onRemove={() => onRemove(sc.id, a.id)} />
          ))}
        </div>
      )}
    </div>
  );
}

/* ---- spot card / selector ---- */
function SpotCard({ spot, scenes, active, onSelect, onPatch, onRemove, canRemove, lead }) {
  const list = scenes.filter(s => sceneSpot(s) === spot.id);
  const scheduled = list.filter(s => sd(s) > 0).length;
  const days = [...new Set(list.filter(s => sd(s) > 0).map(s => sd(s)))].sort((a, b) => a - b);
  const asks = list.reduce((a, s) => a + (s.asks || []).length, 0);
  const open = list.reduce((a, s) => a + openAsks(s), 0);
  const stills = spot.type === 'stills';
  return (
    <div onClick={onSelect}
      className={cx('group relative cursor-pointer overflow-hidden rounded-2xl border bg-ink-850 p-4 transition-colors',
        active ? '' : 'border-line hover:border-ink-600')}
      style={active ? { borderColor: spot.color, boxShadow: `inset 0 0 0 1px ${spot.color}` } : undefined}>
      <div className="absolute right-0 top-0 h-full w-1" style={{ background: spot.color, opacity: active ? 1 : 0.4 }} />
      <div className="flex items-start gap-3">
        <span className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl font-display text-[18px] font-700"
          style={{ background: spot.color + '24', color: spot.color }}>{spot.code}</span>
        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2">
            <Edit value={spot.name} onChange={v => onPatch({ name: v })} className="text-[15px] font-700 text-head" />
            <span className="inline-flex items-center gap-1 rounded-md bg-ink-800 px-1.5 py-0.5 font-mono text-[9.5px] font-700 uppercase tracking-wide text-zinc-400">
              <Icon name={stills ? 'Camera' : 'Film'} size={10} />{stills ? 'Stills' : 'Film'}
            </span>
          </div>
          <div className="mt-0.5 font-mono text-[10.5px] text-zinc-500"><Edit value={spot.format} onChange={v => onPatch({ format: v })} className="font-mono text-[10.5px]" /></div>
        </div>
        {canRemove && (
          <button onClick={e => { e.stopPropagation(); onRemove(); }} title="Delete spot"
            className="flex h-7 w-7 items-center justify-center rounded-md text-zinc-600 opacity-0 transition hover:bg-proflow/15 hover:text-proflow-soft group-hover:opacity-100"><Icon name="Trash2" size={13} /></button>
        )}
      </div>

      <div className="mt-3 text-[12px] leading-snug text-zinc-400"><Edit value={spot.deliverable} onChange={v => onPatch({ deliverable: v })} multiline className="text-[12px] text-zinc-400" /></div>

      <div className="mt-3 flex flex-wrap items-center gap-x-4 gap-y-1.5 border-t border-line/70 pt-3">
        <span className="inline-flex items-center gap-1.5 text-[12px] font-600 text-zinc-300">
          <Icon name={stills ? 'Images' : 'Clapperboard'} size={13} className="text-zinc-500" />
          {list.length} <span className="font-normal text-zinc-500">{stills ? 'looks' : 'scenes'}</span>
        </span>
        <span className="inline-flex items-center gap-1.5 text-[12px] font-600 text-zinc-300">
          <Icon name="CalendarRange" size={13} className="text-zinc-500" />
          {days.length ? days.map(d => 'D' + d).join(' ') : '—'} <span className="font-normal text-zinc-500">({scheduled}/{list.length})</span>
        </span>
        {open > 0 && (
          <span className="inline-flex items-center gap-1.5 text-[12px] font-700 text-amber">
            <Icon name="MessageSquareDot" size={13} />{open} <span className="font-normal text-amber/80">open</span>
          </span>
        )}
      </div>

      <div className="mt-2.5 flex items-center gap-2">
        {lead && <span className="inline-flex items-center gap-1.5"><Avatar name={lead.name} color={lead.color} size={20} /><span className="text-[11px] text-zinc-400">{spot.lead}</span></span>}
        <span className="ml-auto font-mono text-[10px] text-zinc-600">{asks} request{asks === 1 ? '' : 's'}</span>
      </div>
    </div>
  );
}

function SpotsModule({ mobile }) {
  const [scenes, setScenes] = useStoryboard();
  const [spots, setSpots] = useSpots();
  const [active, setActive] = useActiveSpot();
  const ctx = useContext(AccessContext);
  const setModule = ctx && ctx.setModule;

  /* ---- spot mutations ---- */
  const patchSpot = (id, p) => setSpots(ss => ss.map(s => s.id === id ? { ...s, ...p } : s));
  const addSpot = () => setSpots(ss => {
    const code = nextSpotCode(ss);
    return [...ss, { id: 'spot-' + Date.now(), code, name: 'New Spot ' + code, type: 'film',
      color: SPOT_COLORS[ss.length % SPOT_COLORS.length], format: 'TBD', deliverable: 'Describe the deliverable…', lead: '' }];
  });
  const removeSpot = id => {
    const sp = spots.find(s => s.id === id);
    const n = scenes.filter(s => sceneSpot(s) === id).length;
    if (n > 0) { window.toast && window.toast(`Move the ${n} scene${n === 1 ? '' : 's'} off ${sp ? sp.name : 'this spot'} first`, 'Info'); return; }
    setSpots(ss => ss.filter(s => s.id !== id));
    if (active === id) setActive('all');
    window.toast && window.toast('Spot removed', 'Check');
  };

  /* ---- ask (request) mutations on the shared storyboard ---- */
  const mapScene = (scId, fn) => setScenes(ss => ss.map(s => s.id === scId ? fn(s) : s));
  const addAsk = scId => mapScene(scId, s => ({ ...s, asks: [...(s.asks || []), { id: 'as-' + Date.now(), source: 'Client', text: '', status: 'requested' }] }));
  const removeAsk = (scId, aId) => mapScene(scId, s => ({ ...s, asks: (s.asks || []).filter(a => a.id !== aId) }));
  const textAsk = (scId, aId, v) => mapScene(scId, s => ({ ...s, asks: (s.asks || []).map(a => a.id === aId ? { ...a, text: v } : a) }));
  const moveScene = (scId, spotId) => mapScene(scId, s => ({ ...s, spot: spotId, kind: (spots.find(p => p.id === spotId) || {}).type === 'stills' ? 'stills' : 'film' }));
  const SRC = Object.keys(ASK_SOURCES);
  const cycleSource = (scId, aId) => mapScene(scId, s => ({ ...s, asks: (s.asks || []).map(a => a.id === aId ? { ...a, source: SRC[(SRC.indexOf(a.source) + 1) % SRC.length] } : a) }));
  const cycleStatus = (scId, aId) => mapScene(scId, s => ({ ...s, asks: (s.asks || []).map(a => a.id === aId ? { ...a, status: ASK_STATUS[(ASK_STATUS.indexOf(a.status) + 1) % ASK_STATUS.length] } : a) }));

  /* ---- scope ---- */
  const inScope = spots.filter(sp => active === 'all' || sp.id === active);
  const filmCount = scenes.filter(s => sceneSpot(s) !== 'stills' && (spots.find(p => p.id === sceneSpot(s)) || {}).type !== 'stills').length;
  const stillsCount = scenes.filter(s => (spots.find(p => p.id === sceneSpot(s)) || {}).type === 'stills').length;
  const openTotal = scenes.reduce((a, s) => a + openAsks(s), 0);
  const leadOf = sp => PEOPLE.find(p => p.name === sp.lead);

  const deepLink = id => setModule && setModule(id);

  if (!spots.length) {
    return (
      <div className="fade-up">
        <SectionTitle kicker="This job" icon="Film" title="Spots" />
        <Panel><div className="px-4 py-12 text-center">
          <Icon name="Film" size={26} className="mx-auto text-zinc-600" />
          <div className="mt-2 text-[13px] font-600 text-zinc-300">No spots yet</div>
          <div className="mt-1 text-[12px] text-zinc-500">A project can carry several films + a stills shoot. Add the first one.</div>
          <button onClick={addSpot} className="mt-4 inline-flex items-center gap-2 rounded-xl bg-cta px-4 py-2.5 text-[12.5px] font-700 text-cta-ink hover:bg-cta-hover"><Icon name="Plus" size={15} /> Add spot</button>
        </div></Panel>
      </div>
    );
  }

  return (
    <div className="fade-up">
      <SectionTitle kicker="This job" icon="Film" title="Spots"
        action={<div className="flex items-center gap-2.5">
          <Chip tone="neutral"><Icon name="Film" size={12} /> {spots.length} spots</Chip>
          {openTotal > 0 && <Chip tone="amber"><Icon name="MessageSquareDot" size={12} /> {openTotal} open requests</Chip>}
          <button onClick={addSpot} className="flex items-center gap-2 rounded-xl bg-cta px-3.5 py-2.5 text-[12.5px] font-700 text-cta-ink transition-colors hover:bg-cta-hover"><Icon name="Plus" size={15} /> Add spot</button>
        </div>} />

      <p className="mb-5 max-w-[680px] text-[13px] leading-relaxed text-zinc-400">
        One job, many deliverables. Each <span className="text-zinc-200">spot</span> groups its own scenes (or stills looks) and flows into the
        Stripboard, Call Sheet and Daily Report — pick a spot once and every screen scopes to it. Client &amp; photographer
        <span className="text-zinc-200"> requests</span> hang off the exact scene they belong to.
      </p>

      {/* spot cards */}
      <div className={cx('grid gap-3.5', mobile ? 'grid-cols-1' : 'grid-cols-2')}>
        <div onClick={() => setActive('all')}
          className={cx('flex cursor-pointer items-center gap-3 rounded-2xl border bg-ink-850 p-4 transition-colors',
            active === 'all' ? 'border-zinc-400' : 'border-line hover:border-ink-600')}
          style={active === 'all' ? { boxShadow: 'inset 0 0 0 1px rgb(var(--z400))' } : undefined}>
          <span className="flex h-11 w-11 items-center justify-center rounded-xl bg-ink-700 text-zinc-300"><Icon name="LayoutGrid" size={19} /></span>
          <div className="min-w-0">
            <div className="text-[15px] font-700 text-head">All spots</div>
            <div className="font-mono text-[10.5px] text-zinc-500">{filmCount} film scenes · {stillsCount} stills looks</div>
          </div>
        </div>
        {spots.map(sp => (
          <SpotCard key={sp.id} spot={sp} scenes={scenes} active={active === sp.id}
            onSelect={() => setActive(active === sp.id ? 'all' : sp.id)}
            onPatch={p => patchSpot(sp.id, p)} onRemove={() => removeSpot(sp.id)} canRemove={spots.length > 1}
            lead={leadOf(sp)} />
        ))}
      </div>

      {/* deep links */}
      <div className="mt-4 flex flex-wrap items-center gap-2">
        <span className="font-mono text-[10px] uppercase tracking-[0.14em] text-zinc-500">
          {active === 'all' ? 'All spots' : (spots.find(s => s.id === active) || {}).name} →
        </span>
        {[['schedule', 'Stripboard', 'CalendarRange'], ['production', 'Call Sheet', 'ClipboardList'], ['dpr', 'Daily Report', 'ClipboardCheck']].map(([id, label, icon]) => (
          <button key={id} onClick={() => deepLink(id)}
            className="inline-flex items-center gap-1.5 rounded-lg border border-line bg-ink-900 px-2.5 py-1.5 text-[11.5px] font-600 text-zinc-300 transition-colors hover:border-ink-600 hover:text-head">
            <Icon name={icon} size={13} className="text-zinc-500" />{label}<Icon name="ArrowUpRight" size={12} className="text-zinc-600" />
          </button>
        ))}
      </div>

      {/* requests inbox, grouped by spot → scene */}
      <div className="mt-8">
        <div className="mb-4 flex items-center gap-2.5">
          <Icon name="Inbox" size={16} className="text-electric-soft" />
          <h3 className="font-display text-[18px] font-800 italic text-head">Requests</h3>
          <span className="font-mono text-[11px] text-zinc-500">client · photographer · director asks, on the scene they belong to</span>
        </div>

        <div className="space-y-6">
          {inScope.map(sp => {
            const list = scenes.filter(s => sceneSpot(s) === sp.id);
            if (!list.length) return null;
            const stills = sp.type === 'stills';
            return (
              <div key={sp.id}>
                <div className="mb-2.5 flex items-center gap-2">
                  <SpotBadge spotId={sp.id} />
                  <span className="text-[13px] font-700 text-zinc-200">{sp.name}</span>
                  <span className="font-mono text-[10px] text-zinc-600">{list.length} {stills ? 'looks' : 'scenes'}</span>
                </div>
                <div className="space-y-2.5">
                  {list.map(sc => (
                    <SceneAsks key={sc.id} sc={sc} stills={stills} spots={spots} onMove={moveScene}
                      onAdd={() => addAsk(sc.id)} onCycleSource={cycleSource} onCycleStatus={cycleStatus}
                      onText={textAsk} onRemove={removeAsk} />
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

window.SpotsModule = SpotsModule;
