/**
 * Fact-loader engine (D0091).
 *
 * The stable, table-agnostic half of the loader. Given a registry of
 * `FactTableDescriptor`s and the two ports, it — per run — reads the manifest,
 * fetches only the artifacts the active descriptors declare, runs every
 * descriptor whose sources are all present, validates each emitted row against
 * the descriptor's `rowSchema`, and groups rows per table. It then ensures
 * each table and MERGEs the accumulated rows.
 *
 * It contains no table or column names: everything specific comes from the
 * descriptors. Adding a table or a column never touches this file.
 *
 * Error posture (design § Error handling): per-run isolation. A run that
 * cannot be read or whose rows fail `rowSchema` is skipped-and-reported; the
 * batch continues. A run missing an artifact type simply produces zero rows
 * for the tables that need it — absence is normal, not an error.
 *
 * Pure of I/O specifics: it awaits ports, never touches GCS/BigQuery directly.
 *
 * @see docs/decisions/D0091-bigquery-analytics-fact-loader.md
 * @see docs/design-docs/bigquery-analytics-fact-loader.md
 */
import type { BigQueryFactStore } from "../ports/bigquery-fact-store.js";
import type { Logger } from "../ports/logger.js";
import type { RunArtifactReader } from "../ports/run-artifact-reader.js";
import type { RunId } from "../types/branded-ids.js";
import { type FactTableRegistry } from "./descriptor.js";
/** Collaborators for a load run. All injected — the engine wires nothing. */
export interface FactLoaderDeps {
    readonly reader: RunArtifactReader;
    readonly store: BigQueryFactStore;
    readonly registry: FactTableRegistry;
    /** Optional — skip reasons and a summary line are logged when present. */
    readonly logger?: Logger;
}
/** What to load: the run ids to project (backfill = all report-runs). */
export interface LoadFactsInput {
    readonly runIds: readonly RunId[];
}
/** Per-run outcome. Skipped runs carry the reason; loaded runs the row total. */
export interface RunLoadOutcome {
    readonly runId: RunId;
    readonly status: "loaded" | "skipped";
    /** Present when `status === "skipped"`. */
    readonly reason?: string;
    /** Rows extracted from this run (present when `status === "loaded"`). */
    readonly rows?: number;
}
/** Per-table MERGE summary. */
export interface TableLoadSummary {
    readonly table: string;
    readonly dataset: string;
    readonly inserted: number;
    readonly updated: number;
}
/** Aggregate result of a load run. */
export interface LoadFactsResult {
    readonly loaded: number;
    readonly skipped: number;
    readonly outcomes: readonly RunLoadOutcome[];
    readonly tables: readonly TableLoadSummary[];
}
/**
 * Project the declared fact tables from a set of runs into the store.
 *
 * Runs are processed independently: a failure in one is recorded and skipped,
 * never aborting the batch. Rows from successful runs are grouped per table and
 * MERGEd once per table (batch load) so re-running is idempotent.
 */
export declare function loadFacts(deps: FactLoaderDeps, input: LoadFactsInput): Promise<LoadFactsResult>;
