// Detail-level filter for /tools/verfahrensablauf (m/paliad#149 Phase 2 P3).
//
// m's framing (2026-05-27 14:40, design §2.4a + §3.3a):
//
//   "It is more that I want a grade of detail in our swimlane display.
//    I want to show them but also be able to 'focus' by not displaying
//    optional things. We need an option 'show only selected' or
//    'mandatory' ... filter events from the timeline based on whether
//    they are selected in this scenario."
//
// Three modes:
//   - mandatory_only — render only priority='mandatory' rules
//   - selected (default) — mandatory + every rule whose effective
//                          selection (priority-default OR scenario-flag
//                          override) is true. Honest summary of "the
//                          lawyer's scenario".
//   - all_options — render everything, with unselected optionals
//                   rendered dotted-border + muted so the user sees
//                   what they're NOT considering.
//
// Selection model (per design §2.4a):
//   - priority='mandatory'   → always selected (cannot be deselected)
//   - priority='recommended' → default-selected; rule:<uuid>=false in
//                              scenario_flags deselects
//   - priority='optional'    → default-unselected; rule:<uuid>=true in
//                              scenario_flags selects
//   - conditional rules      → respect their condition_expr first; if
//                              the predicate doesn't hold, they're
//                              effectively unselected regardless of
//                              their priority default

import { type CalculatedDeadline } from "./views/verfahrensablauf-core";

export type DetailMode = "mandatory_only" | "selected" | "all_options";

const STORAGE_KEY = "verfahrensablauf:view_mode";
const DEFAULT_MODE: DetailMode = "selected";

export function getDetailMode(): DetailMode {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw === "mandatory_only" || raw === "selected" || raw === "all_options") {
      return raw;
    }
  } catch {
    // localStorage unavailable (private mode, security policy) — fall
    // through to default. Render still works; just no persistence.
  }
  return DEFAULT_MODE;
}

export function setDetailMode(mode: DetailMode): void {
  try {
    localStorage.setItem(STORAGE_KEY, mode);
  } catch {
    // best-effort
  }
}

// isRuleSelected: combine priority default with the scenario-flag
// override map. Returns the effective selection state.
//
//   priority='mandatory'  → always true
//   priority='recommended' → default true, flipped by rule:<uuid>=false
//   priority='optional'    → default false, flipped by rule:<uuid>=true
//   other (informational)  → treated as optional
export function isRuleSelected(
  dl: CalculatedDeadline,
  scenarioFlags: Record<string, boolean> | null,
): boolean {
  // A conditional rule that the engine left unprojected (no concrete
  // date because its predicate doesn't hold) is effectively unselected
  // in "selected" view mode — even for priority='mandatory' rules,
  // because mandatory means "must be filed IF the predicate fires",
  // not "always render". Surfacing a non-applicable conditional row in
  // "Gewählt" would be a lie. The "all_options" view re-surfaces it via
  // the unfiltered render path so the lawyer can see what scenarios
  // would unlock it.
  if (dl.isConditional) return false;

  if (dl.priority === "mandatory") return true;

  const key = dl.ruleId ? `rule:${dl.ruleId}` : null;
  const override = key && scenarioFlags ? scenarioFlags[key] : undefined;
  if (typeof override === "boolean") return override;

  return dl.priority === "recommended";
}

// filterByDetailMode applies the three-way filter to a deadlines list.
// Returns a NEW array with the appropriate subset; the caller passes
// the filtered list to the existing renderColumnsBody / renderTimelineBody.
//
// all_options: returns the input as-is, with an `__detailUnselected`
// flag set on optionals/conditionals that aren't part of the active
// scenario — the renderer reads this flag to add the dotted-border
// muted styling.
export function filterByDetailMode(
  deadlines: CalculatedDeadline[],
  mode: DetailMode,
  scenarioFlags: Record<string, boolean> | null,
): CalculatedDeadline[] {
  if (mode === "all_options") {
    // No filtering, but tag the unselected rows so the renderer can
    // dim them. The original CalculatedDeadline doesn't carry this
    // axis — we stamp it via a cast so the renderer can pick it up
    // without growing the public type. Read-only at the renderer side.
    return deadlines.map((dl) => {
      const unselected = !isRuleSelected(dl, scenarioFlags) && !dl.isRootEvent;
      return unselected
        ? ({ ...dl, __detailUnselected: true } as CalculatedDeadline & { __detailUnselected: true })
        : dl;
    });
  }
  if (mode === "mandatory_only") {
    return deadlines.filter(
      (dl) => dl.priority === "mandatory" || dl.isRootEvent,
    );
  }
  // "selected": mandatory always, plus rules whose effective selection
  // is true. Root events always render (they're the proceeding anchor).
  return deadlines.filter(
    (dl) => dl.isRootEvent || isRuleSelected(dl, scenarioFlags),
  );
}
