/**
 * Auto-generate RuleId type from filesystem
 *
 * Scans src/rules/**\/*.ts files and generates:
 * - RuleId union type
 * - ALL_RULE_IDS constant array
 * - isRuleId() type guard
 *
 * This eliminates manual type maintenance and ensures types
 * stay in sync with actual rule files.
 *
 * Usage: npm run generate:types
 */

import { glob } from 'glob';
import { writeFile } from 'fs/promises';
import { basename, join } from 'path';
import { log } from '../util/logger';

/**
 * Generate RuleId type from discovered rule files
 */
async function generateRuleIds(): Promise<void> {
  const rulesDir = join(__dirname, '../../src/rules');

  log.info(`Scanning for rule files in: ${rulesDir}`);

  // Find all rule files (exclude tests, index, and rule-ids itself)
  const ruleFiles = (await glob('**/*.ts', {
    cwd: rulesDir,
    ignore: ['**/*.test.ts', '**/index.ts', '**/rule-ids.ts', '**/*.d.ts'],
  })).sort();

  log.info(`Found ${ruleFiles.length} rule files`);

  // Extract rule IDs from filenames and sort
  const ruleIds = ruleFiles.map((file) => basename(file, '.ts')).sort();

  // Generate TypeScript content
  const content = generateTypeScriptContent(ruleIds);

  // Write to src/rules/rule-ids.ts
  const outputPath = join(rulesDir, 'rule-ids.ts');
  await writeFile(outputPath, content, 'utf-8');

  log.info(`Generated ${ruleIds.length} rule IDs in ${outputPath}`);

  // Generate auto-registration file
  const registrationContent = generateRegistrationContent(ruleFiles);
  const registrationPath = join(rulesDir, 'index.ts');
  await writeFile(registrationPath, registrationContent, 'utf-8');

  log.info(`Generated auto-registration in ${registrationPath}`);
  log.info(`Rule IDs: ${ruleIds.join(', ')}`);
}

/**
 * Generate auto-registration content for index.ts
 */
function generateRegistrationContent(ruleFiles: string[]): string {
  // Generate import statements for each rule file
  const imports = ruleFiles
    .map((file) => {
      const id = basename(file, '.ts');
      const path = './' + file.replace(/\.ts$/, '').replace(/\\/g, '/');
      const varName = id.replace(/-/g, '_') + '_rule';
      return `import { rule as ${varName} } from '${path}';`;
    })
    .join('\n');

  // Generate registration calls
  const registrations = ruleFiles
    .map((file) => {
      const id = basename(file, '.ts');
      const varName = id.replace(/-/g, '_') + '_rule';
      return `RuleRegistry.register(${varName});`;
    })
    .join('\n');

  return `/**
 * Auto-generated Rule Registration
 *
 * DO NOT EDIT MANUALLY
 *
 * This file is auto-generated by scripts/generate-rule-types.ts
 * Run 'npm run generate:types' to regenerate
 *
 * @generated
 * Rules registered: ${ruleFiles.length}
 */

import { RuleRegistry } from '../utils/rules/registry';

// Import all rule files
${imports}

// Register all rules with the registry
// This happens automatically when this module is imported
${registrations}

// Export all rules for direct access if needed
export {
${ruleFiles
  .map((file) => {
    const id = basename(file, '.ts');
    const varName = id.replace(/-/g, '_') + '_rule';
    return `  ${varName},`;
  })
  .join('\n')}
};
`;
}

/**
 * Generate the TypeScript content for rule-ids.ts
 */
function generateTypeScriptContent(ruleIds: string[]): string {
  // Handle case of no rules (during migration)
  const ruleIdType =
    ruleIds.length > 0
      ? `export type RuleId =\n${ruleIds.map((id) => `  | '${id}'`).join('\n')};`
      : `export type RuleId = string; // No rules discovered yet`;

  const ruleIdArray =
    ruleIds.length > 0
      ? `export const ALL_RULE_IDS: readonly RuleId[] = [\n${ruleIds.map((id) => `  '${id}',`).join('\n')}\n] as const;`
      : `export const ALL_RULE_IDS: readonly RuleId[] = [] as const;`;

  return `/**
 * Auto-generated Rule IDs
 *
 * DO NOT EDIT MANUALLY
 *
 * This file is auto-generated by scripts/generate-rule-types.ts
 * Run 'npm run generate:types' to regenerate
 *
 * @generated
 * Rule count: ${ruleIds.length}
 */

/**
 * Union of all rule IDs
 *
 * This type is automatically generated from discovered rule files.
 * Each rule file in src/rules/**\\/*.ts becomes a valid RuleId.
 *
 * NOTE: Some rules (cross-file validation) have their validation logic
 * implemented in validators rather than in their validate() function,
 * as they require stateful context across multiple files.
 */
${ruleIdType}

/**
 * All valid rule IDs as a readonly array
 *
 * Useful for runtime validation and iteration.
 */
${ruleIdArray}

/**
 * Type guard to check if a string is a valid RuleId
 *
 * @param value - String to check
 * @returns True if value is a valid RuleId
 *
 * @example
 * if (isRuleId(userInput)) {
 *   // TypeScript knows userInput is RuleId
 *   const rule = await ruleLoader.getRule(userInput);
 * }
 */
export function isRuleId(value: string): value is RuleId {
  return (ALL_RULE_IDS as readonly string[]).includes(value);
}
`;
}

/**
 * Main execution
 */
generateRuleIds()
  .then(() => {
    log.info('Rule type generation complete');
    process.exit(0);
  })
  .catch((error) => {
    log.error(`Failed to generate rule types: ${error}`);
    process.exit(1);
  });
