/**
 * audit-dev-frontend/types.ts — CLI args + finding + report shapes.
 *
 * Two finding shapes coexist:
 * - System-prompt envelope (code/label/params/solution) — what the Studio
 *   audit-reports UI consumes. The CLI emits this verbatim inside auditReport.
 * - CLI-internal extension (file/line/autoFixable/snippet) — used by apply.ts
 *   to locate and rewrite. Stripped before the system-prompt envelope is built.
 *
 * Severity vocabulary matches the existing audit-dev-* skills: 'err'|'warn'|'ok'.
 * 'ok' findings are emitted for passing rules so the UI shows green checks.
 */
import { z } from 'zod';

export const AuditDevFrontendModeSchema = z.enum(['audit', 'apply']);
export type AuditDevFrontendMode = z.infer<typeof AuditDevFrontendModeSchema>;

export const AuditDevFrontendArgsSchema = z.object({
  /** Absolute path to the generated SmartStack web root (holds src/pages). */
  projectPath: z.string().min(1),
  /** `audit` = report only ; `apply` = mechanical rewrite of fixable findings. */
  mode: AuditDevFrontendModeSchema,
  /** Path to the PRD frontend slice JSON (parsed structured form, not Markdown). */
  prdSlice: z.string().optional(),
  /** Path to a pre-built inventory JSON. If omitted, self-built from disk. */
  inventory: z.string().optional(),
  /** Active module code (PRD scope). */
  moduleCode: z.string().min(1),
  /** Active application code (PRD scope). */
  appCode: z.string().min(1),
  /** Subset of rule codes to run (e.g. ['DEV-UI-001','DEV-UI-005']). Empty = all. */
  rules: z.array(z.string()).default([]),
  /**
   * Regeneration threshold — in `apply` mode, any *Page.tsx file with ≥ this
   * many ERROR findings remaining after mechanical fixes is REBUILT from
   * scaffold-component template (OVERWRITES). Default 3, 0 to disable.
   */
  regenerateThreshold: z.number().int().min(0).max(100).default(3),
  /** Optional path to the .NET project root (for DEV-UI-011 controller scan). */
  backendPath: z.string().optional(),
  /** Optional path to Persistence/Seeding/Core/CoreNavigationSeedDataProvider.cs (for DEV-UI-012). */
  coreNavigationSeedPath: z.string().optional(),
});

export type AuditDevFrontendArgs = z.infer<typeof AuditDevFrontendArgsSchema>;

// ─── Finding ──────────────────────────────────────────────────────────────

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

/**
 * A single audit finding. The first half is the system-prompt envelope shape
 * (consumed by Studio audit-reports UI). The second half is CLI-internal
 * metadata used by apply.ts to locate and rewrite the file.
 */
export interface Finding {
  // ─── System-prompt envelope shape (do not rename) ───
  /** Always 'devFrontend' for this skill. */
  dimension: 'devFrontend';
  /** Rule code, e.g. 'DEV-UI-001'. */
  code: string;
  severity: Severity;
  /** Display label, e.g. 'DEV_UI_001_err'. */
  label: string;
  /** Rule-specific data (rendered into the solution / UI). */
  params: Record<string, unknown>;
  /** Mandatory on err/warn — user-facing remediation. */
  solution: string;
  /** Optional skill name to delegate the fix to. */
  fixSkill?: string;
  /** Optional phase key to re-run. */
  fixPhaseKey?: string;

  // ─── CLI-internal extension (stripped before system-prompt envelope) ───
  /** Relative file path (from projectPath) where the finding lives. Optional for cross-cutting findings. */
  file?: string;
  line?: number;
  autoFixable: boolean;
  /** Source context for apply.ts to locate the violation. */
  snippet?: string;
}

// ─── Inventory ────────────────────────────────────────────────────────────

export interface PageEntry {
  /** Relative file path from projectPath, e.g. 'src/pages/crm/contacts/ContactListPage.tsx'. */
  file: string;
  /** Inferred entity name from filename (e.g. 'Contact', null if not a *Page.tsx). */
  entity: string | null;
  /** Inferred page kind (list, detail, form, dashboard, home). */
  kind: 'list' | 'detail' | 'form' | 'create' | 'edit' | 'dashboard' | 'home' | 'other';
}

