/**
 * CATALOGUE (Bekky, 2026-08-31, stitches 5–7): the per-plant cohort
 * life-history ledger — ONE ledger-preserving section of the plant's JSON that
 * remembers its group life: every cohort it has belonged to (past + present),
 * when it joined/left, how far its own rhythm deviated from the group's, the
 * AI bridge plan it joined under, and — when it leaves — the AI's one-line
 * reason why.
 *
 * DESIGN RULINGS (Bekky, 2026-08-31):
 *  - Suitcase rule: history RIDES ON THE PLANT, not the group — a plant that
 *    exits any group takes its calendar with it automatically.
 *  - Ledger-preserving: membership stays are appended and never removed or
 *    reordered; an open stay is only updated once to record when/why it closed.
 *    The future CLIPPING mechanism (not this build) may deprecate older data.
 *  - Sectioned + pullable: renderCohortHistorySection (contextPacket.ts) lifts
 *    exactly this section into whichever AI call needs it — never the whole JSON.
 */

import type { GroupableCareAction } from './care';

/** One membership event in a plant's cohort life-history ledger. */
export type CohortHistoryEntry = {
  /** The cohort this entry is about (stable while the crew keeps its object). */
  cohortId: string;
  /** Human group name at the time of the event. */
  name: string;
  /** Which chore this membership was for (water/fertilize are independent). */
  action: GroupableCareAction;
  /** Local YYYY-MM-DD the plant joined this cohort. */
  joined: string;
  /** Local YYYY-MM-DD the plant left — ABSENT = still a member (open entry). */
  left?: string;
  /** How the plant's own last-<action> date compared to the crew's earliest at
   *  join time, in signed days (positive = fresher/sooner than the group).
   *  Bounded ±30. Absent = no dates on file. */
  deviationDays?: number;
  /** The AI bridge plan the plant joined under (one sentence, with dates). */
  foldPlan?: string;
  /** WHY the plant left (AI-authored when a regroup moves it, one line). */
  reasonLeft?: string;
  /** Which AI pass authored the join: grouping (placed) or care (bridge plan). */
  source: 'grouping' | 'care';
};

/** True when a membership ledger entry has not been closed. */
function isOpenCohortHistoryEntry(entry: CohortHistoryEntry): boolean {
  return typeof entry.left !== 'string' || entry.left.trim().length === 0;
}

/** Read a plant's newest open ledger entry for one cohort + action. */
export function openCohortHistoryEntry(
  plant: { cohortHistory?: CohortHistoryEntry[] },
  cohortId: string,
  action: GroupableCareAction,
): CohortHistoryEntry | undefined {
  const history = plant.cohortHistory;
  if (!Array.isArray(history)) return undefined;

  // Search newest-first so malformed legacy data with duplicate open entries
  // resolves to the most recently appended membership rather than the oldest.
  for (let index = history.length - 1; index >= 0; index -= 1) {
    const entry = history[index];
    if (
      entry
      && entry.cohortId === cohortId
      && entry.action === action
      && isOpenCohortHistoryEntry(entry)
    ) {
      return entry;
    }
  }
  return undefined;
}

/**
 * Close every matching open membership entry (pure — returns a new ledger).
 * Duplicate open entries can exist in legacy/corrupt data, so all matches are
 * closed together to restore the one-open-entry invariant.
 */
export function closeCohortHistoryEntry(
  plant: { cohortHistory?: CohortHistoryEntry[] },
  cohortId: string,
  action: GroupableCareAction,
  left: string,
  reasonLeft?: string,
): CohortHistoryEntry[] {
  const history = Array.isArray(plant.cohortHistory) ? plant.cohortHistory : [];
  const normalizedReason = typeof reasonLeft === 'string' ? reasonLeft.trim() : '';

  return history.map(entry => {
    if (
      entry.cohortId !== cohortId
      || entry.action !== action
      || !isOpenCohortHistoryEntry(entry)
    ) {
      return entry;
    }

    return {
      ...entry,
      left,
      ...(normalizedReason ? { reasonLeft: normalizedReason } : {}),
    };
  });
}

/**
 * Append one entry to a plant's ledger (pure — returns a new ledger).
 * Closes an existing open entry for the same cohort+action (re-join = close
 * old, append new) so the ledger stays tidy: one open entry per cohort+action.
 */
export function appendCohortHistoryEntry(
  plant: { cohortHistory?: CohortHistoryEntry[] },
  entry: CohortHistoryEntry,
): CohortHistoryEntry[] {
  const closedHistory = closeCohortHistoryEntry(
    plant,
    entry.cohortId,
    entry.action,
    entry.joined,
  );
  return [...closedHistory, { ...entry }];
}
