/**
 * validate.ts — Input validation for the create-ba-order CLI.
 *
 * Stage 1: Zod structural validation.
 * Stage 2: Business validation (BA root exists, module references valid).
 */
import path from 'node:path';
import { directoryExists, fileExists } from '../../../../lib/fs.js';
import { BaOrderInputSchema } from './types.js';
import type { BaOrderInput, ValidationResult, ModuleKey } from './types.js';
import { moduleKey } from './types.js';

/**
 * Validate the raw input.
 * Checks structure (Zod) then business rules (directories exist, no dangling refs).
 */
export async function validate(raw: unknown): Promise<ValidationResult> {
  const errors: string[] = [];
  const warnings: string[] = [];

  // --- Stage 1: Zod ---
  const parsed = BaOrderInputSchema.safeParse(raw);
  if (!parsed.success) {
    const zodErrors = parsed.error.issues.map(
      (i) => `${i.path.join('.')}: ${i.message}`,
    );
    return { valid: false, errors: zodErrors, warnings };
  }

  const spec = parsed.data;

  // --- Stage 2: Business validation ---

  // Check baRoot exists
  const baRootExists = await directoryExists(spec.baRoot);
  if (!baRootExists) {
    errors.push(`BA root directory not found: ${spec.baRoot}`);
    return { valid: false, errors, warnings };
  }

  // Build the set of declared module keys
  const declaredKeys = new Set<ModuleKey>();
  for (const mod of spec.modules) {
    declaredKeys.add(moduleKey(mod.appCode, mod.moduleCode));
  }

  // Check each module's directory exists and dependencies are valid
  for (const mod of spec.modules) {
    const modDir = path.join(spec.baRoot, mod.appCode, mod.moduleCode);
    const indexPath = path.join(modDir, 'index.md');
    const hasIndex = await fileExists(indexPath);

    if (!hasIndex) {
      errors.push(
        `Module directory or index.md not found: ${modDir} (${mod.appCode}/${mod.moduleCode})`,
      );
    }

    // Verify all dependsOn references point to declared modules
    for (const dep of mod.dependsOn) {
      if (!declaredKeys.has(dep as ModuleKey)) {
        // Not in scope — could be valid (external) or a typo
        const depDir = path.join(spec.baRoot, ...dep.split('/'));
        const depExists = await directoryExists(depDir);
        if (!depExists) {
          warnings.push(
            `${mod.appCode}/${mod.moduleCode} depends on ${dep} which is not in scope and not found on disk`,
          );
        }
      }
    }
  }

  if (errors.length > 0) {
    return { valid: false, spec, errors, warnings };
  }

  return { valid: true, spec, errors, warnings };
}
