// Shared types for the PR-bot. Small and explicit — the bot's behavior
// should be readable from these shapes alone.

export interface Gap {
  id: string;              // hash of prd_line — stable key for dedup
  prd_line: string;        // the [bot-eligible]-tagged line, verbatim
  context: string;         // surrounding context from the PRD (next ~5 lines)
  source_file: string;     // e.g. "docs/PRD.md"
  line_number: number;
}

export interface ProposalFile {
  path: string;            // repo-relative (no leading slash)
  content: string;         // full file content — the bot writes whole files, not patches
  is_new: boolean;         // true = create, false = overwrite existing
}

export interface Proposal {
  summary: string;         // one-line
  rationale: string;       // why this change addresses the gap
  files: ProposalFile[];
  estimated_loc: number;   // total added+changed lines across all files
  model_used: string;
  tokens_used: number;
}

export interface CycleContext {
  startedAt: string;       // ISO
  dailyCallsUsed: number;
  dailyCallsBudget: number;
  dailyTokensUsed: number;
  dailyTokensBudget: number;
  lastCycleAt: string | null;
  lastCycleGapId: string | null;
  autotuneBusy: boolean;
  workingTreeDirty: boolean;
}

export type CycleOutcome =
  | "ok"                    // PR opened
  | "skipped_pause"
  | "skipped_cost"
  | "skipped_policy"
  | "skipped_no_gap"
  | "skipped_dirty_tree"
  | "proposal_rejected"
  | "apply_failed"
  | "cycle_noop"            // proposal applied but every file was identical to what's on disk
  | "tests_failed"
  | "pr_skipped_by_policy"
  | "pr_failed"
  | "model_failed";

// Mem0-aligned apply outcomes. Three shapes instead of the binary
// "written" / "errored". NOOP is a first-class outcome — identical
// content shouldn't waste test cycles or open an empty PR.
export type ApplyMode = "add" | "update" | "noop";

export interface ApplyOutcome {
  added: string[];
  updated: string[];
  noop: string[];
  errors: string[];
}

export interface CycleResult {
  cycle_id: string;
  started_at: string;
  ended_at: string;
  outcome: CycleOutcome;
  reason: string;
  gap: Gap | null;
  proposal: Proposal | null;
  filesAdded: string[];
  filesUpdated: string[];
  filesNoop: string[];
  testsGreen: boolean | null;     // null = not run
  testsOutput: string;
  prUrl: string | null;
  tokens_used: number;
  cloud_calls: number;
}

export interface CostState {
  date: string;          // YYYY-MM-DD UTC
  calls: number;
  tokens: number;
}
