// Main app — wires data, state, modals, popover, drawer, tweaks together.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "zoom": 1,
  "density": "regular",
  "showPercent": true
}/*EDITMODE-END*/;

function App() {
  const D = window.CrewliData;
  const H = window.CrewliHelpers;
  const M = window.CrewliModals;
  const P = window.CrewliPopover;
  const TT = window.CrewliTimetable;

  // ─── Mutable state (local, no backend) ────────────────────────────
  const [stages, setStages] = React.useState(D.STAGES);
  const [stageDays, setStageDays] = React.useState(D.STAGE_DAYS);
  const [artists, setArtists] = React.useState(D.ARTISTS);
  const [performances, setPerformances] = React.useState(D.PERFORMANCES);
  const [parked, setParked] = React.useState(D.PARKED);
  const [pending, setPending] = React.useState(D.PENDING);
  const [activeDayId, setActiveDayId] = React.useState(D.EVENT.days[0].id);

  // ─── UI state ─────────────────────────────────────────────────────
  const [popover, setPopover] = React.useState(null); // { perf, rect } or { kind: 'queue', queueKey, item, rect }
  const [drawer, setDrawer] = React.useState(null); // { perf }
  const [perfModal, setPerfModal] = React.useState(null); // { mode, perf }
  const [stageModal, setStageModal] = React.useState(null); // stage
  const [matrixOpen, setMatrixOpen] = React.useState(false);

  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const activeDay = D.EVENT.days.find(d => d.id === activeDayId);

  // Stages active on this day
  const dayStages = React.useMemo(() => {
    const ids = new Set(stageDays.filter(sd => sd.day_id === activeDayId).map(sd => sd.stage_id));
    return stages.filter(s => ids.has(s.id));
  }, [stages, stageDays, activeDayId]);

  const dayPerformances = performances.filter(p => p.day_id === activeDayId);

  // Reducer-ish dispatch
  function dispatch(action) {
    if (action.type === "update_perf") {
      setPerformances(prev => prev.map(p => p.id === action.perf.id ? action.perf : p));
    } else if (action.type === "delete_perf") {
      setPerformances(prev => prev.filter(p => p.id !== action.id));
    } else if (action.type === "add_perf") {
      setPerformances(prev => [...prev, action.perf]);
    } else if (action.type === "update_stage") {
      setStages(prev => prev.map(s => s.id === action.stage.id ? action.stage : s));
    } else if (action.type === "delete_stage") {
      setStages(prev => prev.filter(s => s.id !== action.id));
      setStageDays(prev => prev.filter(sd => sd.stage_id !== action.id));
      // Move scheduled performances on this stage to the wachtrij (parked) i.p.v. ze te verwijderen.
      setPerformances(prev => prev.filter(p => p.stage_id !== action.id));
      setParked(prev => [
        ...prev,
        ...performances
          .filter(p => p.stage_id === action.id)
          .map(p => ({ ...p, stage_id: null, dur: (p.end - p.start) || 60 })),
      ]);
    } else if (action.type === "add_stage") {
      setStages(prev => [...prev, action.stage]);
      setStageDays(prev => [...prev, ...action.days.map(did => ({ stage_id: action.stage.id, day_id: did }))]);
    } else if (action.type === "reorder_stages") {
      // dayStageIds = the new ordering for the day's stages.
      // Strategy: keep non-day stages in their relative positions; replace the
      // dayStages slice with the new order.
      setStages(prev => {
        const dayIdSet = new Set(action.dayStageIds);
        const dayLookup = Object.fromEntries(prev.filter(s => dayIdSet.has(s.id)).map(s => [s.id, s]));
        const orderedDay = action.dayStageIds.map(id => dayLookup[id]).filter(Boolean);
        const result = [];
        let cursor = 0;
        for (const s of prev) {
          if (dayIdSet.has(s.id)) {
            // Replace with the next entry from orderedDay
            if (cursor < orderedDay.length) result.push(orderedDay[cursor++]);
          } else {
            result.push(s);
          }
        }
        return result;
      });
    } else if (action.type === "set_stage_days_for_stage") {
      setStageDays(prev => [
        ...prev.filter(sd => sd.stage_id !== action.stageId),
        ...action.dayIds.map(did => ({ stage_id: action.stageId, day_id: did })),
      ]);
    } else if (action.type === "set_stage_days_all") {
      setStageDays(action.list);
    } else if (action.type === "park_perf") {
      const p = performances.find(x => x.id === action.id);
      if (!p) return;
      setPerformances(prev => prev.filter(x => x.id !== action.id));
      setParked(prev => [...prev, { ...p, stage_id: null }]);
    } else if (action.type === "unpark_perf") {
      const p = parked.find(x => x.id === action.perf.id);
      if (!p) return;
      setParked(prev => prev.filter(x => x.id !== action.perf.id));
      setPerformances(prev => [...prev, action.perf]);
    } else if (action.type === "schedule_pending") {
      setPending(prev => prev.filter(x => x.id !== action.pendingId));
      setPerformances(prev => [...prev, action.perf]);
    } else if (action.type === "delete_pending") {
      setPending(prev => prev.filter(x => x.id !== action.id));
    } else if (action.type === "delete_parked") {
      setParked(prev => prev.filter(x => x.id !== action.id));
    } else if (action.type === "update_pending_status") {
      // Pending items don't actually carry a status — promoting their status
      // means moving them into parked (no stage_id) with that status.
      const pa = pending.find(x => x.id === action.id);
      if (!pa) return;
      const newParked = {
        id: "p_" + Math.random().toString(36).slice(2, 8),
        artist_id: pa.artist_id, day_id: pa.day_id,
        stage_id: null, start: null, end: null,
        status: action.status,
      };
      setPending(prev => prev.filter(x => x.id !== action.id));
      setParked(prev => [...prev, newParked]);
    } else if (action.type === "update_parked_status") {
      setParked(prev => prev.map(p => p.id === action.id ? { ...p, status: action.status } : p));
    } else if (action.type === "add_parked") {
      setParked(prev => [...prev, action.parked]);
    } else if (action.type === "add_artist") {
      setArtists(prev => [...prev, action.artist]);
    }
  }

  // Move parked → performances when used via drag
  React.useEffect(() => {
    const movedBack = performances.filter(p => p.stage_id !== null && parked.some(pk => pk.id === p.id));
    if (movedBack.length > 0) {
      setParked(prev => prev.filter(pk => !movedBack.some(m => m.id === pk.id)));
    }
  }, [performances]);

  // Layout numerics
  const pxPerMin = 4 * t.zoom;
  const rowHeight = t.density === "compact" ? 52 : t.density === "comfy" ? 84 : 64;

  // ─── Conflict counts (for header chip) ─────────────────────────────
  const conflictIds = H.findConflicts(dayPerformances);

  // Day counts
  const stageCountByDay = {};
  D.EVENT.days.forEach(d => {
    stageCountByDay[d.id] = stageDays.filter(sd => sd.day_id === d.id).length;
  });

  // ─── Handlers ─────────────────────────────────────────────────────
  function handleSelectPerf(perf, rect) {
    setPopover({ kind: "perf", perfId: perf.id, rect });
    setDrawer(null);
  }
  function handleSelectQueueItem(item, rect) {
    // item: { kind, id, artist, status, dur, src }
    setPopover({ kind: "queue", queueKey: item.kind + ":" + item.id, item, rect });
    setDrawer(null);
  }
  function handleClickEmpty(stage, minute, end, lane) {
    setPerfModal({
      mode: "add",
      perf: {
        stage_id: stage.id,
        start: minute,
        end: typeof end === "number" ? end : minute + 60,
        status: "concept",
        ...(Number.isInteger(lane) ? { lane } : {}),
      },
    });
    setPopover(null);
  }
  function handleEditStage(stage) { setStageModal(stage); setPopover(null); }
  function handleAddStage() {
    // Draft stage — niet meteen toevoegen aan state. StageEditor commit'et bij Opslaan.
    const id = "s_" + Math.random().toString(36).slice(2, 8);
    const draft = { id, name: "Nieuwe stage", color: "#3cc2a8", capacity: 2000, __draft: true };
    setStageModal(draft);
  }

  const popoverPerf = popover && popover.kind === "perf" ? performances.find(p => p.id === popover.perfId) : null;
  const drawerPerf  = drawer  ? performances.find(p => p.id === drawer.perfId)  : null;

  return (
    <div className="cw-app">
      <main className="cw-main">
        <Header
          dayCounts={stageCountByDay}
          conflicts={conflictIds.size}
          activeDayId={activeDayId}
          onChangeDay={setActiveDayId}
          onOpenMatrix={() => setMatrixOpen(true)}
          onAddStage={handleAddStage}
          onAddPerformance={() => setPerfModal({
            mode: "add",
            // No stage/time = lands in wachtrij
            perf: { status: "requested" }
          })}
        />

        <div className="cw-tt-wrap">
          <TT.Timetable
            dispatch={(action) => {
              if (action.type === "update_perf" && parked.some(pk => pk.id === action.perf.id) && action.perf.stage_id) {
                dispatch({ type: "unpark_perf", perf: action.perf });
              } else {
                dispatch(action);
              }
            }}
            activeDay={activeDay}
            stages={dayStages}
            performances={dayPerformances}
            parked={parked.filter(p => p.day_id === activeDayId)}
            pending={pending.filter(p => p.day_id === activeDayId)}
            artists={artists}
            onSelectPerf={handleSelectPerf}
            onClickEmptyCell={handleClickEmpty}
            onEditStage={handleEditStage}
            onScheduleFromParking={({ pending: pa, stage, minute, lane, openModal }) => {
              if (openModal) {
                setPerfModal({
                  mode: "add",
                  perf: { artist_id: pa.artist_id, stage_id: stage.id, start: minute, end: minute + 60, status: "requested", lane },
                  pendingId: pa.id,
                });
              } else {
                const newPerf = {
                  id: "p_" + Math.random().toString(36).slice(2, 8),
                  artist_id: pa.artist_id, stage_id: stage.id, day_id: pa.day_id,
                  start: minute, end: minute + 60, status: "requested",
                  ...(Number.isInteger(lane) ? { lane } : {}),
                };
                dispatch({ type: "schedule_pending", pendingId: pa.id, perf: newPerf });
              }
            }}
            pxPerMin={pxPerMin}
            baseRowHeight={rowHeight}
            density={t.density}
            showPercent={t.showPercent}
            onSelectQueueItem={handleSelectQueueItem}
            selectedQueueId={popover && popover.kind === "queue" ? popover.queueKey : null}
          />
        </div>

        <FooterToolbar
          stagesActive={dayStages.length}
          performances={dayPerformances.length}
          conflicts={conflictIds.size}
        />
      </main>

      {/* Popover — performance */}
      {popover && popover.kind === "perf" && popoverPerf && (() => {
        const stage = stages.find(s => s.id === popoverPerf.stage_id);
        const artist = artists.find(a => a.id === popoverPerf.artist_id);
        return (
          <P.Popover
            perf={popoverPerf} artist={artist} stage={stage}
            anchorRect={popover.rect} sections={D.ADVANCE_SECTIONS}
            onChangeStatus={(k) => dispatch({ type: "update_perf", perf: { ...popoverPerf, status: k }})}
            onOpenDetailPage={(a, p) => alert(`Navigatie naar /events/${D.EVENT.id || "ezf_2026"}/artists/${a.id} (out of scope voor deze PoC)`)}
            onClose={() => setPopover(null)}
          />
        );
      })()}

      {/* Popover — queue item (read-only summary, status switch, navigate to detail) */}
      {popover && popover.kind === "queue" && (() => {
        const it = popover.item;
        const artist = artists.find(a => a.id === it.artist.id);
        return (
          <P.QueuePopover
            item={it} artist={artist}
            anchorRect={popover.rect} sections={D.ADVANCE_SECTIONS}
            onChangeStatus={(k) => {
              if (it.kind === "pending") dispatch({ type: "update_pending_status", id: it.id, status: k });
              else dispatch({ type: "update_parked_status", id: it.id, status: k });
            }}
            onOpenDetailPage={(a) => alert(`Navigatie naar /events/${D.EVENT.id || "ezf_2026"}/artists/${a.id} (out of scope voor deze PoC)`)}
            onClose={() => setPopover(null)}
          />
        );
      })()}

      {/* Drawer is no longer used — detail navigation is handled via Open detailpagina */}

      {/* Modals */}
      {perfModal && (
        <M.PerformanceModal
          mode={perfModal.mode} perf={perfModal.perf}
          day={activeDay} stages={dayStages.length ? dayStages : stages}
          artists={artists}
          onSave={({ newArtist, perf }) => {
            if (perfModal.mode === "add") {
              if (newArtist) dispatch({ type: "add_artist", artist: newArtist });
              if (perf.stage_id) {
                dispatch({ type: "add_perf", perf });
              } else {
                dispatch({ type: "add_parked", parked: { ...perf, dur: perf.dur || 60 } });
              }
            } else {
              dispatch({ type: "update_perf", perf });
            }
            setPerfModal(null);
          }}
          onDelete={(id) => { dispatch({ type: "delete_perf", id }); setPerfModal(null); }}
          onClose={() => setPerfModal(null)}
        />
      )}
      {stageModal && (
        <M.StageEditor
          stage={stageModal} days={D.EVENT.days} stageDays={stageDays}
          mode={stageModal.__draft ? "create" : "edit"}
          defaultDayIds={stageModal.__draft ? [activeDayId] : null}
          onSave={(s, dayIds) => {
            const { __draft, ...clean } = s;
            if (stageModal.__draft) {
              dispatch({ type: "add_stage", stage: clean, days: dayIds });
            } else {
              dispatch({ type: "update_stage", stage: clean });
              dispatch({ type: "set_stage_days_for_stage", stageId: clean.id, dayIds });
            }
            setStageModal(null);
          }}
          onDelete={(id) => {
            const affected = performances.filter(p => p.stage_id === id).length;
            const stageName = stages.find(s => s.id === id)?.name || "deze stage";
            const msg = affected > 0
              ? `Weet je zeker dat je "${stageName}" wilt verwijderen?\n\n${affected} ingeplande act${affected === 1 ? "" : "s"} ${affected === 1 ? "wordt" : "worden"} naar de wachtrij verplaatst (niet verwijderd).`
              : `Weet je zeker dat je "${stageName}" wilt verwijderen?`;
            if (!window.confirm(msg)) return;
            dispatch({ type: "delete_stage", id });
            setStageModal(null);
          }}
          onClose={() => setStageModal(null)}
        />
      )}
      {matrixOpen && (
        <M.LineupMatrix
          stages={stages} days={D.EVENT.days} stageDays={stageDays}
          onSave={(list) => { dispatch({ type: "set_stage_days_all", list }); setMatrixOpen(false); }}
          onClose={() => setMatrixOpen(false)}
        />
      )}

      {/* Tweaks */}
      <TweaksPanel>
        <TweakSection label="Weergave" />
        <TweakRadio label="Dichtheid" value={t.density}
          options={["compact", "regular", "comfy"]}
          onChange={(v) => setTweak("density", v)} />
        <TweakSlider label="Zoom" value={t.zoom} min={0.5} max={2.5} step={0.1} unit="×"
          onChange={(v) => setTweak("zoom", v)} />
        <TweakSection label="Block details" />
        <TweakToggle label="Advancing %" value={t.showPercent} onChange={(v) => setTweak("showPercent", v)} />
      </TweaksPanel>
    </div>
  );
}

