/**
 * ui-polish/theme-capabilities.ts — Project-aware conformance context.
 *
 * The historical bug this fixes: ui-polish audited every client against a frozen
 * snapshot of ONE reference app (tokens.json, extracted from
 * SmartStack.app/customisation-ui). Two failure modes followed:
 *
 *   1. It could not see theme-PRIMITIVE bypass — a page that hand-rolls
 *      `bg-[var(--color-accent-500)] text-white` instead of the `.btn .btn-primary`
 *      helper passed, because the engine only hunts raw colors.
 *   2. It enforced page ARCHITECTURE (`PageTemplate`, `Slot`) verbatim on apps that
 *      never adopted it, and its auto-fix imported a component that does not exist
 *      → broken build.
 *
 * The cure is to derive the conformance contract from what the TARGET project
 * actually provides, then:
 *   - flag bypass of primitives the project SHIPS (helper classes / soft tokens), and
 *   - never require / auto-import components the project LACKS.
 *
 * Two different guarantees, deliberately asymmetric:
 *   - Helper CSS classes (`.btn-primary`, `.badge-accent`) ship with the platform
 *     theme (`@atlashub/smartstack/theme.css`) → seeded from the tokens.json
 *     baseline (always present) and UNIONed with the project's local CSS.
 *   - React components (`PageTemplate`, `Slot`) are scaffolded per app → detected
 *     by scanning `src/components/**` on disk; never presumed.
 *
 * Zero-dep, regex/glob-based — consistent with audit.ts. NOT an AST/CSS parser.
 */
import path from 'node:path';
import { findFiles, readText } from '../../../../../lib/fs.js';
import type { TokensFile } from './types.js';

export interface ThemeCapabilities {
  /** CSS helper classes available to the project (e.g. 'btn', 'btn-primary', 'badge-accent'). */
  helperClasses: Set<string>;
  /** CSS custom-property names defined in the project's own stylesheets (e.g. '--accent-bg'). */
  tokens: Set<string>;
  /**
   * True when the project's scanned CSS defines the core design-system tokens
   * (sentinels: the accent ramp + a surface token), i.e. the theme lives in the
   * project's own stylesheets and `tokens` is authoritative. Only then can a rule
   * safely conclude a `var(--x)` reference is DANGLING (R24): if the theme were
   * imported solely from the package (not scanned), `tokens` would be incomplete
   * and every reference would look dangling. When false, R24 stays silent.
   */
  tokensComplete: boolean;
  /** Component basenames present anywhere under src/components (e.g. 'PageTemplate', 'DataTable'). */
  components: Set<string>;
  /** Does the project ship a component file named `<name>` under src/components? */
  hasComponent(name: string): boolean;
}

/** ".btn .btn-primary" → ['btn', 'btn-primary'] ; ".badge-accent" → ['badge-accent']. */
function classNamesFromSelector(selector: string): string[] {
  return selector
    .split(/\s+/)
    .map((s) => s.replace(/^\./, '').trim())
    .filter(Boolean);
}

/**
 * Build the project-aware conformance context. Pure read-only filesystem scan;
 * never throws (a project with no CSS / no components simply yields the baseline).
 */
export async function detectThemeCapabilities(
  projectPath: string,
  tokens: TokensFile,
): Promise<ThemeCapabilities> {
  // 1) Baseline helper classes from the tokens catalogue. The platform theme
  //    ships these via @atlashub/smartstack/theme.css (which lives in
  //    node_modules and is therefore invisible to findFiles), so we presume them
  //    present in every SmartStack app.
  const helperClasses = new Set<string>();
  for (const entry of Object.values(tokens.componentClasses ?? {})) {
    if (entry && typeof entry.class === 'string') {
      for (const c of classNamesFromSelector(entry.class)) helperClasses.add(c);
    }
  }

  // 2) Union the project's OWN stylesheets: real class selectors + the
  //    custom-property names actually defined here (e.g. the soft-accent triplet
  //    --accent-bg/--accent-text/--accent-border that this project declares).
  const tokenNames = new Set<string>();
  let cssFiles: string[] = [];
  try {
    cssFiles = await findFiles('src/**/*.css', { cwd: projectPath });
  } catch {
    /* no stylesheets — baseline only */
  }
  const classSelRe = /\.([a-z][a-z0-9-]*)/g; // simple, over-captures harmlessly
  const customPropRe = /(--[a-z][a-z0-9-]*)\s*:/g; // a definition, not a var() use
  for (const file of cssFiles) {
    let css: string;
    try {
      css = await readText(file);
    } catch {
      continue;
    }
    let m: RegExpExecArray | null;
    classSelRe.lastIndex = 0;
    while ((m = classSelRe.exec(css)) !== null) helperClasses.add(m[1]);
    customPropRe.lastIndex = 0;
    while ((m = customPropRe.exec(css)) !== null) tokenNames.add(m[1]);
  }

  // 3) Components present on disk. Scaffolded per app — never presumed.
  const components = new Set<string>();
  let compFiles: string[] = [];
  try {
    compFiles = await findFiles('src/components/**/*.tsx', { cwd: projectPath });
    compFiles.push(...(await findFiles('src/components/**/*.ts', { cwd: projectPath })));
  } catch {
    /* none */
  }
  for (const f of compFiles) {
    components.add(path.basename(f).replace(/\.(tsx|ts)$/, ''));
  }

  // The theme is authoritative in the project's own CSS when the accent ramp AND a
  // surface token are defined locally (scaffold-theme emits exactly these). Used as
  // the safety gate for the dangling-token rule (R24).
  const tokensComplete =
    tokenNames.has('--color-accent-500') &&
    (tokenNames.has('--bg-card') || tokenNames.has('--bg-app'));

  return {
    helperClasses,
    tokens: tokenNames,
    tokensComplete,
    components,
    hasComponent: (name: string) => components.has(name),
  };
}
