// Admission decisions drawer — a read-only, operator-facing view of what the
// orchestrator decided on each recent pass, newest first.
//
// line-manager-visibility (roadmap Sprint 2): before this, runAdmissionPass's
// results existed only as a conductor.log line, so "why did nothing get admitted
// today?" was unanswerable from the board.

const { useState: useStateAD, useEffect: useEffectAD, useRef: useRefAD } = React;

// State-changing activity outcomes shown to operators. All other manager-pass
// rows are deliberately represented only by the one collapsed no-op timestamp.
// This is an activity feed, not a dump of manager heartbeats. `held` is
// accepted for compatibility with projected Layer-1 activity; the backend's
// current persisted feed returns promoted/rejected only.
const ADMISSION_OUTCOMES = {
  'promoted': { label: 'Promoted', tone: 'bg-emerald-100 text-emerald-800' },
  'held': { label: 'Held', tone: 'bg-amber-100 text-amber-800' },
  'rejected': { label: 'Rejected', tone: 'bg-red-100 text-red-800' },
};
const STATE_CHANGING_OUTCOMES = new Set(['promoted', 'held', 'rejected']);

// dependency-relaxation-proposals: `ceilings` (snapshot meta.ceilings) and `alProjectId`
// feed the CeilingsLine and PlanRelaxationsPanel drawn under the decisions — same
// question, same drawer. Both are optional: an old snapshot or a demo board renders the
// decisions alone.
const AdmissionDecisionsDrawer = ({ open, onClose, ceilings, alProjectId }) => {
  const [decisions, setDecisions] = useStateAD([]);
  const [lastNoopPass, setLastNoopPass] = useStateAD(null);
  const [loading, setLoading] = useStateAD(false);
  const [error, setError] = useStateAD(null);
  const closeButton = useRefAD(null);

  const loadDecisions = async () => {
    setLoading(true);
    setError(null);
    try {
      const data = await window.Api.getAdmissionDecisions();
      // Do not let an old proxy, malformed response, or a future noisy outcome
      // turn this signal-only view back into a heartbeat log. In compact form,
      // the invariant is: filter(decision => STATE_CHANGING_OUTCOMES.has(decision?.outcome)).
      setDecisions(Array.isArray(data?.decisions)
        ? data.decisions.filter((decision) => STATE_CHANGING_OUTCOMES.has(decision?.outcome))
        : []);
      setLastNoopPass(typeof data?.lastNoopPass?.timestamp === 'string' && data.lastNoopPass.timestamp
        ? data.lastNoopPass
        : null);
    } catch (err) {
      setError(err);
      setDecisions([]);
      setLastNoopPass(null);
    } finally {
      setLoading(false);
    }
  };

  useEffectAD(() => {
    if (!open) return undefined;
    loadDecisions();
    // Focus moves in after the drawer has committed to the page.
    const focusTimer = setTimeout(() => closeButton.current?.focus(), 0);
    const onKeyDown = (event) => { if (event.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKeyDown);
    return () => {
      clearTimeout(focusTimer);
      window.removeEventListener('keydown', onKeyDown);
    };
  }, [open]);

  if (!open) return null;

  return (
    <div className="fixed inset-0 z-50 flex">
      <div className="flex-1 bg-black/30" onClick={onClose} aria-hidden="true" />
      <aside
        role="dialog"
        aria-modal="true"
        aria-labelledby="admission-decisions-title"
        className="w-[600px] max-w-[90vw] bg-white shadow-2xl flex flex-col"
      >
        <header className="px-6 py-4 border-b border-neutral-200 flex items-center justify-between gap-4 shrink-0">
          <div>
            <h2 id="admission-decisions-title" className="text-lg font-black text-neutral-800">Admission decisions</h2>
            <p className="text-xs text-neutral-500 mt-0.5">The orchestrator decides what is admitted. The line-manager pass checks eligibility and applies that choice.</p>
          </div>
          <div className="flex items-center gap-3 shrink-0">
            <button
              type="button"
              onClick={loadDecisions}
              disabled={loading}
              className="text-sm font-bold text-indigo-700 hover:text-indigo-900 disabled:text-neutral-400"
            >
              Refresh
            </button>
            <button
              ref={closeButton}
              type="button"
              onClick={onClose}
              aria-label="Close admission decisions"
              className="text-neutral-500 hover:text-neutral-800"
            >
              ✕
            </button>
          </div>
        </header>
        <div className="flex-1 overflow-y-auto p-4">
          {window.CeilingsLine && <div className="mb-3"><window.CeilingsLine ceilings={ceilings} /></div>}
          {loading && <div role="status" className="text-sm text-neutral-500 italic">Loading admission decisions…</div>}
          {!loading && error && (
            <div role="alert" className="text-sm text-red-700 bg-red-50 border border-red-200 rounded p-3">
              Unable to load admission decisions. The conductor may not be running.
            </div>
          )}
          {!loading && !error && decisions.length === 0 && !lastNoopPass && (
            <div role="status" className="text-sm text-neutral-500 italic">
              No admission decisions yet. They appear after the line manager's next pass.
            </div>
          )}
          {!loading && !error && lastNoopPass && (
            <div role="status" className="mb-3 border border-neutral-200 rounded-lg p-3 bg-neutral-50 flex items-center justify-between gap-3">
              <span className="text-sm font-semibold text-neutral-700">Nothing to do</span>
              <time className="text-[11px] font-mono text-neutral-500 shrink-0">{lastNoopPass.timestamp}</time>
            </div>
          )}
          {!loading && !error && decisions.length > 0 && (
            <ol className="flex flex-col gap-3">
              {decisions.map((decision, index) => {
                const outcome = ADMISSION_OUTCOMES[decision.outcome]
                  ?? { label: String(decision.outcome || 'Unknown outcome'), tone: 'bg-neutral-200 text-neutral-700' };
                return (
                  <li
                    key={`${decision.timestamp}-${decision.pmProjectId}-${decision.outcome}-${index}`}
                    className="border border-neutral-200 rounded-lg p-3 bg-neutral-50"
                  >
                    <div className="flex items-start justify-between gap-3">
                      <span className={`px-2 py-0.5 rounded-full text-xs font-bold ${outcome.tone}`}>{outcome.label}</span>
                      <time className="text-[11px] font-mono text-neutral-500 shrink-0">{decision.timestamp}</time>
                    </div>
                    {/* featureKey and reason are null for the outcomes the agent
                        is never consulted for — render nothing rather than an
                        empty label that reads like missing data. */}
                    {decision.featureKey && (
                      <div className="mt-2 text-xs text-neutral-700">
                        <span className="font-bold text-neutral-500">Feature:</span> {decision.featureKey}
                      </div>
                    )}
                    {decision.reason && (
                      <div className="mt-1 text-xs text-neutral-700">
                        <span className="font-bold text-neutral-500">Reason:</span> {decision.reason}
                      </div>
                    )}
                    <div className="mt-1 text-[11px] font-mono text-neutral-400">
                      project {decision.pmProjectId} → {decision.alProjectId}
                    </div>
                  </li>
                );
              })}
            </ol>
          )}
          {window.PlanRelaxationsPanel && <window.PlanRelaxationsPanel alProjectId={alProjectId} />}
        </div>
      </aside>
    </div>
  );
};

window.AdmissionDecisionsDrawer = AdmissionDecisionsDrawer;
