// Plain data shapes shared by every surface. Results are stored (saved runs, exports), so these shapes only grow.

export type Impact = "critical" | "serious" | "moderate" | "minor";
export type WcagLevel = "A" | "AA" | "AAA";
export type RuleLevel = WcagLevel | "best-practice";

export interface Settings {
  level: WcagLevel;
  bestPractice: boolean;
}

/** A WCAG 2.2 success criterion; `url` is its W3C Understanding document. */
export interface Criterion {
  sc: string;
  name: string;
  level: WcagLevel;
  url: string;
}

export interface RuleText {
  title: string;
  whatIsWrong: string;
  whoIsAffected: string;
  fixSteps: string[];
  example?: { before: string; after: string };
}

// Minimal structural types of axe-core results, so core and its callers do not depend on axe-core's own types.
export interface AxeCheck {
  id: string;
  data?: unknown;
  message?: string;
}

export interface AxeNode {
  target: (string | string[])[];
  html: string;
  failureSummary?: string;
  impact?: Impact | null;
  any: AxeCheck[];
  all: AxeCheck[];
  none: AxeCheck[];
}

export interface AxeRule {
  id: string;
  impact?: Impact | null;
  tags: string[];
  description: string;
  help: string;
  helpUrl: string;
  nodes: AxeNode[];
}

export interface AxeResultsLike {
  violations: AxeRule[];
  incomplete?: AxeRule[];
  url?: string;
}

/** Colours are "#rrggbb". */
export interface ContrastDetail {
  foreground: string;
  background: string;
  ratio: number;
  required: number;
  fontSize?: string;
  fontWeight?: string;
  suggestion: string | null;
}

export interface Issue {
  /** Stable fingerprint of rule id and selector; used for baselines and "hide for this page". */
  key: string;
  ruleId: string;
  impact: Impact;
  level: RuleLevel;
  criteria: Criterion[];
  /** Our explanation; built from axe's help and description when a rule has no text of ours. */
  text: RuleText;
  /** CSS selector from axe's target, with " >>> " between frames and shadow roots. */
  selector: string;
  /** Element markup, at most 300 characters. */
  snippet: string;
  summary?: string;
  contrast?: ContrastDetail;
  helpUrl: string;
}

export interface OutlineInput {
  headings: { level: number; text: string; selector: string }[];
  landmarks: { role: string; label: string; selector: string }[];
}

export interface Outline {
  headings: { level: number; text: string; selector: string; skipped: boolean }[];
  landmarks: { role: string; label: string; selector: string }[];
  problems: string[];
}

export interface PageResult {
  url: string;
  title: string;
  checkedAt: string;
  settings: Settings;
  issues: Issue[];
  counts: Record<Impact, number>;
  untestedFrames: string[];
  outline: Outline;
}

export interface SiteScan {
  origin: string;
  startedAt: string;
  finishedAt?: string;
  settings: Settings;
  pages: { url: string; title: string; issues: Issue[]; error?: string }[];
  stopped?: boolean;
}

export type IssueStatus = "new" | "unchanged" | "fixed";

export interface Comparison {
  new: Issue[];
  unchanged: Issue[];
  fixed: Issue[];
}

export interface ScanComparison {
  byPage: Record<string, Comparison>;
  totals: Record<IssueStatus, number>;
}

export const IMPACTS: readonly Impact[] = ["critical", "serious", "moderate", "minor"];
