import { RichTextBlock, placeholderKeysIn } from '@shared/kernel/rich-text';
import { Diagnostic, lintPlaceholders, PlaceholderDef } from './placeholders';

/**
 * The `Brief` (letter) entity and its derived selectors.
 *
 * A letter has a FIXED section structure (from a template, server-instantiated); the
 * drafter fills a skeleton, never reorders sections. Each block is either a frozen
 * snapshot of a library passage (provenance kept) or free text. Everything the UI
 * needs beyond the stored shape — diagnostics, unresolved placeholders, whether it
 * can be submitted — is DERIVED here, never stored.
 */

export type PassageScope = 'global' | 'beroep';

// Re-export placeholderKeysIn for one-import convenience at call sites.
export { placeholderKeysIn };

/** A passage in the library (the source). Snapshotted into a letter on insert. */
export interface LibraryPassage {
  readonly passageId: string;
  readonly scope: PassageScope;
  readonly beroep?: string; // set when scope === 'beroep'
  readonly sectionKey: string;
  readonly label: string;
  readonly content: RichTextBlock;
  readonly version: number; // library version, for provenance only
}

/** A block inside a letter section: a frozen passage snapshot, or free text. */
export type LetterBlock =
  | {
      readonly type: 'passage';
      readonly blockId: string;
      readonly sourcePassageId: string; // provenance
      readonly sourceVersion: number; // library version at snapshot time (audit only)
      readonly content: RichTextBlock; // FROZEN, possibly edited — source of truth for this block
      readonly edited: boolean; // changed from the snapshot?
    }
  | {
      readonly type: 'freeText';
      readonly blockId: string;
      readonly content: RichTextBlock;
    };

export interface LetterSection {
  readonly sectionKey: string;
  readonly title: string;
  readonly required: boolean;
  // Predefined template sections (aanhef, slot) arrive locked and prefilled — the drafter
  // composes only the unlocked section(s). The reducer refuses edits to locked sections.
  readonly locked: boolean;
  readonly blocks: readonly LetterBlock[];
}

/** The approval state machine as a sum type — transitions are total and guarded in
    `brief.machine.ts`; illegal transitions are unrepresentable. */
export type BriefStatus =
  | { readonly tag: 'draft' }
  | { readonly tag: 'submitted'; readonly submittedBy: string; readonly submittedAt: string }
  | { readonly tag: 'approved'; readonly approvedBy: string; readonly approvedAt: string }
  | { readonly tag: 'rejected'; readonly rejectedBy: string; readonly rejectedAt: string; readonly comments: string }
  | { readonly tag: 'sent'; readonly sentAt: string };

export interface Brief {
  readonly briefId: string;
  readonly beroep: string; // drives which beroep-scoped passages apply
  readonly templateId: string;
  readonly placeholders: readonly PlaceholderDef[]; // valid fields for this letter
  readonly sections: readonly LetterSection[]; // instantiated from the template, in order
  readonly status: BriefStatus;
  readonly drafterId: string;
}

// --- Derived selectors (pure; recomputed, never stored) ---

export function allBlocks(brief: Brief): LetterBlock[] {
  return brief.sections.flatMap((s) => s.blocks);
}

/** Every diagnostic in the letter, in section→block→node order. This is what the
    diagnostics panel renders and what the send gate checks. */
export function allDiagnostics(brief: Brief): Diagnostic[] {
  return allBlocks(brief).flatMap((b) => lintPlaceholders(b.content, brief.placeholders, b.blockId));
}

export function hasBlockingErrors(diagnostics: readonly Diagnostic[]): boolean {
  return diagnostics.some((d) => d.severity === 'error');
}

/** Manual (non-auto-resolvable) placeholder keys still present, deduped. These are the
    `unresolved-at-send` warnings, surfaced as a completeness list. */
export function unresolvedPlaceholders(brief: Brief): string[] {
  const auto = new Set(brief.placeholders.filter((p) => p.autoResolvable).map((p) => p.key));
  const used = allBlocks(brief).flatMap((b) => placeholderKeysIn(b.content));
  return [...new Set(used.filter((k) => !auto.has(k)))];
}

/** A letter can be submitted only when every REQUIRED section has at least one block. */
export function canSubmit(brief: Brief): boolean {
  return brief.sections.every((s) => !s.required || s.blocks.length > 0);
}
