/**
 * cli:build-manifest — generate.ts
 *
 * For each entity × role matrix, emit one test entry per scenario.
 * Output: tests/ui-test/manifest.json (single file).
 */
import type {
  BuildManifestInput,
  ManifestEntity,
  ManifestTest,
  Manifest,
  GeneratedFile,
} from './types.js';

export function generate(spec: BuildManifestInput): GeneratedFile[] {
  const tests: ManifestTest[] = [];

  for (const entity of spec.entities) {
    tests.push(...buildEntityTests(spec, entity));
  }

  const manifest: Manifest = {
    generatedAt: new Date().toISOString(),
    module: spec.module,
    appCode: spec.appCode,
    tests,
  };

  return [
    {
      path: 'tests/ui-test/manifest.json',
      content: JSON.stringify(manifest, null, 2) + '\n',
    },
  ];
}

function buildEntityTests(spec: BuildManifestInput, entity: ManifestEntity): ManifestTest[] {
  const tests: ManifestTest[] = [];
  const plural = entity.plural ?? entity.section;
  const basePath = `/${spec.module}/${plural}`;
  const idPlaceholder = '__detail_id__'; // resolved at runtime by the runner from list.first

  // ─── List + smoke (one test per role with read) ────────
  for (const role of entity.rolesWithRead) {
    tests.push({
      id: `${spec.module}.${entity.section}.list.${role}`,
      scenario: 'list',
      page: basePath,
      role,
      expectations: {
        httpStatus: [200],
        noConsoleError: true,
        rendersTestId: `${entity.section}-list-table`,
      },
    });

    tests.push({
      id: `${spec.module}.${entity.section}.detail.${role}`,
      scenario: 'detail',
      page: `${basePath}/${idPlaceholder}`,
      role,
      expectations: {
        httpStatus: [200],
        noConsoleError: true,
        rendersTestId: `${entity.section}-detail`,
      },
    });
  }

  // ─── Form-submit per role with create ──────────────────
  for (const role of entity.rolesWithCreate) {
    tests.push({
      id: `${spec.module}.${entity.section}.create.${role}`,
      scenario: 'form-submit',
      page: `${basePath}/create`,
      role,
      fixture: entity.fixture,
      expectations: {
        httpStatus: [201, 200],
        redirectsTo: `${basePath}/:id`,
      },
    });
  }

  // ─── Edit per role with update ──────────────────────────
  for (const role of entity.rolesWithUpdate) {
    tests.push({
      id: `${spec.module}.${entity.section}.edit.${role}`,
      scenario: 'edit',
      page: `${basePath}/${idPlaceholder}/edit`,
      role,
      fixture: entity.fixture,
      expectations: {
        httpStatus: [200, 204],
      },
    });
  }

  // ─── Delete per role with delete ────────────────────────
  for (const role of entity.rolesWithDelete) {
    tests.push({
      id: `${spec.module}.${entity.section}.delete.${role}`,
      scenario: 'delete',
      page: `${basePath}/${idPlaceholder}`,
      role,
      expectations: {
        httpStatus: [204, 200],
      },
    });
  }

  // ─── Permission-negative per role without access ───────
  for (const role of entity.rolesWithoutAccess) {
    tests.push({
      id: `${spec.module}.${entity.section}.list.${role}-denied`,
      scenario: 'permission-negative',
      page: basePath,
      role,
      expectations: {
        // Either the API rejects with 403 OR the frontend PermissionGuard
        // renders a denied placeholder. The template handles both.
        httpStatus: [403],
        rendersTestId: `${entity.section}-permission-denied`,
      },
    });
  }

  return tests;
}
