// Public data types for @loomcycle/explorer. These are the shapes the Path /
// Document components render and the data layer produces. They mirror the
// loomcycle Web UI's api.ts (the same off-run /v1/_path + /v1/_document endpoint
// JSON) but live here so the package has no dependency on the app's global api
// module — the runtime is reached through an injected LoomcycleClient /
// ExplorerDataLayer instead (see lib/dataLayer).

// PathScope selects WHICH dirent tree to browse. It is a subtree SELECTOR, not
// an authority grant — the runtime resolves the caller's tenant + subject from
// the authenticated principal and re-authorizes.
//
// DocScope was `agent | user` because Documents once REFUSED tenant scope, and it
// stayed that way after the runtime gained it (loomcycle v1.41.0). The cost was not
// a type error: PathExplorer had to narrow PathScope down to DocScope to hand it to
// the viewer, `"tenant"` had nowhere to go, and it collapsed to `"user"` — so every
// tenant-scope document LISTED in the Path tree and then opened with no chunks,
// because the viewer was asking the user scope for them. The two are the same set
// now, and deliberately spelled out rather than aliased: they answer different
// questions (which tree to browse vs which store a document lives in) and could
// diverge again.
export type PathScope = "agent" | "user" | "tenant";
export type DocScope = "agent" | "user" | "tenant";

// BrowseScope is the RFC AS off-run browse-by-subject override: which subject's
// tree (scopeId → ?scope_id=) and, for an admin, which tenant (tenant →
// ?tenant=). CALLER-AUTHORITATIVE only in that the SERVER re-checks it — a
// substrate:tenant principal's tenant is ignored (confined to its own tenant);
// scopeId picks any subject it may see. Unset fields are omitted, so the server
// falls back to the caller's own subject.
export interface BrowseScope {
  scopeId?: string;
  tenant?: string;
}

// PathEntry is one dirent in an `ls` listing.
export interface PathEntry {
  name: string;
  kind: string; // directory | document | volume_mount | memory_entry
  full_path: string;
  resource_ref?: unknown;
}

// ChunkRow is the structural record returned by query_chunks (no body — kept
// light; fetch the body with ExplorerDataLayer.documentGetChunk).
export interface ChunkRow {
  id: string;
  document_id: string;
  parent_id?: string;
  position: number;
  title: string;
  type?: string;
  status?: string;
  revision: number;
}

// ChunkDetail adds the Markdown body + typed fields (from get_chunk).
export interface ChunkDetail extends ChunkRow {
  body: string;
  fields?: unknown;
  // tags (RFC BS) — the chunk's tag set, returned by get_chunk (omitted when the
  // chunk has none). Nested tags use a slash (area/sub/topic).
  tags?: string[];
  // asset (RFC BO) is present on an image chunk — metadata only; the bytes are
  // fetched from GET /v1/_document/asset/{id} via documentAssetObjectUrl.
  asset?: { media_type: string; size: number };
}

// DocEdge is one cross-reference edge from get_edges (RFC BN P4): a typed link
// between two chunks (possibly in different documents), enriched with each
// endpoint's title/type/status/document_id so the viewer can render a References
// list + a relationship graph without a per-endpoint get_chunk. The endpoint
// fields are omitted when empty (a dangling endpoint, or a chunk with no
// type/status).
export interface DocEdge {
  from_id: string;
  to_id: string;
  kind: string;
  from_title?: string;
  from_type?: string;
  from_status?: string;
  from_document_id?: string;
  to_title?: string;
  to_type?: string;
  to_status?: string;
  to_document_id?: string;
}

// Backlink is one incoming link to the selected chunk, from `backlinks` (RFC BS):
// the linking (source) chunk enriched with its title/type/status/document_id, the
// edge `kind`, and `auto` — true when the link came from a `[[name]]` wikilink in
// the source body (vs a manually-authored edge). from_document_id ≠ the current
// document marks a cross-document backlink (labeled, not navigable in this
// single-document viewer). Endpoint fields are omitted when empty.
export interface Backlink {
  from_id: string;
  kind: string;
  auto: boolean;
  from_title?: string;
  from_type?: string;
  from_status?: string;
  from_document_id?: string;
}

// RelatedChunk is one semantic-neighbour of the selected chunk, from `related`
// (RFC BS) — a vector-similarity match. `score` is the similarity (higher = closer).
// The `related` op REFUSES when the deployment has no embedder configured; the
// viewer surfaces that as a muted note rather than an error.
export interface RelatedChunk {
  chunk_id: string;
  score: number;
  title?: string;
  type?: string;
  document_id?: string;
}

// UnlinkedMention is a chunk whose body mentions the selected chunk's title but
// carries no explicit link to it, from `unlinked_mentions` (RFC BS) — a candidate
// link the author might want to formalize. The list is capped; the response's
// `truncated` flag (not on the row) signals more were elided.
export interface UnlinkedMention {
  chunk_id: string;
  title?: string;
  document_id?: string;
}

// ChunkRevision is one entry in a chunk's edit history, from `history` (RFC BS):
// the monotonic `revision` number, `created_at` (Unix nanoseconds — divide by 1e6
// for a JS Date), and the `actor` who wrote it. Fetch a revision's body with
// documentGetVersion, or a unified diff between two with documentDiff.
export interface ChunkRevision {
  revision: number;
  created_at: number;
  actor: string;
}

// CanvasDoc is the whole-document node/edge graph from `export_canvas` (RFC BS),
// downloadable as a `.canvas` (JSON) file. The node/edge shapes are backend-owned
// and opaque to the viewer (it only serializes them), so they stay `unknown[]`.
export interface CanvasDoc {
  nodes: unknown[];
  edges: unknown[];
}

// Principal is the authenticated identity behind the session (resolved by the
// host, e.g. via GET /v1/_me). The explorer only reads `subject` — it is passed
// into the renderAssistant slot's context so a host-provided Document Assistant
// can scope its agent run to the same subject the viewer shows. Kept minimal on
// purpose; a host may pass its full whoami result (extra fields are ignored).
export interface Principal {
  subject: string;
}

// AssistantContext is what the DocumentViewer's optional renderAssistant slot
// receives. The package deliberately does NOT bundle the run-stream machinery
// (LiveRunPane / useRunStream) — a host wires its own assistant panel and reads
// this context to target the right document + subject.
export interface AssistantContext {
  documentId: string;
  scope: string;
  subject?: string;
  // onChanged lets an injected assistant refresh the viewer after it mutates the
  // document (e.g. at each turn boundary) — it calls the viewer's own reload, so
  // the tree + selected chunk re-fetch. Without it the viewer would show stale
  // content until a manual reload.
  onChanged?: () => void;
}
