// FilterBar types — t-paliad-163. Mirrors the Go FilterSpec/RenderSpec
// shapes from internal/services/{filter_spec,render_spec}.go via
// client/views/types.ts. The FilterBar is the universal frontend
// primitive that consumes a base FilterSpec + RenderSpec, declares
// which axes the surface supports, and emits diffs back through
// onResult after running the spec via /api/views/run.

import type { FilterSpec, RenderSpec, RenderShape, ViewRunResult, ListRowAction } from "../views/types";

// AxisKey — every filter dimension the bar can render. Declared per
// surface in mountFilterBar's `axes` array. See design §3.1 for the
// universal-vs-per-surface split.
export type AxisKey =
  | "time"
  | "project"
  | "personal_only"
  | "deadline_status"
  | "deadline_event_type"
  | "appointment_type"
  | "approval_viewer_role"
  | "approval_status"
  | "approval_entity_type"
  | "project_event_kind"
  | "timeline_status"
  | "timeline_track"
  | "shape"
  | "sort"
  | "density";

// Effective spec — the result of overlaying URL + localStorage prefs
// on top of the base spec. Handed back to onResult so the surface can
// dispatch into the matching shape renderer with the right config.
export interface EffectiveSpec {
  filter: FilterSpec;
  render: RenderSpec;
}

// Per-axis state — what the URL codec round-trips. Each axis's value
// type is bounded to the FilterSpec/RenderSpec subset it touches.
export interface BarState {
  // Universal
  time?: TimeOverlay;
  project?: ProjectOverlay;
  personal_only?: boolean;

  // Per-source
  deadline_status?: string[];
  deadline_event_type?: { ids: string[]; include_untyped: boolean };
  appointment_type?: string[];
  approval_viewer_role?: "approver_eligible" | "self_requested" | "any_visible";
  approval_status?: string[];
  approval_entity_type?: string[];
  project_event_kind?: string[];
  // SmartTimeline axes (t-paliad-173). timeline_status spans actuals +
  // projections; timeline_track is parent / counterclaim / off_script
  // and grows once Slice 3 lands the CCR sub-project FK (child:<id>
  // values dynamically populated then).
  timeline_status?: string[];
  timeline_track?: string[];

  // Render
  shape?: RenderShape;
  sort?: "date_asc" | "date_desc";
  density?: "comfortable" | "compact";
}

export interface TimeOverlay {
  horizon: "next_7d" | "next_30d" | "next_90d" | "past_7d" | "past_30d" | "past_90d" | "any" | "all" | "custom";
  from?: string; // ISO 8601 — only when horizon === "custom"
  to?: string;
}

export interface ProjectOverlay {
  // The bar's project chip is single-select today; Phase C upgrades
  // to multi-select. "personal" is a sentinel — the legacy /events
  // contract reserved this name, we keep it so old bookmarks still
  // resolve to the right state.
  mode: "single" | "personal";
  id?: string;
}

// MountOpts — the public API.
export interface MountOpts {
  // Base spec. Usually a SystemView's FilterSpec+RenderSpec, fetched
  // from /api/views/system on the surface and passed in here. For
  // /views/{slug}, the saved user-view's spec.
  baseFilter: FilterSpec;
  baseRender: RenderSpec;

  // Which axes the surface exposes. Order is preserved in the rendered
  // chrome — surfaces use this to control left-to-right grouping.
  axes: AxisKey[];

  // URL parameter namespace. When set, every URL key is prefixed
  // (`?<ns>_time=`, `?<ns>_project=`, …). Used when two bars share a
  // page (dashboard inline lists). Defaults to no prefix.
  urlNamespace?: string;

  // Surface key for localStorage prefs (density, default shape).
  // Required so two surfaces don't share preferences.
  surfaceKey: string;

  // Whether to render "Speichern als Sicht" + "Zur&uuml;cksetzen"
  // trailing actions. Defaults to true. Set false on the dashboard
  // inline bars (per design Q6).
  showSaveAsView?: boolean;

  // Slug of the surface's underlying system view (or saved user view).
  // POSTed to /api/views/{slug}/run with the override body. Required
  // unless `customRunner` is supplied — see below. When the bar runs
  // through this endpoint it is the substrate's canonical entry.
  systemViewSlug?: string;

  // Custom runner. When set, the bar bypasses the substrate POST and
  // hands the effective spec + raw BarState to this function instead.
  // Used by surfaces that need axes the EffectiveSpec doesn't round-trip
  // (e.g. SmartTimeline's timeline_status / timeline_track, t-paliad-176).
  // The state argument is a frozen snapshot — same shape getState()
  // returns on the handle, but available on the very first run before
  // the handle has been assigned. Must be either this OR systemViewSlug
  // — the bar throws if both / neither are provided.
  customRunner?: (effective: EffectiveSpec, state: Readonly<BarState>) => Promise<ViewRunResult>;

  // Per-surface override of the time-axis chip presets. Order is
  // preserved. Default presets are forward-looking (next_*+past_30d+any)
  // — backward-looking surfaces (Verlauf, audit) pass past_*+all here.
  timePresets?: NonNullable<BarState["time"]>["horizon"][];

  // When true, the bar exposes an "Aktualisieren" affordance that
  // PATCHes /api/user-views/{userViewId} with the effective spec.
  // Set on /views/{slug} where the user is viewing a saved view.
  userViewId?: string;

  // Called every time the spec changes (mount, URL change, axis
  // interaction). The surface dispatches to the matching shape
  // renderer with the rows from /api/views/{slug}/run.
  onResult(result: ViewRunResult, effective: EffectiveSpec): void;

  // Optional — surface-specific row-action override. Phase 1: /inbox
  // pins this to "approve"; /events Phase 3 pins to "complete_toggle".
  // Future: sourced from the spec's render.list.row_action when set.
  rowAction?: ListRowAction;
}

// Bar handle — what mountFilterBar returns. Pages can call .reset()
// from page-level controls (e.g. an empty-state "Filter zurücksetzen"
// button), or .destroy() if the page tears down.
export interface BarHandle {
  reset(): void;
  refresh(): Promise<void>;
  destroy(): void;
  // Read-only effective spec at this moment (post URL + localStorage
  // overlay). Pages use this to construct deep-link URLs etc.
  getEffective(): EffectiveSpec;
  // Read-only raw BarState. Surfaces with axes the EffectiveSpec doesn't
  // round-trip (timeline_status / timeline_track on the SmartTimeline
  // surface — the substrate FilterSpec has no per-source predicate for
  // those) read state directly to drive client-side filtering. Returns
  // a frozen snapshot; callers must not mutate.
  getState(): Readonly<BarState>;
}