// minimal inline icons
function Icon({ name }) {
  const common = { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round" };
  const paths = {
    home:  <path d="M2 7 L8 2 L14 7 V13 H10 V9 H6 V13 H2 Z" />,
    cal:   <g><rect x="2.5" y="3.5" width="11" height="9.5" rx="1.5"/><path d="M2.5 6.5 H13.5"/><path d="M5 2 V5 M11 2 V5"/></g>,
    build: <g><rect x="3" y="2.5" width="10" height="11" rx="1"/><path d="M6 5 H7 M9 5 H10 M6 8 H7 M9 8 H10 M6 11 H7 M9 11 H10"/></g>,
    users: <g><circle cx="8" cy="6" r="2.2"/><path d="M3.5 13 C 3.5 10 6 9 8 9 C 10 9 12.5 10 12.5 13"/></g>,
    biz:   <g><rect x="2.5" y="4.5" width="11" height="9" rx="1"/><path d="M6 4.5 V3 H10 V4.5"/><path d="M2.5 8.5 H13.5"/></g>,
    alert: <g><path d="M8 2 L14 13 L2 13 Z"/><path d="M8 7 V10 M8 11.5 V11.7"/></g>,
    cog:   <g><circle cx="8" cy="8" r="2"/><path d="M8 2 V4 M8 12 V14 M2 8 H4 M12 8 H14 M3.8 3.8 L5.2 5.2 M10.8 10.8 L12.2 12.2 M3.8 12.2 L5.2 10.8 M10.8 5.2 L12.2 3.8"/></g>,
    plus:  <g><path d="M8 3 V13 M3 8 H13"/></g>,
    edit:  <g><path d="M2.5 13.5 V11 L11 2.5 L13.5 5 L5 13.5 Z"/></g>,
    grid:  <g><rect x="2.5" y="2.5" width="4" height="4"/><rect x="9.5" y="2.5" width="4" height="4"/><rect x="2.5" y="9.5" width="4" height="4"/><rect x="9.5" y="9.5" width="4" height="4"/></g>,
    bell:  <g><path d="M3.5 11 H12.5 C 11 10 11 5.5 8 5.5 C 5 5.5 5 10 3.5 11 Z"/><path d="M6.5 12.5 C 7 13.5 9 13.5 9.5 12.5"/></g>,
    sun:   <g><circle cx="8" cy="8" r="2.5"/><path d="M8 2 V3.5 M8 12.5 V14 M2 8 H3.5 M12.5 8 H14 M3.5 3.5 L4.5 4.5 M11.5 11.5 L12.5 12.5 M3.5 12.5 L4.5 11.5 M11.5 4.5 L12.5 3.5"/></g>,
    search:<g><circle cx="7" cy="7" r="4"/><path d="M10 10 L13.5 13.5"/></g>,
    arrow: <g><path d="M9 3 L4 8 L9 13"/></g>,
    share: <g><circle cx="4" cy="8" r="1.5"/><circle cx="12" cy="4" r="1.5"/><circle cx="12" cy="12" r="1.5"/><path d="M5.3 7.2 L10.7 4.7 M5.3 8.8 L10.7 11.3"/></g>,
    chev:  <g><path d="M5 6.5 L8 9.5 L11 6.5"/></g>,
  };
  return <svg {...common}>{paths[name] || null}</svg>;
}

// ─── Header ─────────────────────────────────────────────────────────
function Header({ dayCounts, conflicts, activeDayId, onChangeDay, onOpenMatrix, onAddPerformance, onAddStage }) {
  const D = window.CrewliData;
  return (
    <div className="cw-hdr">
      <div className="cw-hdr-bar">
        <div className="cw-day-tabs" role="tablist">
          {D.EVENT.days.map(d => (
            <button key={d.id}
              role="tab"
              className={"cw-day-tab" + (d.id === activeDayId ? " is-active" : "")}
              onClick={() => onChangeDay(d.id)}>
              <span className="cw-day-tab-day">{d.label}</span>
              <span className="cw-day-tab-meta">{dayCounts[d.id] || 0} stages</span>
            </button>
          ))}
        </div>

        <div className="cw-hdr-meta">
          {conflicts > 0 && (
            <span className="cw-pill cw-pill-warn">
              <span className="cw-pill-dot" style={{ background: "#d63d4b" }}></span>
              {conflicts} {conflicts === 1 ? "conflict" : "conflicten"}
            </span>
          )}

        </div>

        <div style={{ flex: 1 }}></div>

        <button className="cw-btn cw-btn-soft" onClick={onAddPerformance}>
          <Icon name="plus" /> Performance toevoegen
        </button>
        <button className="cw-btn cw-btn-soft" onClick={onAddStage}>
          <Icon name="plus" /> Stage toevoegen
        </button>
        <button className="cw-btn cw-btn-soft" onClick={onOpenMatrix}>
          <Icon name="grid" /> Stages per {D.EVENT.sub_event_label || "dag"}
        </button>
      </div>
    </div>
  );
}

function FooterToolbar({ stagesActive, performances, conflicts }) {
  return (
    <div className="cw-foot">
      <div className="cw-foot-stats">
        <span><b>{stagesActive}</b> stages</span>
        <span><b>{performances}</b> performances</span>
        <span><b>{conflicts}</b> conflicten</span>
      </div>
      <div style={{ flex: 1 }}></div>
    </div>
  );
}

// Mount
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
