/* Sprint Planner — main app. Wires state, drawers, scenarios, tweaks. */

const { useState: useStateA, useEffect: useEffectA, useRef: useRefA, useCallback: useCallbackA } = React;

/* ============ SCENARIOS ============ */
// Three scenarios reshape the sprint contents:
//   healthy   — rebalanced: nobody over capacity
//   over      — Tom over-allocated by 11h (default; matches v1 demo)
//   under     — under-loaded: lots of slack, AI suggests pulling more

function buildSprint(scenario) {
  if (scenario === 'over') return SPRINT_SEED.slice();
  if (scenario === 'healthy') {
    // Rebalance: move tom's overflow to jin
    return SPRINT_SEED.map(t => {
      if (['SYM-414', 'SYM-417', 'SYM-422'].includes(t.id)) return { ...t, assignee: 'jin' };
      return t;
    }).filter(t => t.id !== 'SYM-422'); // also drop one to bring tom in range
  }
  if (scenario === 'under') {
    // Drop heavy tickets to create slack
    return SPRINT_SEED.filter(t => !['SYM-414', 'SYM-417', 'SYM-422', 'SYM-406', 'SYM-410'].includes(t.id));
  }
  return SPRINT_SEED.slice();
}

/* ============ MAIN APP ============ */

function App() {
  /* ----- Tweaks (persisted via __edit_mode_set_keys) ----- */
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "metaphor": "lanes",
    "scenario": "over",
    "sprintState": "planning",
    "theme": "light"
  }/*EDITMODE-END*/;

  const [tweaks, setTweaks] = useStateA(TWEAK_DEFAULTS);
  const setTweak = (k, v) => {
    const next = typeof k === 'object' ? { ...tweaks, ...k } : { ...tweaks, [k]: v };
    setTweaks(next);
    try { window.parent.postMessage({ type: '__edit_mode_set_keys', edits: next }, '*'); } catch {}
  };

  /* ----- Sprint state ----- */
  const [project, setProject]   = useStateA(PROJECTS[0]);
  const [sprintId, setSprintId] = useStateA('s24');
  const sprint = SPRINTS.find(s => s.id === sprintId);

  const [sprintTickets, setSprintTickets] = useStateA(() => buildSprint(tweaks.scenario));
  const [backlog,       setBacklog]       = useStateA(BACKLOG_SEED.slice());

  // Reset state on scenario change
  useEffectA(() => {
    const fresh = buildSprint(tweaks.scenario);
    const inSprint = new Set(fresh.map(t => t.id));
    setSprintTickets(fresh);
    setBacklog(BACKLOG_SEED.filter(t => !inSprint.has(t.id)));
  }, [tweaks.scenario]);

  // Theme
  useEffectA(() => {
    document.documentElement.setAttribute('data-theme', tweaks.theme === 'dark' ? 'dark' : '');
  }, [tweaks.theme]);

  // Expose backlog to Suggestion component
  useEffectA(() => { window.__backlog = backlog; }, [backlog]);

  /* ----- Drag, drawers, toast ----- */
  const [dragTicket, setDragTicket]       = useStateA(null);
  const [backlogOpen, setBacklogOpen]     = useStateA(false);
  const [switcherOpen, setSwitcherOpen]   = useStateA(false);
  const [editOpen, setEditOpen]           = useStateA(false);
  const [multiSelect, setMultiSelect]     = useStateA([]);
  const [toast, setToast]                 = useStateA(null);
  const undoStackRef = useRefA([]);

  const pushHistory = (snapshot) => {
    undoStackRef.current.push(snapshot);
    if (undoStackRef.current.length > 20) undoStackRef.current.shift();
  };

  const showToast = (msg, canUndo = true) => setToast({ msg, canUndo });

  /* ----- Mutations ----- */
  const addToSprint = useCallbackA((t, assignTo) => {
    if (sprintTickets.find(x => x.id === t.id)) {
      // already in sprint — just reassign if needed
      if (assignTo && t.assignee !== assignTo) reassign(t, assignTo);
      return;
    }
    pushHistory({ sprintTickets, backlog });
    const finalT = assignTo ? { ...t, assignee: assignTo } : t;
    setSprintTickets(prev => [...prev, finalT]);
    setBacklog(prev => prev.filter(x => x.id !== t.id));
    showToast(`Added ${t.id} to sprint${assignTo ? ' · assigned to ' + PEOPLE.find(p => p.id === assignTo).name.split(' ')[0] : ''}`);
  }, [sprintTickets, backlog]);

  const removeFromSprint = useCallbackA((id) => {
    pushHistory({ sprintTickets, backlog });
    const t = sprintTickets.find(x => x.id === id);
    if (!t) return;
    setSprintTickets(prev => prev.filter(x => x.id !== id));
    setBacklog(prev => [t, ...prev]);
    showToast(`Removed ${t.id} · returned to backlog`);
  }, [sprintTickets, backlog]);

  const reassign = useCallbackA((t, toId) => {
    pushHistory({ sprintTickets, backlog });
    setSprintTickets(prev => prev.map(x => x.id === t.id ? { ...x, assignee: toId } : x));
    showToast(`Reassigned ${t.id} → ${PEOPLE.find(p => p.id === toId).name.split(' ')[0]}`);
  }, [sprintTickets, backlog]);

  // Unified handler — called by drag drop on lanes
  const onLaneAssign = useCallbackA((t, toId) => {
    if (sprintTickets.find(x => x.id === t.id)) {
      reassign(t, toId);
    } else {
      addToSprint(t, toId);
    }
  }, [sprintTickets, reassign, addToSprint]);

  const onBulkAdd = useCallbackA((ids) => {
    pushHistory({ sprintTickets, backlog });
    const tx = backlog.filter(t => ids.includes(t.id));
    setSprintTickets(prev => [...prev, ...tx]);
    setBacklog(prev => prev.filter(t => !ids.includes(t.id)));
    setMultiSelect([]);
    showToast(`Added ${tx.length} tickets to sprint`);
  }, [sprintTickets, backlog]);

  const undo = useCallbackA(() => {
    const snap = undoStackRef.current.pop();
    if (!snap) return;
    setSprintTickets(snap.sprintTickets);
    setBacklog(snap.backlog);
    setToast(null);
  }, []);

  /* ----- Sprint lock — actually disabled if over ----- */
  const overPeopleCount = PEOPLE.filter(p => personBucket(p, sprintTickets).overH > 0).length;
  const canLock = overPeopleCount === 0;

  const onLock = () => {
    if (!canLock) {
      showToast(`Can't lock — ${overPeopleCount} person${overPeopleCount > 1 ? 's' : ''} over capacity`, false);
      return;
    }
    setTweak('sprintState', 'locked');
    showToast(`Sprint locked · ${sprintTickets.reduce((s, t) => s + t.hours, 0)}h committed`, false);
  };

  const onClose = () => {
    window.location.href = 'pms2-sprint-close.html';
  };

  const onEditSave = (patch) => {
    // In a real app we'd persist; for the mock we just toast
    showToast('Sprint settings saved');
  };

  /* ----- Keyboard ----- */
  useEffectA(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'z') { e.preventDefault(); undo(); }
      if (e.key === 'b' && !e.target.matches('input,textarea')) { setBacklogOpen(o => !o); }
      if (e.key === 'Escape') {
        setBacklogOpen(false); setSwitcherOpen(false); setEditOpen(false);
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [undo]);

  /* ----- Tweaks panel protocol ----- */
  const [tweaksOpen, setTweaksOpen] = useStateA(false);
  useEffectA(() => {
    const onMsg = (e) => {
      if (e.data?.type === '__activate_edit_mode')   setTweaksOpen(true);
      if (e.data?.type === '__deactivate_edit_mode') setTweaksOpen(false);
    };
    window.addEventListener('message', onMsg);
    try { window.parent.postMessage({ type: '__edit_mode_available' }, '*'); } catch {}
    return () => window.removeEventListener('message', onMsg);
  }, []);

  /* ----- Render ----- */
  return (
    <div className="sp-app" data-state={tweaks.sprintState}>
      <Header
        sprint={sprint}
        project={project}
        state={tweaks.sprintState}
        onSwitcher={() => setSwitcherOpen(true)}
        onEdit={() => setEditOpen(true)}
        onClose={onClose}
        onLock={onLock}
      />

      <Banner tickets={sprintTickets} state={tweaks.sprintState} />

      {/* Soft banner row depending on sprint state */}
      {tweaks.sprintState === 'closing' && (
        <div className="state-banner state-banner--warn">
          <Icon d={ICONS.flag} size={14} />
          <span><strong>Sprint ends in 2 days.</strong> 4 tickets still open — start reviewing or auto-carry them into Sprint 25.</span>
          <button className="btn btn--sm btn--accent" onClick={onClose}>Open review →</button>
        </div>
      )}
      {tweaks.sprintState === 'locked' && (
        <div className="state-banner">
          <Icon d={ICONS.lock} size={14} />
          <span><strong>Sprint is locked.</strong> Tickets are read-only. Unlock to make changes or close the sprint when it ends.</span>
          <button className="btn btn--sm" onClick={() => setTweak('sprintState', 'planning')}>Unlock</button>
        </div>
      )}
      {tweaks.sprintState === 'closed' && (
        <div className="state-banner state-banner--good">
          <Icon d={ICONS.check} size={14} />
          <span><strong>Sprint closed.</strong> Shipped 38pt · 84h. Velocity updated. Carry-over moved to Sprint 25.</span>
          <a className="btn btn--sm" href="pms2-sprint-close.html">View review</a>
        </div>
      )}

      {/* Capacity legend — labels the bar elements inline */}
      {tweaks.metaphor === 'lanes' && (
        <div className="lane-legend">
          <span className="lane-legend__title">Capacity ·</span>
          <span className="lane-legend__sw lane-legend__sw--filled" /> Used
          <span className="lane-legend__sw lane-legend__sw--pto" /> PTO
          <span className="lane-legend__sw lane-legend__sw--overflow" /> Over
          <span className="lane-legend__sw lane-legend__sw--limit" /> Contracted limit
          <span className="lane-legend__hint">·  drag tickets between lanes to reassign · click + on backlog drawer to add</span>
        </div>
      )}

      {/* The view itself */}
      <main className="sp-view">
        {tweaks.metaphor === 'lanes' && (
          <Lanes
            tickets={sprintTickets}
            onAssign={onLaneAssign}
            onRemove={removeFromSprint}
            dragTicket={dragTicket}
            setDragTicket={setDragTicket}
            scenario={tweaks.scenario}
            state={tweaks.sprintState}
          />
        )}
        {tweaks.metaphor === 'twocol' && (
          <TwoColumn
            backlog={backlog}
            sprintTickets={sprintTickets}
            onAdd={addToSprint}
            onRemove={removeFromSprint}
            dragTicket={dragTicket}
            setDragTicket={setDragTicket}
          />
        )}
        {tweaks.metaphor === 'timeline' && (
          <Timeline
            tickets={sprintTickets}
            onRemove={removeFromSprint}
            dragTicket={dragTicket}
            setDragTicket={setDragTicket}
            onAssign={onLaneAssign}
          />
        )}
      </main>

      {/* Floating action bar */}
      <div className="sp-fab">
        <button className="sp-fab__main" onClick={() => setBacklogOpen(true)}>
          <Icon d={ICONS.plus} size={14} />
          Backlog
          <span className="sp-fab__count">{backlog.length}</span>
          <kbd>B</kbd>
        </button>
      </div>

      {/* Overlays */}
      <BacklogDrawer
        open={backlogOpen}
        onClose={() => setBacklogOpen(false)}
        backlog={backlog}
        sprintTickets={sprintTickets}
        onAdd={(t) => addToSprint(t)}
        dragTicket={dragTicket}
        setDragTicket={setDragTicket}
        multiSelect={multiSelect}
        setMultiSelect={setMultiSelect}
        onBulkAdd={onBulkAdd}
      />

      <Switcher
        open={switcherOpen}
        onClose={() => setSwitcherOpen(false)}
        sprint={sprint}
        project={project}
        onPickSprint={(id) => { setSprintId(id); setSwitcherOpen(false); }}
        onPickProject={(id) => { setProject(PROJECTS.find(p => p.id === id)); setSwitcherOpen(false); }}
        onNew={() => { setSwitcherOpen(false); setEditOpen(true); showToast('Creating a new sprint after this one…', false); }}
      />

      <EditPanel
        open={editOpen}
        onClose={() => setEditOpen(false)}
        sprint={sprint}
        onSave={onEditSave}
      />

      <Toast toast={toast} onUndo={undo} onDismiss={() => setToast(null)} />

      {tweaksOpen && <TweaksPanel tweaks={tweaks} setTweak={setTweak} onClose={() => { setTweaksOpen(false); try { window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*'); } catch {} }} canLock={canLock} />}
    </div>
  );
}

/* ============ TWEAKS PANEL ============ */

function TweaksPanel({ tweaks, setTweak, onClose, canLock }) {
  return (
    <div className="tweaks-panel">
      <header className="tweaks-panel__head">
        <div>
          <div className="tweaks-panel__title">Tweaks</div>
          <div className="tweaks-panel__sub">Experiment with paradigms &amp; states</div>
        </div>
        <button className="icon-btn" onClick={onClose}><Icon d={ICONS.x} size={14} /></button>
      </header>

      <div className="tweaks-panel__section">
        <div className="tweaks-panel__lbl">Planner metaphor</div>
        <div className="seg seg--vert">
          {[
            ['lanes', 'Capacity Lanes', 'Per-person track; tickets are bar segments'],
            ['twocol', 'Two-column', 'Backlog ↔ Sprint, polished'],
            ['timeline', 'Timeline', 'Gantt-like across sprint days'],
          ].map(([v, lbl, sub]) => (
            <button key={v}
              className={"tweak-radio" + (tweaks.metaphor === v ? " is-active" : "")}
              onClick={() => setTweak('metaphor', v)}>
              <span className="tweak-radio__dot" />
              <span>
                <div className="tweak-radio__lbl">{lbl}</div>
                <div className="tweak-radio__sub">{sub}</div>
              </span>
            </button>
          ))}
        </div>
      </div>

      <div className="tweaks-panel__section">
        <div className="tweaks-panel__lbl">Capacity scenario</div>
        <div className="seg">
          {[['healthy', 'Healthy'], ['over', 'Over-allocated'], ['under', 'Under-loaded']].map(([v, lbl]) => (
            <button key={v}
              className={"seg__item" + (tweaks.scenario === v ? " is-active" : "")}
              aria-pressed={tweaks.scenario === v}
              onClick={() => setTweak('scenario', v)}>{lbl}</button>
          ))}
        </div>
      </div>

      <div className="tweaks-panel__section">
        <div className="tweaks-panel__lbl">Sprint state</div>
        <div className="seg seg--wrap">
          {[['planning', 'Planning'], ['locked', 'Locked'], ['closing', 'Closing soon'], ['closed', 'Closed']].map(([v, lbl]) => (
            <button key={v}
              className={"seg__item" + (tweaks.sprintState === v ? " is-active" : "")}
              aria-pressed={tweaks.sprintState === v}
              onClick={() => setTweak('sprintState', v)}>{lbl}</button>
          ))}
        </div>
        {!canLock && tweaks.sprintState === 'planning' && (
          <div className="tweak-hint">Lock button is disabled because someone is over capacity. Switch to "Healthy" scenario to unblock.</div>
        )}
      </div>

      <div className="tweaks-panel__section">
        <div className="tweaks-panel__lbl">Theme</div>
        <div className="seg">
          {[['light', 'Light'], ['dark', 'Dark']].map(([v, lbl]) => (
            <button key={v}
              className={"seg__item" + (tweaks.theme === v ? " is-active" : "")}
              aria-pressed={tweaks.theme === v}
              onClick={() => setTweak('theme', v)}>{lbl}</button>
          ))}
        </div>
      </div>

      <footer className="tweaks-panel__foot">
        <a href="pms2-sprint-close.html" className="tweaks-panel__link">
          → See the close-sprint review screen
        </a>
        <a href="pms2-sprint v1.html" className="tweaks-panel__link">
          → Compare with the original v1
        </a>
      </footer>
    </div>
  );
}

window.App = App;

ReactDOM.createRoot(document.getElementById('sp-root')).render(<App />);
