#!/usr/bin/env node
/**
 * ui-polish/index.ts — Entry point.
 * parse args → validate → load tokens → dispatch to audit() or apply() → print JSON.
 *
 * Invoked by Claude via Bash. See sibling SKILL.md for the invocation contract.
 */
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { Command } from 'commander';
import { validateArgs } from './validate.js';
import { audit, loadTokens } from './audit.js';
import { apply } from './apply.js';
import type { UiPolishOutput, UiPolishMode } from './types.js';

const program = new Command();

program
  .name('ui-polish')
  .description('Audit / apply UI conformity fixes on a generated SmartStack web app')
  .requiredOption('--project-path <path>', 'Absolute path to the generated web root (holds src/pages)')
  .requiredOption('--mode <mode>', 'audit | apply', 'audit')
  .option('--rules <list>', 'Comma-separated rule IDs to run (default = all)')
  .option('--page <list>', 'Comma-separated page file path(s) relative to project root to scan (default = all pages)')
  .option('--url <list>', 'Comma-separated URL paths to resolve to component files via PageRegistry')
  .option('--tokens-file <path>', 'Override path for tokens.json')
  .option('--regenerate-threshold <n>', 'Pages with ≥ N errors get rebuilt from template in apply mode (default 3, 0 = disable regen)', '3')
  .parse(process.argv);

const opts = program.opts();

const raw = {
  projectPath: opts.projectPath as string,
  mode: opts.mode as UiPolishMode,
  rules: typeof opts.rules === 'string'
    ? opts.rules.split(',').map((s: string) => s.trim()).filter(Boolean)
    : [],
  paths: typeof opts.page === 'string'
    ? opts.page.split(',').map((s: string) => s.trim()).filter(Boolean)
    : [],
  urls: typeof opts.url === 'string'
    ? opts.url.split(',').map((s: string) => s.trim()).filter(Boolean)
    : [],
  tokensFile: opts.tokensFile as string | undefined,
  regenerateThreshold: Number(opts.regenerateThreshold ?? 3),
};

const validation = validateArgs(raw);
if (!validation.valid || !validation.args) {
  const output: UiPolishOutput = {
    success: false,
    command: 'ui-polish',
    data: { mode: (raw.mode ?? 'audit'), rulesRun: [], valid: false },
    report: null,
    filesModified: [],
    errors: validation.errors,
    warnings: [],
    nextSteps: [],
  };
  console.log(JSON.stringify(output, null, 2));
  process.exit(1);
}

// Async work is wrapped in an IIFE (not top-level await) so tsx can run this
// file in any module context — consistent with the other dev CLIs' `void main()`.
void (async () => {
  try {
    // Default tokens.json is sibling of the skill folder.
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
  const defaultTokens = path.resolve(__dirname, '../../tokens.json');
  const tokensPath = validation.args.tokensFile ?? defaultTokens;
  const tokens = await loadTokens(tokensPath);

  const report = validation.args.mode === 'apply'
    ? await apply(validation.args, tokens)
    : await audit(validation.args, tokens);

  const valid = report.counts.errors === 0;
  const rulesRun = validation.args.rules.length > 0
    ? validation.args.rules
    : tokens.hardRules.map((r) => r.id);

  const nextSteps: string[] = [];
  if (report.counts.errors > 0) {
    nextSteps.push(
      `${report.counts.errors} ERROR finding${report.counts.errors > 1 ? 's' : ''} — blocking UI polish gate.`,
    );
    if (validation.args.mode === 'audit') {
      const fixable = report.findings.filter((f) => f.autoFixable).length;
      if (fixable > 0) {
        nextSteps.push(`Run with --mode apply to auto-fix ${fixable} mechanical issue(s).`);
      }
    }
  }
  if (validation.args.mode === 'apply' && 'fixesApplied' in report) {
    nextSteps.push(
      `Applied ${report.fixesApplied} fix(es) across ${report.filesModified.length} file(s). Re-run --mode audit to verify.`,
    );
    if (report.fixesSkipped.length > 0) {
      nextSteps.push(
        `${report.fixesSkipped.length} finding(s) were auto-fixable in tokens.json but could not be rewritten mechanically (file parse issue or ambiguous context). Claude should handle those.`,
      );
    }
  }

  const output: UiPolishOutput = {
    success: true,
    command: 'ui-polish',
    data: { mode: validation.args.mode, rulesRun, valid },
    report,
    filesModified: 'filesModified' in report ? report.filesModified : [],
    errors: [],
    warnings: [],
    nextSteps,
  };
  console.log(JSON.stringify(output, null, 2));
  process.exit(valid ? 0 : 1);
} catch (err) {
  const message = err instanceof Error ? err.message : String(err);
  const output: UiPolishOutput = {
    success: false,
    command: 'ui-polish',
    data: { mode: validation.args.mode, rulesRun: [], valid: false },
    report: null,
    filesModified: [],
    errors: [`Unexpected error: ${message}`],
    warnings: [],
    nextSteps: [],
  };
  console.log(JSON.stringify(output, null, 2));
  process.exit(1);
  }
})();