export interface RegistryEntry {
  /** Relative file path from projectPath. */
  file: string;
  /** Component keys registered (e.g. 'crm.contacts.directory'). */
  keys: string[];
  /** Lazy import targets resolved against registry file location. */
  imports: Array<{ key: string; importPath: string; resolvedAbsPath: string | null }>;
}

export interface FrontendInventory {
  pages: PageEntry[];
  registries: RegistryEntry[];
  i18n: {
    locales: string[];
    namespaces: string[];
  };
}

// ─── PRD slice (structured) ───────────────────────────────────────────────

export interface PrdScreen {
  code: string;
  entity?: string;
  kind?: string;
  /** SmartComponent type (SmartListView, SmartForm, etc.) */
  smartComponent?: string;
}

export interface PrdUseCase {
  code: string;
  /** Optional reference to the entity primarily handled by the use case. */
  entity?: string;
  /** Optional sequence of screen codes the user navigates through. */
  screens?: string[];
}

export interface PrdBusinessRule {
  code: string;
  description: string;
  /** Where the rule must be enforced. 'ui' or 'both' means a UI-side check is mandatory. */
  enforcedAt?: 'ui' | 'backend' | 'both';
  /** Entity primarily affected (used to locate the form page). */
  entity?: string;
  validExamples?: unknown[];
  invalidExamples?: unknown[];
}

export interface PrdPermission {
  /** Permission key, e.g. 'crm.contacts.read'. */
  code: string;
  /** Optional related entity (used to map permission → page). */
  entity?: string;
  /** Optional related screen (used when entity-less). */
  screen?: string;
}

export interface PrdRbacEntry {
  /** Actor/role code, e.g. 'sales-rep'. */
  actor: string;
  /** Permission codes granted. */
  grants: string[];
}

export interface PrdSliceFrontend {
  applicationCode?: string;
  moduleCode?: string;
  screens?: PrdScreen[];
  useCases?: PrdUseCase[];
  businessRules?: PrdBusinessRule[];
  permissions?: PrdPermission[];
  rbac?: PrdRbacEntry[];
}

// ─── Reports ──────────────────────────────────────────────────────────────

export interface AuditReport {
  scope: 'devFrontend';
  applicationCode: string;
  moduleCode: string;
  projectPath: string;
  filesInspected: number;
  findings: Finding[];
  counts: { errors: number; warnings: number; oks: number };
  byRule: Record<string, number>;
}

export interface ApplyReport extends AuditReport {
  fixesApplied: number;
  filesModified: string[];
  fixesSkipped: Finding[];
  regenerations?: Array<{ file: string; regenerated: boolean; reason: string }>;
}

// ─── Output envelope ──────────────────────────────────────────────────────

/**
 * The CLI prints this object on stdout. Two consumers:
 * - Orchestrator (ba-develop subagent) reads `report.findings`
 *   and decides whether the gate passes (counts.errors === 0 after apply).
 * - Studio audit-reports UI reads `report.auditReport` (system-prompt-shaped)
 *   to render green/red checks per rule.
 *
 * The two views are derived from the same internal `findings` array.
 */
export interface AuditDevFrontendOutput {
  success: boolean;
  command: 'audit-dev-frontend';
  data: {
    mode: AuditDevFrontendMode;
    rulesRun: string[];
    valid: boolean;
  };
  report: (AuditReport | ApplyReport) | null;
  /** System-prompt-shaped envelope for Studio audit-reports UI. */
  auditReport: {
    scope: 'devFrontend';
    applicationCode: string;
    moduleCode: string;
    findings: Array<Pick<Finding, 'dimension' | 'code' | 'severity' | 'label' | 'params' | 'solution' | 'fixSkill' | 'fixPhaseKey'>>;
  } | null;
  filesModified: string[];
  errors: string[];
  warnings: string[];
  nextSteps: string[];
}
