/**
 * Copyright: (c) Myia SAS 2026.
 * This file and its contents are licensed under the AGPLv3 License.
 * Please see the LICENSE file at the root of this repository
 */
import type { FullConfig, Reporter, TestCase, TestResult } from '@playwright/test/reporter';
import type { TestAttachmentsRecord, TestResultRecord } from '../../domain/trace-event-recorder/model/statement-trace-schema';
import { CrashErrorClassifier } from './crash-error-classifier';
import { FailingStatementFinder } from './failing-statement-finder';
import { NdjsonTailInspector } from './ndjson-tail-inspector';
/**
 * Subdirectory under each project's `outputDir` where the fixture
 * writes a registry entry per (testId, attempt). Shared string
 * constant between fixture (writer) and reporter (reader).
 */
export declare const HEAL_PENDING_SUBDIR = ".heal-pending";
/**
 * One entry of the `videoPages` array the fixture writes into the
 * registry at teardown. Carries the per-page identity the reporter
 * needs to stamp `pageName` / `pageUrl` onto the corresponding video
 * attachment.
 *
 * No file-path field: Playwright renames each recording from its
 * `.playwright-artifacts-N/<hash>.webm` recording location to a
 * deterministic `<outputDir>/video[-N].webm` AFTER the fixture's
 * teardown but BEFORE the reporter's `onTestEnd`, so any path the
 * fixture could capture is stale by the time the reporter sees it.
 *
 * The join is positional instead: the fixture pushes entries in the
 * same order as `page.context().pages()`, and the reporter pairs the
 * Nth video attachment in `result.attachments` with `videoPages[N]`.
 */
export interface VideoPageInfo {
    /** Synthetic role label: `'main'`, `'page-1'`, `'page-2'`, … */
    name: string;
    /** Page URL captured at fixture teardown. */
    url: string;
}
/**
 * Shape of the per-test registry entry. Minimal: everything else
 * the reporter needs is reachable from `TestCase` / `TestResult`.
 */
export interface HealTraceContext {
    /** Absolute path to the per-test `heal-traces.ndjson`. */
    ndjsonPath: string;
    /**
     * Absolute path to the per-(test, attempt) directory inside the
     * persistent `heal-traces/` tree — the destination root the reporter
     * copies Playwright artefacts into. Reporter-emitted
     * `test-attachments` paths are relative to this directory.
     */
    rootDir: string;
    /**
     * Absolute `testInfo.outputDir` — the source directory Playwright
     * writes its own attachments into (`trace.zip`, `video.webm`,
     * failure screenshots, user `testInfo.attach()` files).
     */
    playwrightOutputDir: string;
    /** Per-process executionId at the time the fixture wrote the entry. */
    executionId: string;
    /**
     * Video → page map the fixture appends at teardown (overwriting the
     * setup-time entry). Absent when the test recorded no video or
     * crashed before teardown could capture it.
     */
    videoPages?: VideoPageInfo[];
}
/**
 * Context handed to the `onRescue` hook. Extends `HealTraceContext`
 * with the per-test correlation fields the hook needs to route the
 * synthetic record.
 */
export interface RescueContext extends HealTraceContext {
    /** Playwright `TestCase.id` — stable hash of (file, title, project). */
    testId: string;
    /** 1-indexed attempt number = `TestResult.retry + 1`. */
    attempt: number;
    /** `TestResult.workerIndex` where the crash happened. */
    workerIndex: number;
}
/**
 * Invoked after the reporter has appended its synthetic
 * `test-result` to the NDJSON. Intended for extensions that need to
 * forward the rescued record to a live destination the in-worker
 * exporters never got to reach — anything running per-test inside
 * the worker dies with it on a crash.
 *
 * Errors are caught by the reporter and logged to `process.stderr`;
 * a failing hook never breaks the Playwright run or prevents the
 * on-disk append from landing.
 */
export type RescueHook = (record: TestResultRecord, ctx: RescueContext) => void | Promise<void>;
/**
 * Context handed to the `onAttachmentsWritten` hook. Same fields as
 * `RescueContext` — the hook needs the same per-test correlation —
 * but kept as its own type so consumers don't have to import an
 * unrelated name (`RescueContext`) to use a non-rescue hook.
 */
