/* Render screen — cost approval + render queue with clapperboard rows.
   Live mode (data._live + projectId): clicking Approve calls
   POST /api/projects/:id/approve, dispatches `cinematon:live-batch` so the
   app starts polling jobs, and the queue shows real progress. */

const ScreenRender = ({ data, projectId, batchId, batchKind }) => {
  const { cost, jobs, project } = data;
  const isLive = Boolean(data._live) && Boolean(projectId);

  // Approval state: in live mode, "approved" is implied by an active batchId.
  const [localApproved, setLocalApproved] = React.useState(false);
  const approved = isLive ? Boolean(batchId) : localApproved;

  const [acked, setAcked] = React.useState(false);
  const [showApproval, setShowApproval] = React.useState(true);
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);

  const totalApproved = jobs.reduce((a, j) => a + (j.cost || 0), 0);
  const completed = jobs.filter(j => j.status === "complete").length;
  const rendering = jobs.filter(j => j.status === "rendering").length;
  const failed = jobs.filter(j => j.status === "failed").length;

  const onApprove = async (kind) => {
    if (!isLive) { setLocalApproved(true); return; }
    setSubmitting(true); setError(null);
    try {
      const r = await window.CinematonAPI.approve(projectId, {
        kind,
        acknowledgement_text: kind === 'preview' ? window.CinematonAPI.ACK_PREVIEW : window.CinematonAPI.ACK_FULL,
      });
      // Tells app.jsx to start polling /api/batches/:id/status, which feeds data.jobs.
      window.dispatchEvent(new CustomEvent('cinematon:live-batch', { detail: { batch_id: r.batch.id, kind } }));
    } catch (err) { setError(err); }
    finally { setSubmitting(false); }
  };

  const onRetry = async (jobId, packet) => {
    if (!isLive) return;
    try {
      await window.CinematonAPI.resubmitJob(jobId, packet);
    } catch (err) { console.error('resubmit failed', err); setError(err); }
  };

  const onCancel = async (jobId) => {
    if (!isLive) return;
    if (!confirm('Cancel this job?')) return;
    try {
      await window.CinematonAPI.cancelJob(jobId);
    } catch (err) { console.error('cancel failed', err); setError(err); }
  };

  return (
    <div className="page" style={{ display: "grid", gridTemplateRows: "auto 1fr", height: "100%" }}>
      <PageHeader title="Render Queue" sub={`Stage 07 / 09 · seedance_2_0 · byteplus_modelark · ${jobs.length} jobs`}>
        {approved
          ? <span className="tag good"><Icon name="check" size={11}/> Cost approved · ${totalApproved.toFixed(2)}</span>
          : <span className="tag warn"><Icon name="warn" size={11}/> Awaiting cost approval</span>}
        <button className="btn ghost"><Icon name="regen"/> Refresh prices</button>
        <button className="btn">Pause queue</button>
      </PageHeader>

      <div style={{ display: "grid", gridTemplateColumns: showApproval && !approved ? "1fr 460px" : "1fr 320px", height: "100%", overflow: "hidden" }}>
        {/* Queue */}
        <div className="scroll" style={{ padding: 22 }}>
          {/* Summary tiles */}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12, marginBottom: 18 }}>
            <SummaryTile label="Complete" value={completed} of={jobs.length} kind="good"/>
            <SummaryTile label="Rendering" value={rendering} of={jobs.length} kind="accent"/>
            <SummaryTile label="Queued" value={jobs.filter(j=>j.status==="queued").length} of={jobs.length} kind="muted"/>
            <SummaryTile label="Failed" value={failed} of={jobs.length} kind="bad"/>
          </div>

          <div className="queue">
            <div className="qrow head">
              <div>SHOT</div><div>DUR</div><div>PROMPT</div><div>STATUS</div><div>PROGRESS</div><div>COST</div><div>ACT</div>
            </div>
            {jobs.map(j => {
              const shot = data.shots.find(s => s.id === j.id);
              return (
                <div key={j.id} className="qrow">
                  <div className="id">{j.id}</div>
                  <div className="mono dim">{shot.dur}s</div>
                  <div className="desc">{shot.desc}</div>
                  <div className={`status ${j.status}`}>
                    {j.status === "rendering" && <span className="row gap-sm" style={{ alignItems: "center" }}><MatonStatus progress={j.progress}/> RENDERING</span>}
                    {j.status === "complete" && "✓ COMPLETE"}
                    {j.status === "queued" && "○ QUEUED"}
                    {j.status === "failed" && "× FAILED"}
                  </div>
                  <div className="progress"><span style={{ width: `${j.progress}%` }}/></div>
                  <div className="mono">${j.cost.toFixed(2)}</div>
                  <div className="row gap-sm">
                    {j.status === "failed" && (
                      <button className="btn ghost" style={{ padding: "3px 8px", fontSize: 11 }} title="Resubmit"
                              onClick={() => onRetry(j.id)} disabled={!isLive}>
                        <Icon name="regen" size={11}/>
                      </button>
                    )}
                    {(j.status === "rendering" || j.status === "queued") && (
                      <button className="btn ghost" style={{ padding: "3px 8px", fontSize: 11 }} title="Cancel"
                              onClick={() => onCancel(j.id)} disabled={!isLive}>
                        ×
                      </button>
                    )}
                    {j.status === "complete" && (
                      <button className="btn ghost" style={{ padding: "3px 8px", fontSize: 11 }} title="Inspect">
                        <Icon name="eye" size={11}/>
                      </button>
                    )}
                  </div>
                </div>
              );
            })}
          </div>

          {/* Preview-before-paid: after preview batch completes, show actual frames + CTA */}
          {batchKind === 'preview' && completed === jobs.length && completed > 0 && (
            <PreviewReviewPanel
              projectId={projectId} jobs={jobs} cost={cost}
              isLive={isLive} submitting={submitting}
              onApproveFull={() => onApprove('full')}
              error={error}
            />
          )}

          {failed > 0 && (
            <div className="card" style={{ marginTop: 14, borderColor: "oklch(0.4 0.1 25)" }}>
              <div className="row" style={{ marginBottom: 8 }}>
                <Icon name="warn"/> <b style={{ color: "var(--bad)" }}>1 failed job</b>
              </div>
              <div className="mono" style={{ fontSize: 11.5, color: "var(--fg-muted)" }}>sh_015 · prompt_validation: lighting field below 8 chars</div>
              <div className="row gap-sm" style={{ marginTop: 10 }}>
                <button className="btn"><Icon name="regen"/> Auto-repair & retry</button>
                <button className="btn ghost">Open prompt</button>
              </div>
            </div>
          )}
        </div>

        {/* Right: cost approval OR job inspector */}
        <div className="panel right">
          {showApproval && !approved ? (
            <ApprovalPanel cost={cost} project={project} acked={acked} setAcked={setAcked}
              isLive={isLive} submitting={submitting} error={error}
              onApprovePreview={() => onApprove('preview')}
              onApproveFull={() => onApprove('full')}
              onClose={() => setShowApproval(false)}/>
          ) : (
            <div className="scroll" style={{ height: '100%' }}>
              <JobInspector job={jobs.find(j => j.status === "rendering") || jobs[0]} shots={data.shots}/>
              <RenderAuditPanel projectId={projectId} isLive={isLive} jobs={jobs} cost={cost} batchId={batchId}/>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// PRD §16.4: after a preview batch completes, the user reviews the actual
// generated frames before committing to the full $150 spend. Loads
// /assets?type=video and renders inline previews + an "Approve full" CTA.
const PreviewReviewPanel = ({ projectId, jobs, cost, isLive, submitting, onApproveFull, error }) => {
  const [assets, setAssets] = React.useState([]);
  React.useEffect(() => {
    if (!projectId || !window.CinematonAPI) return;
    let cancelled = false;
    window.CinematonAPI.listAssets(projectId, { type: 'video' })
      .then((a) => { if (!cancelled) setAssets(Array.isArray(a) ? a : []); })
      .catch(() => {});
    return () => { cancelled = true; };
  }, [projectId, jobs.length]);

  const previewShotIds = new Set(jobs.map((j) => j.id));
  const previewAssets = assets.filter((a) => previewShotIds.has(a.linked_entity_id));

  return (
    <div className="card" style={{ marginTop: 18, marginBottom: 14, borderColor: 'var(--accent-dim)', padding: 16 }}>
      <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12 }}>
        <div>
          <div className="card-title" style={{ marginBottom: 4 }}>Preview render complete</div>
          <div className="dim" style={{ fontSize: 12 }}>
            Review the {previewAssets.length || jobs.length} preview clip{previewAssets.length === 1 ? '' : 's'} below before committing the full ${cost.estimated_total_cost_usd.toFixed(2)} render.
          </div>
        </div>
        <button className="btn primary"
          onClick={onApproveFull}
          disabled={!isLive || submitting || cost.estimated_total_cost_usd > 150}
          style={{ padding: '10px 16px' }}>
          {submitting ? 'Submitting…' : `Approve full render · $${cost.estimated_total_cost_usd.toFixed(2)} / $150`}
        </button>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px, 1fr))', gap: 10 }}>
        {previewAssets.length > 0 ? previewAssets.map((a) => (
          <div key={a.asset_id} style={{ background: 'var(--bg-inset)', border: '1px solid var(--line)', borderRadius: 4, overflow: 'hidden' }}>
            <div style={{ background: 'var(--bg-3)', aspectRatio: '16/9', display: 'grid', placeItems: 'center' }}>
              {a.storage_uri.startsWith('mock://') ? (
                <span className="mono dim" style={{ fontSize: 10 }}>MOCK · {a.linked_entity_id}</span>
              ) : a.storage_uri.startsWith('file://') ? (
                <span className="mono dim" style={{ fontSize: 10 }}>file · {a.linked_entity_id}</span>
              ) : (
                <video src={a.storage_uri} controls preload="metadata" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
              )}
            </div>
            <div style={{ padding: '6px 8px', fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--fg-muted)' }}>{a.linked_entity_id}</div>
          </div>
        )) : (
          jobs.map((j) => (
            <div key={j.id} style={{ background: 'var(--bg-3)', aspectRatio: '16/9', borderRadius: 4, display: 'grid', placeItems: 'center' }}>
              <span className="mono dim" style={{ fontSize: 10 }}>asset pending · {j.id}</span>
            </div>
          ))
        )}
      </div>
      {error && (
        <div style={{ marginTop: 10, padding: 8, color: 'var(--bad)', fontSize: 11 }}>
          {error.message || String(error)}
        </div>
      )}
    </div>
  );
};

