import { z } from 'zod';
import type { SquashBranchType } from '../lib/squash-base.js';
import type { SquashMode } from '../lib/squash-policy.js';

export const SquashSpecSchema = z.object({
  cwd: z.string().min(1),
  contextName: z.string().min(1),
  description: z.string().min(1),
  /** Target a single assembly; otherwise all assemblies of the context. */
  assembly: z.string().optional(),
  /**
   * Explicit base (reference) branch override. When omitted, the base is
   * derived from the current branch TYPE (feature→develop,
   * release/hotfix/develop→main) — see lib/squash-base.ts.
   */
  baseBranch: z.string().optional(),
  /** Dry-run: plan without mutating files. */
  dryRun: z.boolean().optional().default(false),
  /**
   * Operation intent. `rebase-snapshot` lowers the branch-only threshold to 1
   * and resets the snapshot even with zero branch-only migrations — see
   * lib/squash-policy.ts. The `rebase-snapshot` CLI sets this; squash defaults
   * to `squash`.
   */
  mode: z.enum(['squash', 'rebase-snapshot']).optional().default('squash'),
  /**
   * Deliberate override of the "branch is behind its reference" safety guard.
   * Squashing while behind the reference can regenerate a migration that DROPS
   * what the reference added (data loss at merge). Off by default — sync first.
   */
  allowBehindBase: z.boolean().optional().default(false),
  /**
   * THE ONE EXCEPTION (`/efcore -BruteForce`). Full re-baseline: delete EVERY
   * migration — including the ones that already live on the reference /
   * production branch — plus the snapshot, and regenerate a single
   * InitialCreate. Lifts the branch-only restriction AND the behindBase /
   * missing-reference safety gates. `main`/`master` stay blocked even here.
   * Off by default. Requires `confirmBruteForce: true` to actually run.
   */
  bruteForce: z.boolean().optional().default(false),
  /**
   * Validation token for `bruteForce`. With `bruteForce:true` but WITHOUT this,
   * the CLI REFUSES and returns `requiresConfirmation:true` plus the exact list
   * of production migrations that would be destroyed — forcing an explicit
   * human "yes" before any prod history is rewritten.
   */
  confirmBruteForce: z.boolean().optional().default(false),
  /** @deprecated Ignored — reference-branch migrations are always preserved (use `bruteForce` for a deliberate full re-baseline). */
  aggressive: z.boolean().optional().default(false),
});
export type SquashSpec = z.infer<typeof SquashSpecSchema>;
export type { SquashMode };

export interface SquashAssemblyResult {
  assemblyName: string;
  contextName: string;
  backupDir: string | null;
  deletedFiles: string[];
  /** Count of migrations inherited from the reference branch, left untouched. */
  baseMigrationsPreserved: number;
  /** 🔒 Names of the migrations inherited from the reference branch (the INTOUCHABLE parent set). */
  parentMigrations: string[];
  /** ☠️ Reference-branch migrations MISSING from the working tree (deleted/renamed locally). Non-empty ⇒ a merge would drop them from production — a plain squash REFUSES. */
  missingFromWorkingTree: string[];
  resetSnapshot: boolean;
  newMigrationName: string | null;
  /**
   * True when the reference branch carries NO migrations/snapshot for this
   * context (an "empty base"): the snapshot is deleted (not restored) and the
   * consolidated migration is a full `InitialCreate`. A normal, casualty-free
   * squash — NOT a bruteForce re-baseline.
   */
  emptyBaseModel?: boolean;
  /**
   * True when a FAILED real run was automatically rolled back to its pre-run
   * state (no half-squashed tree). When set, `deletedFiles` is empty because the
   * deletions were undone — the operation is a no-op. Only false-with-warning if
   * the rollback itself failed (the backup is then the manual recovery path).
   */
  rolledBack?: boolean;
  /** SQL objects (schema.name) frozen into the regenerated migration so a plain `database update` deploys them. */
  sqlObjectsInlined: string[];
  dotnetOutput: string;
  success: boolean;
  error?: string;
}

export interface SquashResult {
  success: boolean;
  cwd: string;
  /** The reference branch the squash diffed/restored against. */
  baseBranch: string;
  /** Branch type that selected the reference branch (the GOLDEN RULE). */
  branchType: SquashBranchType;
  currentBranch: string;
  /** Commits the current branch is behind the reference branch (0 = in sync). */
  behindBase: number;
  /**
   * True when a `bruteForce` run was refused for lack of `confirmBruteForce`.
   * The agent must show the casualties (the prod migrations that would die) and
   * ask the user, then re-run with `confirmBruteForce: true`.
   */
  requiresConfirmation?: boolean;
  /** Path of the immutable audit record written under `.claude/Efcore-history/` (destructive runs only). */
  historyLogPath?: string;
  assemblies: SquashAssemblyResult[];
  warnings: string[];
  error?: string;
}
