/**
 * cli:build-manifest — validate.ts
 */
import { BuildManifestInputSchema, type BuildManifestInput, type ValidationResult } from './types.js';

export function validate(raw: unknown): ValidationResult & { data?: BuildManifestInput } {
  const result = BuildManifestInputSchema.safeParse(raw);
  if (!result.success) {
    const errors = result.error.issues.map((i) => `[${i.path.join('.')}] ${i.message}`);
    return { valid: false, errors, warnings: [] };
  }

  const spec = result.data;
  const errors: string[] = [];
  const warnings: string[] = [];

  for (const e of spec.entities) {
    const allRoles = new Set([
      ...e.rolesWithRead,
      ...e.rolesWithCreate,
      ...e.rolesWithUpdate,
      ...e.rolesWithDelete,
      ...e.rolesWithoutAccess,
    ]);

    if (allRoles.size === 0) {
      warnings.push(`Entity ${e.name} has no roles defined — no tests will be emitted for it.`);
    }

    if (e.rolesWithCreate.length > 0 && Object.keys(e.fixture).length === 0) {
      warnings.push(`Entity ${e.name} has create roles but no fixture — form-submit tests will submit an empty form (likely fail validators).`);
    }

    // Sanity: rolesWithoutAccess shouldn't overlap with read roles
    const readSet = new Set(e.rolesWithRead);
    for (const role of e.rolesWithoutAccess) {
      if (readSet.has(role)) {
        errors.push(`Entity ${e.name}: role "${role}" is in both rolesWithRead and rolesWithoutAccess — pick one.`);
      }
    }
  }

  return { valid: errors.length === 0, errors, warnings, data: spec };
}
