/**
 * cli:run-ui-test — execute.ts
 *
 * Reads manifest + users, runs each test via dev-browser-driver, returns
 * an aggregated report. NO persistence here — the Studio runner consumes
 * the envelope and calls uiTestService for DB rows.
 *
 * Auto-correction is also out of scope for this CLI — it's the Studio
 * runner's responsibility (Vague 7). The CLI runs each test EXACTLY ONCE.
 */
import path from 'node:path';
import { promises as fs } from 'node:fs';
import {
  runDevBrowserTest,
  type DriverTestSpec,
  type DriverTestUser,
} from './lib/dev-browser-driver.js';
import type { RunUiTestInput, RunUiTestResult, RunUiTestReport } from './types.js';

interface ManifestFile {
  generatedAt: string;
  module: string;
  appCode: string;
  tests: Array<{
    id: string;
    scenario: DriverTestSpec['scenario'];
    page: string;
    role: string;
    fixture?: Record<string, unknown>;
    expectations: DriverTestSpec['expectations'];
  }>;
}

interface UsersFile {
  generatedAt: string;
  module: string;
  appCode: string;
  users: Array<{
    role: string;
    email: string;
    password: string;
    applicationCode: string;
    roleName: string;
  }>;
}

export async function execute(spec: RunUiTestInput): Promise<RunUiTestReport> {
  const manifestAbs = path.resolve(spec.projectPath, spec.manifestPath);
  const usersAbs = path.resolve(spec.projectPath, spec.usersPath);

  const manifest: ManifestFile = JSON.parse(await fs.readFile(manifestAbs, 'utf8'));
  const usersFile: UsersFile = JSON.parse(await fs.readFile(usersAbs, 'utf8'));

  const usersByRole = new Map<string, DriverTestUser>();
  for (const u of usersFile.users) {
    usersByRole.set(u.role, { role: u.role, email: u.email, password: u.password });
  }

  // Filter tests by --test-id and --module if provided.
  let tests = manifest.tests;
  if (spec.testId) tests = tests.filter((t) => t.id === spec.testId);
  if (spec.module && spec.module !== manifest.module) {
    return {
      module: manifest.module,
      appCode: manifest.appCode,
      totalTests: 0,
      passed: 0,
      failed: 0,
      durationMs: 0,
      results: [],
    };
  }

  const startedAt = Date.now();
  const results: RunUiTestResult[] = [];

  for (const t of tests) {
    const user = usersByRole.get(t.role);
    if (!user) {
      results.push({
        testId: t.id,
        scenario: t.scenario,
        page: t.page,
        role: t.role,
        status: 'fail',
        reason: `driver-error: no test user seeded for role "${t.role}"`,
        durationMs: 0,
        capture: { exitCode: null },
      });
      continue;
    }

    const driverResult = await runDevBrowserTest(
      {
        id: t.id,
        scenario: t.scenario,
        page: t.page,
        role: t.role,
        fixture: t.fixture,
        expectations: t.expectations,
      },
      user,
      {
        frontendUrl: spec.frontendUrl,
        apiUrl: spec.apiUrl,
        captureDir: spec.captureDir,
        timeoutMs: spec.timeoutMs,
        cwd: spec.projectPath,
      },
    );

    results.push({
      testId: t.id,
      scenario: t.scenario,
      page: t.page,
      role: t.role,
      status: driverResult.status,
      reason: driverResult.reason,
      durationMs: driverResult.durationMs,
      capture: driverResult.capture,
    });
  }

  const passed = results.filter((r) => r.status === 'pass').length;
  const failed = results.length - passed;

  return {
    module: manifest.module,
    appCode: manifest.appCode,
    totalTests: results.length,
    passed,
    failed,
    durationMs: Date.now() - startedAt,
    results,
  };
}
