/**
 * lib/frontend-fixers.ts — Shared mechanical rewrite helpers for *.tsx pages.
 *
 * Used by both `ui-polish/apply.ts` (CSS / token rewrites) and
 * `audit-dev-frontend/apply.ts` (PRD-vs-code rewrites). Keeping the shared
 * fixers here avoids divergence between the two skills' implementations.
 *
 * Each fixer is a pure function: `(source, optsByRule) → string | null`.
 * Returning null = "couldn't apply safely; mark as skipped".
 */

// ─── Helpers ──────────────────────────────────────────────────────────────

/**
 * Walk forward from after the opening JSX tag and find the matching `);` of
 * the surrounding return(...). Returns the absolute offset of the `;` (or
 * the closing `)` if no `;`). Returns -1 if no balanced close found.
 */
export function findMatchingReturnClose(text: string): number {
  let depth = 1; // we already consumed the opening tag
  let i = 0;
  while (i < text.length) {
    const c = text[i];
    if (c === '<') {
      if (text[i + 1] === '/') depth--;
      else if (text[i + 1] !== '!') depth++;
    }
    if (depth === 0) {
      const close = text.slice(i).match(/>\s*\)\s*;?/);
      if (!close) return -1;
      return i + close.index! + close[0].length;
    }
    i++;
  }
  return -1;
}

/** Inject an import line after the last existing top-level import. */
export function injectImport(source: string, importLine: string): string {
  if (source.includes(importLine)) return source;
  const lastImport = source.match(/^import[\s\S]*?from\s+['"][^'"]+['"];?\s*$/gm);
  if (lastImport && lastImport.length > 0) {
    const last = lastImport[lastImport.length - 1];
    const insertAt = source.indexOf(last) + last.length;
    return source.slice(0, insertAt) + `\n${importLine}` + source.slice(insertAt);
  }
  return importLine + '\n' + source;
}

// ─── Fixers ───────────────────────────────────────────────────────────────

/**
 * Wrap the page body in <PageTemplate title={...}>...</PageTemplate>.
 *
 * - Skips if PageTemplate is already imported and used.
 * - Inserts the import after the last existing import.
 * - Heuristic JSX wrap: replace `return ( <root>` with
 *   `return ( <PageTemplate title={'TODO-title'}> <root>` and rebalance close.
 * - Returns null if no JSX return found (page shape too exotic to patch).
 *
 * Equivalent to ui-polish's fixR1; extracted so audit-dev-frontend reuses it
 * for DEV-UI-005.
 */
export function fixPageTemplateWrap(source: string): string | null {
  if (!/from\s+['"]@\/components\/ui\/PageTemplate['"]/.test(source)) {
    source = injectImport(source, `import { PageTemplate } from '@/components/ui/PageTemplate';`);
  }
  if (/<PageTemplate[\s>]/.test(source)) {
    // Already wraps, just return source as-is (import was inserted if missing).
    return source;
  }
  const returnMatch = source.match(/return\s*\(\s*\n(\s*)(<(?!PageTemplate\b|>|\/)[^>]+>)/);
  if (!returnMatch) return null;
  const indent = returnMatch[1];
  const bodyOpen = returnMatch[2];
  const beforeReturn = source.slice(0, returnMatch.index! + returnMatch[0].length - bodyOpen.length);
  const afterBodyOpen = source.slice(returnMatch.index! + returnMatch[0].length);
  const closingParenIdx = findMatchingReturnClose(afterBodyOpen);
  if (closingParenIdx === -1) return null;
  const bodyInner = afterBodyOpen.slice(0, closingParenIdx);
  const rest = afterBodyOpen.slice(closingParenIdx);
  return (
    beforeReturn
    + `<PageTemplate title={/* TODO: derive from useTranslation */ 'TODO-title'}>\n${indent}  ${bodyOpen}`
    + bodyInner.replace(/\n/g, '\n  ')
    + `\n${indent}</PageTemplate>`
    + rest
  );
}

/**
 * Wrap the page body in <PermissionGuard permission="<key>">...</PermissionGuard>.
 *
 * Same shape as fixPageTemplateWrap but for PermissionGuard. If the page
 * already wraps in <PageTemplate>, the guard goes outside (Permission first,
 * then PageTemplate). Used for DEV-UI-011.
 */
export function fixPermissionGuardWrap(source: string, permission: string): string | null {
  if (new RegExp(`PermissionGuard[^>]*permission=\\{?["'\`]${permission.replace(/\./g, '\\.')}["'\`]`).test(source)) {
    return source; // already wrapped with this exact permission
  }
  if (!/from\s+['"]@\/components\/PermissionGuard['"]/.test(source) && !/PermissionGuard.*from\s+['"]@atlashub\/smartstack['"]/.test(source)) {
    source = injectImport(source, `import { PermissionGuard } from '@/components/PermissionGuard';`);
  }
  const returnMatch = source.match(/return\s*\(\s*\n(\s*)(<(?!PermissionGuard\b|>|\/)[^>]+>)/);
  if (!returnMatch) return null;
  const indent = returnMatch[1];
  const bodyOpen = returnMatch[2];
  const beforeReturn = source.slice(0, returnMatch.index! + returnMatch[0].length - bodyOpen.length);
  const afterBodyOpen = source.slice(returnMatch.index! + returnMatch[0].length);
  const closingParenIdx = findMatchingReturnClose(afterBodyOpen);
  if (closingParenIdx === -1) return null;
  const bodyInner = afterBodyOpen.slice(0, closingParenIdx);
  const rest = afterBodyOpen.slice(closingParenIdx);
  return (
    beforeReturn
    + `<PermissionGuard permission="${permission}">\n${indent}  ${bodyOpen}`
    + bodyInner.replace(/\n/g, '\n  ')
    + `\n${indent}</PermissionGuard>`
    + rest
  );
}

/**
 * Append a `PageRegistry.register(key, lazyWithRetry(() => import(importPath)))`
 * line to the registry file source, AFTER the last existing register call (or
 * at the end of the file if none exist).
 *
 * Used for DEV-UI-002 in-place fix when an orphan page exists and the
 * appropriate module registry is identified. Uses `lazyWithRetry` (not bare
 * `React.lazy`) for parity with scaffold-routes output — survives transient
 * HMR/CDN chunk-load failures.
 */
export function appendRegistryEntry(source: string, key: string, importPath: string): string {
  const registerLine = `PageRegistry.register('${key}', lazyWithRetry(() => import('${importPath}')));`;
  if (source.includes(registerLine)) return source;
  const lastRegister = [...source.matchAll(/(?:Page|Component)Registry\.register\s*\([^;]+;\s*$/gm)];
  if (lastRegister.length > 0) {
    const last = lastRegister[lastRegister.length - 1];
    const insertAt = last.index! + last[0].length;
    return source.slice(0, insertAt) + `\n${registerLine}` + source.slice(insertAt);
  }
  // No existing register call — append before the last closing brace if any.
  if (!/import\s*\{[^}]*\b(?:lazy|lazyWithRetry)\b[^}]*\}\s*from/.test(source)) {
    source = injectImport(source, `import { lazyWithRetry } from '@atlashub/smartstack';`);
  }
  if (!/import\s*\{[^}]*\bPageRegistry\b[^}]*\}\s*from/.test(source)) {
    source = injectImport(source, `import { PageRegistry } from '@atlashub/smartstack';`);
  }
  return source.trimEnd() + '\n\n' + registerLine + '\n';
}
