// Today view — the dominant surface.
// Shows committed tasks for today (if any) and a chooser of 2–3 atomic
// options across tracks so the user can pick what fits the day, rather
// than being pushed down a single path.

const { useState, useEffect, useRef, useMemo } = React;

function TodayView({ tasks, journals, today, onUpdateTask, onAddTask,
                     onAddJournal, onUpdateJournal, onDeleteJournal,
                     onNavigate, tweaks }) {
  const U = window.CAIRN_UTIL;
  const C = window.CAIRN;
  const todayTasks = U.tasksOn(tasks, today);
  const phase = U.phaseAt(today);

  // Track which task just got marked done so we can animate the accent retirement.
  const [recentlyDone, setRecentlyDone] = useState(null);
  // Briefly highlight a just-picked task as it joins today's stack.
  const [justPicked, setJustPicked] = useState(null);

  const handleDone = (taskId) => {
    setRecentlyDone(taskId);
    setTimeout(() => {
      onUpdateTask(taskId, {
        status: "done",
        completedAt: U.nowLocalISOMinute(),
      });
      setRecentlyDone(null);
    }, 200);
  };

  const handleSkip = (taskId) => onUpdateTask(taskId, { status: "skipped" });
  const handleReturn = (taskId) => onUpdateTask(taskId, { scheduledDate: null });

  // ── Day budget ──
  const dow = U.parseISO(today).getDay();
  const isWeekend = dow === 0 || dow === 6;
  const target = isWeekend ? C.SETTINGS.weekendBlockMinutes : C.SETTINGS.weekdayBlockMinutes;
  const plannedToday = todayTasks.filter(t => t.status === "planned");
  const doneToday = todayTasks.filter(t => t.status === "done");
  const committedMin = U.sumMinutes(plannedToday) + U.sumMinutes(doneToday);
  const remainingMin = Math.max(0, target - committedMin);

  // ── Build a curated set of 2–3 atomic candidates ──
  // Pool: backlog tasks (no date) + tasks scheduled in the next two weeks
  // (with the user's permission, we'll just promote those forward).
  const horizonISO = U.isoDate(U.addDays(U.parseISO(today), 14));
  const inToday = (id) => todayTasks.some(t => t.id === id);
  const pool = tasks.filter(t =>
    t.status === "planned" &&
    !inToday(t.id) &&
    (t.scheduledDate === null ||
     (t.scheduledDate > today && t.scheduledDate <= horizonISO))
  );

  // Diversify by track — round-robin across track buckets.
  const byTrack = {};
  pool.forEach(t => {
    if (!byTrack[t.track]) byTrack[t.track] = [];
    byTrack[t.track].push(t);
  });
  // Prefer tracks not already in today's stack.
  const tracksInToday = new Set(todayTasks.map(t => t.track));
  const trackOrder = Object.keys(byTrack).sort((a, b) => {
    const aIn = tracksInToday.has(a) ? 1 : 0;
    const bIn = tracksInToday.has(b) ? 1 : 0;
    if (aIn !== bIn) return aIn - bIn;
    // Stable order by track id for determinism
    return a.localeCompare(b);
  });

  const OPTION_COUNT = tweaks.optionCount || 3;
  const options = [];
  let safety = 0;
  while (options.length < OPTION_COUNT && safety++ < 30) {
    let added = false;
    for (const tr of trackOrder) {
      if (byTrack[tr].length > 0 && options.length < OPTION_COUNT) {
        options.push(byTrack[tr].shift());
        added = true;
      }
    }
    if (!added) break;
  }

  const handlePick = (taskId) => {
    onUpdateTask(taskId, { scheduledDate: today });
    setJustPicked(taskId);
    setTimeout(() => setJustPicked(prev => (prev === taskId ? null : prev)), 700);
  };

  const allTodayResolved = todayTasks.length > 0 &&
    todayTasks.every(t => t.status === "done" || t.status === "skipped");

  return (
    <div className="shell shell--narrow today">
      {/* Mono metadata header */}
      <div className="today__meta">
        <span className="meta">{U.formatLongDate(today).toUpperCase()}</span>
        <span className="today__meta-sep" aria-hidden="true">·</span>
        <span className="meta today__meta-phase">
          {phase ? phase.name.toUpperCase() : "NO PHASE"}
        </span>
      </div>

      {/* Today's committed task(s) */}
      {todayTasks.length > 0 && (
        <div className="today__stack">
          {todayTasks.map((task, idx) => {
            // Accent appears on exactly one element at a time:
            // the first planned task in the stack.
            const firstPlannedIdx = todayTasks.findIndex(t => t.status === "planned");
            const showAccent = task.status === "planned" && idx === firstPlannedIdx;
            return (
              <TaskCard
                key={task.id}
                task={task}
                journals={journals}
                isPrimary={idx === 0}
                showAccent={showAccent}
                recentlyDone={recentlyDone === task.id}
                justPicked={justPicked === task.id}
                onDone={() => handleDone(task.id)}
                onSkip={() => handleSkip(task.id)}
                onReturn={() => handleReturn(task.id)}
                onUpdate={(patch) => onUpdateTask(task.id, patch)}
                onAddJournal={onAddJournal}
                onUpdateJournal={onUpdateJournal}
                onDeleteJournal={onDeleteJournal}
                tweaks={tweaks}
                hasDivider={idx > 0}
              />
            );
          })}
        </div>
      )}

      {/* The chooser — always available as long as there are options to pick. */}
      {options.length > 0 && !allTodayResolved && (
        <ChooserSection
          options={options}
          tweaks={tweaks}
          remainingMin={remainingMin}
          target={target}
          committedMin={committedMin}
          hasCommitted={todayTasks.length > 0}
          onPick={handlePick}
          onSeeMore={() => onNavigate("backlog")}
        />
      )}

      {/* End-of-day quiet state */}
      {allTodayResolved && (
        <div className="today__resolved">
          <p className="meta today__resolved-label">Done for today.</p>
          {options.length > 0 && (
            <button
              className="today__resolved-more"
              onClick={() => handlePick(options[0].id)}
            >
              Pull one more →
            </button>
          )}
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────

function ChooserSection({ options, tweaks, remainingMin, target, committedMin,
                          hasCommitted, onPick, onSeeMore }) {
  const U = window.CAIRN_UTIL;
  const targetWords = U.approxHours(target);

  // Calm budget phrasing
  let budgetLine;
  if (!hasCommitted) {
    budgetLine = `${targetWords} to fill`;
  } else if (committedMin < target) {
    budgetLine = `${remainingMin} min of ${targetWords} remaining`;
  } else if (committedMin === target) {
    budgetLine = `${targetWords} committed`;
  } else {
    // Over target — calm acknowledgement, no scolding
    const over = committedMin - target;
    budgetLine = `${U.approxHours(committedMin)} committed · ${over} min over`;
  }

  return (
    <section className={`chooser ${hasCommitted ? "chooser--secondary" : "chooser--primary"}`}>
      <header className="chooser__head">
        <span className="meta">{hasCommitted ? "More for today" : "For today"}</span>
        <span className="meta chooser__budget">{budgetLine.toUpperCase()}</span>
      </header>

      <ul className="chooser__list">
        {options.map((opt) => (
          <ChooserOption
            key={opt.id}
            task={opt}
            tweaks={tweaks}
            onPick={() => onPick(opt.id)}
          />
        ))}
      </ul>

      <button className="chooser__more" onClick={onSeeMore}>
        See all in backlog →
      </button>
    </section>
  );
}

function ChooserOption({ task, tweaks, onPick }) {
  const U = window.CAIRN_UTIL;
  const tr = U.track(task.track);
  const related = U.relatedRefs(task);
  const ordLabel = U.ordinalLabel(task);
  return (
    <li className="opt" style={{ "--track": tr.color }}>
      <span className={"ord opt__ord" + (ordLabel ? "" : " ord--none")}>{ordLabel || "—"}</span>
      <div className="opt__body">
        <span className="opt__mark" />
        <h3 className="opt__title text-pretty">{task.title}</h3>
        <p className="opt__deliverable text-pretty">{task.deliverable}</p>
        {task.refs && task.refs.length > 0 && (
          <div className="opt__refs">
            {window.Refs && React.createElement(window.Refs, { refs: task.refs, max: 2 })}
          </div>
        )}
        {related.length > 0 && (
          <div className="opt__relates">
            {window.Refs && React.createElement(window.Refs, { refs: related, max: 3, label: "↳ Refers to" })}
          </div>
        )}
        <div className="opt__meta">
          <span className="meta-sm opt__est">{U.formatHM(task.estimatedMinutes)}</span>
          <span className="opt__meta-dot" aria-hidden="true">·</span>
          <span className="meta-sm opt__track">{tr.label}</span>
        </div>
      </div>
      <div className="opt__actions">
        <button className="btn btn--quiet opt__pick" onClick={onPick}>
          Pick
        </button>
      </div>
    </li>
  );
}

// ─────────────────────────────────────────────────────────────────────────────

function TaskCard({ task, journals = [], isPrimary, showAccent, recentlyDone, justPicked,
                    onDone, onSkip, onReturn, onUpdate,
                    onAddJournal, onUpdateJournal, onDeleteJournal,
                    tweaks, hasDivider }) {
  const U = window.CAIRN_UTIL;
  const CJ = window.CAIRN_JOURNAL;
  const tr = U.track(task.track);
  const related = U.relatedRefs(task);
  const ordLabel = U.ordinalLabel(task);
  const isDone = task.status === "done";
  const isSkipped = task.status === "skipped";
  const isPlanned = task.status === "planned";

  // Inline editing
  const [editing, setEditing] = useState(null);
  const [draft, setDraft] = useState("");

  // Journal note
  const noteEntries = CJ ? CJ.entriesForTask(journals, task.id) : [];
  const latestNote = noteEntries[noteEntries.length - 1] || null;
  const noteCount = noteEntries.length;
  const [openNoteId, setOpenNoteId] = useState(null);
  const [pendingCloseId, setPendingCloseId] = useState(null);
  const openEntry = openNoteId
    ? (journals.find(j => j.id === openNoteId) || null)
    : null;

  // Blank-cleanup runs after editor's onBlur has flushed to journals.
  useEffect(() => {
    if (!pendingCloseId) return;
    const current = journals.find(j => j.id === pendingCloseId);
    if (current && CJ && CJ.entryIsBlank(current, task)) {
      onDeleteJournal(pendingCloseId);
    }
    setPendingCloseId(null);
  }, [pendingCloseId, journals]);

  const handleNoteClick = () => {
    if (openNoteId) {
      setPendingCloseId(openNoteId);
      setOpenNoteId(null);
      return;
    }
    if (latestNote) {
      setOpenNoteId(latestNote.id);
      return;
    }
    const seed = CJ ? CJ.seedFromTask(task) : { title: "", body: "" };
    const id = onAddJournal({
      taskId: task.id,
      linkedTitleSnapshot: task.title,
      title: seed.title || "",
      body: "",
    });
    setOpenNoteId(id);
  };

  const handleNoteSave = ({ title, body }) => {
    if (!openNoteId) return;
    onUpdateJournal(openNoteId, { title, body });
  };

  const handleNoteClose = () => {
    if (openNoteId) setPendingCloseId(openNoteId);
    setOpenNoteId(null);
  };

  const handleNoteDelete = (id) => {
    onDeleteJournal(id);
    setOpenNoteId(null);
  };

  const noteSeedFocus = CJ ? CJ.seedFromTask(task).autoFocus : "title";
  const editorAutoFocus = openEntry && (openEntry.title || openEntry.body)
    ? "body"
    : noteSeedFocus;

  const startEdit = (field) => {
    if (!isPlanned) return;
    setEditing(field);
    setDraft(field === "estimate" ? String(task.estimatedMinutes) : task[field]);
  };
  const commitEdit = () => {
    if (editing === "estimate") {
      const n = parseInt(draft, 10);
      if (!isNaN(n) && n > 0) onUpdate({ estimatedMinutes: n });
    } else if (editing) {
      onUpdate({ [editing]: draft });
    }
    setEditing(null);
  };

  return (
    <div
      className={[
        "card",
        isPrimary ? "card--primary" : "card--secondary",
        showAccent ? "card--accent" : "",
        isDone ? "card--done" : "",
        isSkipped ? "card--skipped" : "",
        recentlyDone ? "card--retiring" : "",
        justPicked ? "card--just-picked" : "",
        hasDivider ? "card--divided" : "",
        tweaks.markStyle === "rule" ? "card--track-rule" : "",
      ].join(" ")}
      style={{ "--track": tr.color }}
    >
      {showAccent && <div className="card__accent" />}

      <div className="card__title-wrap">
        <span className="card__topline">
          <span className={"ord card__ord" + (ordLabel ? "" : " ord--none")}>{ordLabel || "—"}</span>
          {tweaks.markStyle === "mark" && (
            <span className="card__mark-inline" aria-label={tr.label} style={{ background: tr.color }} />
          )}
        </span>
        {editing === "title" ? (
          <input
            autoFocus
            className="card__title-input"
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            onBlur={commitEdit}
            onKeyDown={(e) => {
              if (e.key === "Enter") commitEdit();
              if (e.key === "Escape") setEditing(null);
            }}
          />
        ) : (
          <h1
            className="card__title display text-pretty"
            onClick={() => startEdit("title")}
            title={isPlanned ? "Click to edit" : ""}
          >
            {task.title}
          </h1>
        )}
      </div>

      {editing === "deliverable" ? (
        <textarea
          autoFocus
          className="card__deliverable-input"
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onBlur={commitEdit}
          onKeyDown={(e) => {
            if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commitEdit();
            if (e.key === "Escape") setEditing(null);
          }}
        />
      ) : (
        <p
          className="card__deliverable text-pretty"
          onClick={() => startEdit("deliverable")}
        >
          {task.deliverable}
        </p>
      )}

      {/* References — quiet mono row, only when present */}
      {task.refs && task.refs.length > 0 && isPlanned && (
        <div className="card__refs">
          {window.Refs && React.createElement(window.Refs, { refs: task.refs })}
        </div>
      )}

      {/* Backlink — which task(s) this one is about. Shown regardless of status
          (it's pure context, useful when picked from the backlog or reviewed). */}
      {related.length > 0 && (
        <div className="card__relates">
          {window.Refs && React.createElement(window.Refs, { refs: related, label: "↳ Refers to" })}
        </div>
      )}

      {isDone && task.completedAt && (
        <p className="meta-sm card__done-line">Done · {U.formatTime(task.completedAt)}</p>
      )}
      {isSkipped && <p className="meta-sm card__done-line">Skipped</p>}

      <div className="card__foot">
        <div className="card__meta">
          {editing === "estimate" ? (
            <input
              autoFocus
              type="text"
              className="card__est-input mono"
              value={draft}
              onChange={(e) => setDraft(e.target.value.replace(/[^\d]/g, ""))}
              onBlur={commitEdit}
              onKeyDown={(e) => {
                if (e.key === "Enter") commitEdit();
                if (e.key === "Escape") setEditing(null);
              }}
            />
          ) : (
            <span
              className="meta-sm card__meta-est"
              onClick={() => startEdit("estimate")}
            >
              {U.formatHM(task.estimatedMinutes)}
            </span>
          )}
          <span className="card__meta-dot" aria-hidden="true">·</span>
          <span className="meta-sm card__meta-track">{tr.label}</span>
          {noteCount > 0 && (
            <React.Fragment>
              <span className="card__meta-dot" aria-hidden="true">·</span>
              <span className="meta-sm card__note-chip">
                {noteCount} {noteCount === 1 ? "NOTE" : "NOTES"}
              </span>
            </React.Fragment>
          )}
        </div>

        {(isPlanned || (isDone && noteCount > 0)) && (
          <div className="card__actions">
            {onAddJournal && (
              <button
                className="btn btn--ghost card__note-btn"
                aria-pressed={openNoteId ? "true" : "false"}
                onClick={handleNoteClick}
              >
                {noteCount > 0 ? `Note (${noteCount})` : "Note"}
              </button>
            )}
            {isPlanned && onReturn && (
              <button className="btn btn--ghost card__return" onClick={onReturn}>
                Return
              </button>
            )}
            {isPlanned && (
              <button className="btn btn--ghost" onClick={onSkip}>Skip</button>
            )}
            {isPlanned && (
              <button className="btn btn--primary" onClick={onDone}>Mark done</button>
            )}
          </div>
        )}
      </div>

      {openNoteId && window.JournalEditor && (
        <div className="card__note-editor">
          {React.createElement(window.JournalEditor, {
            host: "card",
            task: task,
            entry: openEntry,
            autoFocus: editorAutoFocus,
            onSave: handleNoteSave,
            onDelete: handleNoteDelete,
            onClose: handleNoteClose,
          })}
        </div>
      )}
    </div>
  );
}

window.TodayView = TodayView;
