/**
 * audit-dev-actions-alignment / types.ts
 *
 * Detects desync between the three sources of an action's URL:
 *   1. pageSpec.customActions[].endpoint        (BA contract — generated by ba-create-prd)
 *   2. <Entity>Controller.cs [HttpVerb("…")]   (backend reality)
 *   3. <entity>Service.ts api.<verb>(…)         (frontend reality)
 *
 * The healthy state: the three agree. Every drift falls into one of 5 shapes
 * encoded as ACTION-DRIFT-001..005 below. A common instance of ACTION-DRIFT-002
 * is a pagespec ↔ service drift causing a 405 error.
 */
import { z } from 'zod'

export const ActionAlignmentModeSchema = z.enum(['report-only', 'apply'])
export type ActionAlignmentMode = z.infer<typeof ActionAlignmentModeSchema>

export const ActionAlignmentSourceSchema = z.enum(['pagespec', 'controller'])
export type ActionAlignmentSource = z.infer<typeof ActionAlignmentSourceSchema>

export const ActionAlignmentArgsSchema = z.object({
  /** Absolute path to the generated frontend root (holds src/features/.../services/*Service.ts). */
  projectPath: z.string().min(1),
  /** Absolute path to the .NET backend root (holds *Controller.cs under src/.../Controllers/). */
  backendPath: z.string().min(1),
  /**
   * Absolute path to the module folder inside `.smartstack/ba/<APP>/<MODULE>`.
   * The CLI loads every `pagespecs/*.md` directly under this folder.
   * When omitted, the CLI walks the `.smartstack/ba/` tree under `projectPath`
   * (best-effort discovery).
   */
  modulePath: z.string().optional(),
  /** `report-only` writes a markdown report only ; `apply` rewrites the chosen
   *  side of the drift. Default `report-only` (safe). */
  mode: ActionAlignmentModeSchema.default('report-only'),
  /** In `apply` mode, which side wins when pagespec ≠ controller:
   *  - `pagespec`   → rewrite the controller's `[HttpVerb("…")]` to match pagespec.
   *  - `controller` → rewrite the pagespec's `endpoint` to match the controller.
   *  Frontend service.ts is always regenerated from the (now-agreed) source. */
  sourceOfTruth: ActionAlignmentSourceSchema.default('pagespec'),
  /** When true, also write the markdown report to `<projectPath>/_audit/actions-alignment.md`.
   *  Always emitted on stdout regardless. Default true. */
  writeReport: z.boolean().default(true),
})

export type ActionAlignmentArgs = z.infer<typeof ActionAlignmentArgsSchema>

// ─── Domain shapes ────────────────────────────────────────────────────────

/** One pagespec action of `kind:"api"` after normalisation. */
export interface PageActionEntry {
  /** Path of the pagespec file relative to the module folder. */
  file: string
  /** Entity referenced by the pagespec (PascalCase). */
  entity: string
  /** Module folder code (lowercase). */
  module: string
  /** Section folder code (lowercase). */
  section: string
  /** camelCase BA identifier. */
  code: string
  /** kebab-case URL segment — the source of truth. */
  endpoint: string
  /** HTTP verb (uppercase). */
  httpMethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
  /** Where the button lives. */
  scope: 'row' | 'bulk' | 'header'
  /** UC-... traceability (optional). */
  ucReference?: string
}

/** A `[HttpVerb("...")]` declaration on a `*Controller.cs`. */
export interface ControllerEndpointEntry {
  /** Lowercase verb: `get` / `post` / `put` / `patch` / `delete`. */
  verb: string
  /** Route path relative to the controller mount, normalised:
   *   - `{id:guid}/sync-from-proconcept` → `{id}/sync-from-proconcept`
   *   - empty string for the bare `[HttpGet]` / `[HttpPost]` on the resource. */
  routePath: string
  /** Controller file path relative to backendPath. */
  file: string
  /** Inferred entity (from `<Entity>sController.cs` filename, singularized). */
  entity: string
}

/** An axios call `api.<verb>('/path', ...)` in a `*Service.ts`. */
export interface ServiceCallEntry {
  /** Lowercase verb. */
  verb: string
  /** URL normalised: `/api/...` stripped, `${id}` → `{id}`. */
  urlPath: string
  /** Service file path relative to projectPath. */
  file: string
  /** Inferred entity (from `<entity>Service.ts` filename, capitalized). */
  entity: string
}

// ─── Findings ────────────────────────────────────────────────────────────

export type DriftCode =
  | 'ACTION-DRIFT-001'  // pagespec ↔ controller mismatch (endpoint / verb / scope)
  | 'ACTION-DRIFT-002'  // pagespec ↔ service mismatch (URL drift)
  | 'ACTION-DRIFT-003'  // service URL not found in any pagespec or controller (dead call)
  | 'ACTION-DRIFT-004'  // controller endpoint not referenced by any pagespec or service (dead endpoint)
  | 'ACTION-DRIFT-005'  // service verb ≠ controller verb on the same URL (e.g. GET vs POST on same endpoint)

export type Severity = 'err' | 'warn' | 'ok'

export interface AlignmentFinding {
  code: DriftCode | 'ACTION-DRIFT-OK'
  severity: Severity
  entity: string
  /** Short human label. */
  message: string
  /** Concrete suggestion (pagespec / controller / service to edit). */
  suggestion: string
  /** Optional structured detail consumed by `apply.ts`. */
  detail?: {
    pageActionEndpoint?: string
    controllerRoute?: string
    serviceUrl?: string
    pagespecFile?: string
    controllerFile?: string
    serviceFile?: string
    verb?: string
    scope?: PageActionEntry['scope']
  }
}

export interface AlignmentReport {
  /** Totals per severity. */
  counts: { err: number; warn: number; ok: number }
  /** Inventory snapshot per source. */
  inventory: {
    pageActions: number
    controllerEndpoints: number
    serviceCalls: number
    entitiesCovered: string[]
  }
  /** Every finding emitted by the audit pass. */
  findings: AlignmentFinding[]
  /** Markdown rendering, written to disk and embedded in stdout when requested. */
  markdown: string
}

export interface ActionAlignmentOutput {
  success: boolean
  command: 'audit-dev-actions-alignment'
  data: {
    mode: ActionAlignmentMode
    sourceOfTruth: ActionAlignmentSource
    moduleDir: string | null
  }
  report: AlignmentReport | null
  filesModified: string[]
  errors: string[]
  warnings: string[]
  nextSteps: string[]
}

export interface ValidationResult {
  valid: boolean
  args?: ActionAlignmentArgs
  errors: string[]
}