export interface AttachmentsContext extends HealTraceContext {
    /** Playwright `TestCase.id` — stable hash of (file, title, project). */
    testId: string;
    /** 1-indexed attempt number = `TestResult.retry + 1`. */
    attempt: number;
    /** `TestResult.workerIndex` the test ran on. */
    workerIndex: number;
}
/**
 * Invoked synchronously after the reporter has appended a
 * `test-attachments` record to the NDJSON. The record is the same
 * one written to disk — including any video `pageName`/`pageUrl`
 * already resolved by `copyAndBuildAttachmentsRecord`. Intended for
 * extensions that need to forward the attachment list (or its
 * referenced files) to a live destination — typically a remote
 * collector that uploads artefacts to object storage.
 *
 * The hook may return a Promise. Returned promises are tracked and
 * awaited by the reporter's `onEnd` so the trailing test's work
 * isn't lost when Playwright exits. Errors are caught and logged
 * to stderr; a failing hook never breaks the Playwright run.
 *
 * Fired for every test, including those whose `test-result` was
 * synthesized by the crash-rescue path — so crashed tests' artefacts
 * are still shipped through the hook.
 */
export type AttachmentsHook = (record: TestAttachmentsRecord, ctx: AttachmentsContext) => void | Promise<void>;
export interface HealTracerReporterDeps {
    classifier?: CrashErrorClassifier;
    inspector?: NdjsonTailInspector;
    failingStatementFinder?: FailingStatementFinder;
    /**
     * Seam for tests — defaults to `fs.appendFileSync`. Kept sync so
     * the synthetic line hits disk before the reporter returns, same
     * crash-safety discipline as `NdjsonExporter`.
     */
    appendFile?: (path: string, data: string) => void;
    onRescue?: RescueHook;
    onAttachmentsWritten?: AttachmentsHook;
}
/** Path to the registry file for a given (project, test, attempt). */
export declare function healPendingRegistryPath(projectOutputDir: string, testId: string, attempt: number): string;
export declare class HealTracerReporter implements Reporter {
    private readonly classifier;
    private readonly inspector;
    private readonly failingStatementFinder;
    private readonly appendFile;
    private readonly onRescue;
    private readonly onAttachmentsWritten;
    private readonly pendingAttachmentsHooks;
    private readonly stderrByWorker;
    private projectOutputDirs;
    private executionId;
    private executionSource;
    private startedAt;
    private rootDir;
    private playwrightVersion;
    private gitInfo;
    private readonly testEntriesById;
    constructor(deps?: HealTracerReporterDeps);
    printsToStdio(): boolean;
    onBegin(config: FullConfig, _suite: unknown): void;
    onTestBegin(_test: TestCase, result: TestResult): void;
    onStdErr(chunk: string | Buffer, _test: void | TestCase, result: void | TestResult): void;
    onTestEnd(test: TestCase, result: TestResult): void;
    private resolveTraceContext;
    /**
     * Copy each Playwright attachment from its `outputDir` location
     * into the persistent heal-traces tree (under `traceCtx.rootDir`),
     * mapping by `name` / `contentType`:
     *
     *   - 'trace'                     → <rootDir>/trace.zip
     *   - contentType: 'video/...'    → <rootDir>/videos/<basename>
     *   - 'screenshot' + image/...    → <rootDir>/screenshots/<basename>
     *   - other (user attachments)    → <rootDir>/<rel-from-outputDir>
     *
     * Returns a `test-attachments` record whose `path` fields are the
     * destination-relative locations using forward slashes, so the
     * local-viewer-server can serve them via `/api/asset/...`.
     *
     * Attachments whose source path falls outside Playwright's
     * outputDir are dropped — those would not be safely servable.
     */
    private copyAndBuildAttachmentsRecord;
    private placeAttachment;
    private cleanupRegistry;
    private buildSyntheticTestResult;
    private invokeRescueHook;
    private invokeAttachmentsHook;
    /**
     * Upsert the per-test entry for `test` and append an attempt slice
     * built from `result`. When `ndjsonPath` is non-null and the
     * attempt did not pass, scans the file for the deepest threw
     * statement and attaches it as `failingStatement` + `error`. A
     * `rescuedRecord` (synthesized by the rescue path) supplies
     * `error` when no statement-level threw is present.
     * `failureScreenshot` (the heal-traces-relative path to
     * Playwright's failure screenshot, already copied into the tree)
     * is attached independently whenever the attempt did not
     * pass/skip and Playwright produced one.
     */
    private captureAttempt;
    private pickFailureScreenshot;
    private upsertTestEntry;
    private mapTestStatus;
    onEnd(): Promise<void>;
}