// PRD §16.5 + §29: surfaces approvals timeline + actual-vs-estimated cost
// after a batch is in flight. Loads /approvals + /cost-rollup, polls every
// 4s while jobs are still rendering so the actual cost ticks up live.
const RenderAuditPanel = ({ projectId, isLive, jobs, cost, batchId }) => {
  const [approvals, setApprovals] = React.useState([]);
  const [rollup, setRollup] = React.useState(null);
  const [loading, setLoading] = React.useState(false);

  const stillRendering = jobs.some((j) => j.status === 'rendering' || j.status === 'queued');

  React.useEffect(() => {
    if (!isLive || !projectId || !window.CinematonAPI) return;
    let cancelled = false;
    const refresh = async () => {
      setLoading(true);
      try {
        const [a, r] = await Promise.all([
          window.CinematonAPI.listApprovals(projectId).catch(() => []),
          window.CinematonAPI.costRollup(projectId).catch(() => null),
        ]);
        if (cancelled) return;
        setApprovals(Array.isArray(a) ? a : []);
        setRollup(r);
      } finally { if (!cancelled) setLoading(false); }
    };
    refresh();
    if (!stillRendering) return () => { cancelled = true; };
    const t = setInterval(refresh, 4000);
    return () => { cancelled = true; clearInterval(t); };
  }, [projectId, isLive, batchId, stillRendering]);

  if (!isLive) return null;

  const estimated = cost?.estimated_total_cost_usd ?? 0;
  const actual = rollup?.total_usd ?? 0;
  const pct = rollup?.pct_of_cap ?? 0;
  const cap = rollup?.cap_usd ?? 150;

  return (
    <div style={{ borderTop: '1px solid var(--line)', padding: 16 }}>
      <div className="card-title" style={{ marginBottom: 8 }}>Approvals ({approvals.length})</div>
      {approvals.length === 0 ? (
        <div className="dim mono" style={{ fontSize: 11 }}>(none yet)</div>
      ) : (
        <div className="col gap-sm" style={{ marginBottom: 14 }}>
          {approvals.map((a) => (
            <div key={a.approval_id} style={{ borderLeft: '2px solid var(--accent)', paddingLeft: 10 }}>
              <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
                <span className="mono" style={{ fontSize: 11 }}>{a.type === 'render_cost_acknowledgement' ? 'render approve' : a.type}</span>
                <span className="mono" style={{ fontSize: 11 }}>${(a.estimated_total_cost_usd || 0).toFixed(2)}</span>
              </div>
              <div className="dim mono" style={{ fontSize: 10 }}>{new Date(a.approved_at).toLocaleString()}</div>
            </div>
          ))}
        </div>
      )}

      <hr className="hr" style={{ margin: '12px 0' }}/>

      <div className="card-title" style={{ marginBottom: 8 }}>
        Spend {loading && <span className="dim mono" style={{ fontSize: 10 }}>· refreshing</span>}
      </div>
      <div className="col gap-sm" style={{ fontSize: 12 }}>
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <span className="muted">Estimated</span>
          <span className="mono">${estimated.toFixed(2)}</span>
        </div>
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <span className="muted">Actual (so far)</span>
          <span className="mono" style={{ color: actual > estimated ? 'var(--bad)' : 'var(--good)' }}>${actual.toFixed(2)}</span>
        </div>
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <span className="muted">% of $150 cap</span>
          <span className="mono">{pct.toFixed(1)}%</span>
        </div>
        <div style={{ height: 6, background: 'var(--bg-inset)', borderRadius: 3, overflow: 'hidden', marginTop: 4 }}>
          <div style={{ width: `${Math.min(100, (actual / cap) * 100)}%`, height: '100%', background: actual > estimated ? 'var(--bad)' : 'var(--accent)' }}/>
        </div>
      </div>

      {rollup && Object.keys(rollup.by_provider || {}).length > 0 && (
        <>
          <div className="card-title" style={{ marginBottom: 6, marginTop: 14 }}>By provider</div>
          <div className="col gap-sm" style={{ fontSize: 11 }}>
            {Object.entries(rollup.by_provider).map(([k, v]) => (
              <div key={k} className="row" style={{ justifyContent: 'space-between' }}>
                <span className="mono dim">{k}</span>
                <span className="mono">${(v.cost_usd || 0).toFixed(4)} <span className="dim">({v.calls})</span></span>
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
};

const SummaryTile = ({ label, value, of, kind }) => (
  <div className="card" style={{ padding: 14 }}>
    <div className="card-title" style={{ marginBottom: 6 }}>{label}</div>
    <div className="row" style={{ alignItems: "baseline", gap: 6 }}>
      <span className="serif" style={{ fontStyle: "italic", fontSize: 32, color: kind === "accent" ? "var(--accent)" : kind === "good" ? "var(--good)" : kind === "bad" ? "var(--bad)" : "var(--fg)" }}>{value}</span>
      <span className="mono dim" style={{ fontSize: 11 }}>/ {of}</span>
    </div>
  </div>
);

const ApprovalPanel = ({ cost, project, acked, setAcked, isLive, submitting, error,
                          onApprovePreview, onApproveFull, onClose }) => (
  <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
    <div className="panel-header">
      <div className="htitle">Render cost approval</div>
      <button className="btn ghost icon" onClick={onClose}><Icon name="x"/></button>
    </div>
    <div style={{ padding: 18, overflow: "auto" }}>
      <p className="serif" style={{ fontStyle: "italic", fontSize: 16, lineHeight: 1.5, color: "var(--fg-muted)", margin: "0 0 14px" }}>
        Before paid rendering, review the prompts, rights warnings, and estimated cost.
      </p>

      <div className="card" style={{ marginBottom: 14 }}>
        <div className="cost-row"><span className="label">Project</span><span className="v">{project?.title || '—'}</span></div>
        <div className="cost-row"><span className="label">Provider</span><span className="v">{cost.provider} / {cost.route}</span></div>
        <div className="cost-row"><span className="label">Total shots</span><span className="v">{cost.total_shots}</span></div>
        <div className="cost-row"><span className="label">Total seconds</span><span className="v">{cost.total_seconds}s</span></div>
        <div className="cost-row"><span className="label">Resolution</span><span className="v">{cost.resolution}</span></div>
        <div className="cost-row"><span className="label">Input mix</span><span className="v">{cost.input_mix.text_only_shots} text · {cost.input_mix.image_reference_shots} img</span></div>
      </div>

      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-title">Cost breakdown</div>
        <div className="cost-row"><span className="label">Provider video</span><span className="v">${cost.estimated_provider_cost_usd.toFixed(2)}</span></div>
        <div className="cost-row"><span className="label">Partner audio (music · sfx · ambience)</span><span className="v">${cost.estimated_partner_audio_cost_usd.toFixed(2)}</span></div>
        <div className="cost-row total"><span className="label">Estimated total</span><span className="v">${cost.estimated_total_cost_usd.toFixed(2)} USD</span></div>
        <div className="dim" style={{ fontSize: 10.5, marginTop: 4 }}>Estimate may shift ±8% if pricing, duration, resolution, or input type changes.</div>
      </div>

      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-title">Rights & safety</div>
        <div className="check ok"><span className="ico"/><div style={{ flex: 1 }}>No real-person likeness detected</div></div>
        <div className="check ok"><span className="ico"/><div style={{ flex: 1 }}>No copyrighted music or recognizable melodies</div></div>
        <div className="check ok"><span className="ico"/><div style={{ flex: 1 }}>Horror safety constraints satisfied (no minors in danger on-screen)</div></div>
      </div>

      <label className={`checkbox ${acked ? "checked" : ""}`}>
        <input type="checkbox" checked={acked} onChange={e => setAcked(e.target.checked)}/>
        <span>I have reviewed the prompts, rights warnings, and estimated render cost.</span>
      </label>

      {isLive ? (
        <div className="col gap-sm" style={{ marginTop: 14 }}>
          <button className="btn" disabled={!acked || submitting}
            style={{ width: "100%", justifyContent: "center", padding: 10, fontSize: 12 }}
            onClick={onApprovePreview}>
            {submitting ? 'Submitting…' : 'Approve preview render (≤$10)'}
          </button>
          <button className="btn primary" disabled={!acked || submitting || cost.estimated_total_cost_usd > 150}
            style={{ width: "100%", justifyContent: "center", padding: 12, fontSize: 13 }}
            onClick={onApproveFull}
            title={cost.estimated_total_cost_usd > 150 ? 'Estimate exceeds $150 cap' : ''}>
            <Icon name="render"/> Approve full render · ${cost.estimated_total_cost_usd.toFixed(2)} / $150
          </button>
        </div>
      ) : (
        <button className="btn primary" disabled={!acked}
          style={{ width: "100%", justifyContent: "center", padding: 12, marginTop: 14, fontSize: 13 }}
          onClick={onApproveFull}>
          <Icon name="render"/> Approve render · ${cost.estimated_total_cost_usd.toFixed(2)}
        </button>
      )}

      {error && (
        <div className="card" style={{ marginTop: 12, borderColor: 'oklch(0.4 0.1 25)', color: 'var(--bad)', fontSize: 11 }}>
          {error.message || String(error)}
        </div>
      )}
    </div>
  </div>
);

const JobInspector = ({ job, shots }) => {
  const shot = shots.find(s => s.id === job.id);
  return (
    <div>
      <div className="panel-header"><div className="htitle">Job · {job.id}</div></div>
      <div style={{ padding: 16 }} className="col gap-md">
        <div style={{ position: "relative", height: 180, borderRadius: 4, overflow: "hidden", border: "1px solid var(--line)" }}>
          <div className="thumb-placeholder" data-label={`RENDERING · ${job.progress}%`}/>
          <div className="thumb-noir"/>
        </div>
        <dl className="dl">
          <dt>Status</dt><dd className={`status ${job.status} mono`}>{job.status.toUpperCase()}</dd>
          <dt>Progress</dt><dd className="mono">{job.progress}%</dd>
          <dt>ETA</dt><dd className="mono">{job.eta}</dd>
          <dt>Cost</dt><dd className="mono">${job.cost.toFixed(2)}</dd>
          <dt>Shot</dt><dd>{shot.desc}</dd>
        </dl>
      </div>
    </div>
  );
};

// Tiny rendering indicator: the operator dot rides the beam from left → right
// at the same percent as the job's progress. The mark itself becomes the status.
const MatonStatus = ({ progress = 0 }) => {
  // beam runs from x=-72 (input marker) to x=72 (output marker), 144px range
  const cx = -72 + (Math.max(0, Math.min(100, progress)) / 100) * 144;
  return (
    <svg width="22" height="16" viewBox="-110 -100 220 200" style={{ flexShrink: 0 }} aria-hidden="true">
      <rect x="-78" y="-50" width="156" height="100" rx="6" fill="none" stroke="currentColor" strokeWidth="6" opacity="0.55"/>
      <line x1="-78" y1="0" x2="78" y2="0" stroke="#5e9bff" strokeWidth="6"/>
      <circle cx="-72" cy="0" r="6" fill="currentColor" opacity="0.55"/>
      <circle cx="72" cy="0" r="6" fill="currentColor" opacity="0.55"/>
      <circle cx={cx} cy="0" r="14" fill="#5e9bff">
        <animate attributeName="r" values="11;15;11" dur="1.4s" repeatCount="indefinite"/>
      </circle>
    </svg>
  );
};

window.ScreenRender = ScreenRender;
