export type DatasetRepresentationId =
  | "native-scan"
  | "normalized-scan"
  | "rolling-local-map";

export type DatasetSourceAdmissionStatus =
  | "blocked-storage-policy"
  | "ready-for-download"
  | "ready-for-smoke"
  | "downloading"
  | "downloaded"
  | "verifying"
  | "verified"
  | "frame-ready"
  | "smoke-ready";

export interface DatasetSource {
  sourceId: string;
  sourceKind: "goose" | "rellis";
  displayName: string;
  role: string;
  license: string;
  commercialUse:
    | "allowed-with-share-alike"
    | "research-only-license-review-required";
  format: string;
  frameSemantics: "one-lidar-revolution";
  sensor: string;
  platforms: string[];
  superclasses: string[];
  smokeExampleMb: number | null;
  primaryScanArchiveGb: number;
  labelArchiveGb: number | null;
  posesArchiveGb: number | null;
  fragmentCount: number;
  annotatedScanCount: number;
  admissionStatus: DatasetSourceAdmissionStatus;
  archive: {
    bytesTransferred: number;
    totalBytes: number;
    sizeBytes: number | null;
    sha256: string | null;
    integrity: string;
    vendorChecksumAvailable: false;
  } | null;
  frame: {
    frameId: string;
    pointCount: number;
    semanticClassCount: number;
    groundTruthGroundFraction: number;
    previewPointCount: number;
    previewSha256: string | null;
    previewAvailable: true;
  } | null;
}

export interface DatasetGatewayCatalog {
  storage: {
    configured: boolean;
    admitted: boolean;
    status: "ready" | "blocked-storage-policy";
    attestation: "worker-manifest" | "local-worker-path" | "none";
    manifestValid: boolean;
    requiredWindowsRoot: string;
    requiredWslRoot: string;
  };
  sources: DatasetSource[];
  representations: Array<{
    id: DatasetRepresentationId;
    title: string;
    purpose: string;
    accumulation: boolean;
  }>;
  pipeline: Array<{
    stage: string;
    requires: string[];
    produces: string;
  }>;
  currentInput: {
    representation: "vendor-mapped-increment";
    nativeScan: false;
    perPointTime: false;
    ringOrLine: false;
    admittedForPatchworkpp: false;
    reason: string;
  };
  nextAction: string;
}

export interface DatasetNativeScanPreview {
  sourceId: "goose-3d/v2025-08-22" | "rellis-3d/v1.1";
  frameId: string;
  sourcePointCount: number;
  pointCount: number;
  pointsXyzM: Array<[number, number, number]>;
  remission0To255: number[];
  semanticLabelIds: number[];
  semanticRgb0To255: number[];
  groundTruthGround: number[];
  evaluationMask: number[];
  classes: Array<{
    labelId: number;
    className: string;
    hex: string;
    challengeCategoryId: number | null;
    challengeCategoryName: string | null;
    groundTarget: "ground" | "non-ground" | "ignore" | null;
    sourcePointCount: number | null;
  }>;
  coordinateFrame: {
    frameId: string;
    x: "forward";
    y: "left";
    z: "up";
  } | null;
  compatibilitySmokeOnly: boolean;
}

export interface DatasetGroundMetrics {
  precision: number;
  recall: number;
  f1: number;
  groundIou: number;
  accuracy: number;
  artificialGroundRecall: number;
  naturalGroundRecall: number;
  obstacleNonGroundRecall: number;
}

export interface DatasetGroundComparison {
  sourceId: "goose-3d/v2025-08-22";
  frameId: string;
  pointCount: number;
  currentGround: number[];
  patchworkGround: number[];
  patchworkAssigned: number[];
  groundTruthGround: number[];
  evaluated: number[];
  currentDisagreement: number[];
  patchworkDisagreement: number[];
  metrics: {
    current: DatasetGroundMetrics;
    patchworkpp: DatasetGroundMetrics;
  };
  latencyMs: {
    current: number;
    patchworkpp: number;
  };
  patchworkAssignedFraction: number;
  inputProfile: {
    frameId: "sensor/lidar/vls128_roof";
    sensorHeightM: number;
    heightEvidence: "published-dimensioned-schematic";
    normalizedScanProduced: false;
  };
}

export class DatasetGatewayContractError extends Error {}

type DatasetFetch = (
  input: RequestInfo | URL,
  init?: RequestInit,
) => Promise<Response>;

const SAFE_ID = /^[a-z0-9][a-z0-9._:/-]{0,159}$/;

function record(value: unknown, label: string): Record<string, unknown> {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new DatasetGatewayContractError(`${label}: ожидался объект`);
  }
  return value as Record<string, unknown>;
}

function array(value: unknown, label: string): unknown[] {
  if (!Array.isArray(value)) {
    throw new DatasetGatewayContractError(`${label}: ожидался массив`);
  }
  return value;
}

function string(value: unknown, label: string, safe = false): string {
  if (
    typeof value !== "string"
    || !value
    || (safe && !SAFE_ID.test(value))
  ) {
    throw new DatasetGatewayContractError(`${label}: некорректная строка`);
  }
  return value;
}

function strings(value: unknown, label: string): string[] {
  return array(value, label).map((item, index) =>
    string(item, `${label}[${index}]`, true)
  );
}

function displayStrings(value: unknown, label: string): string[] {
  return array(value, label).map((item, index) =>
    string(item, `${label}[${index}]`)
  );
}

function boolean(value: unknown, label: string): boolean {
  if (typeof value !== "boolean") {
    throw new DatasetGatewayContractError(`${label}: ожидался boolean`);
  }
  return value;
}

function number(value: unknown, label: string): number {
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
    throw new DatasetGatewayContractError(`${label}: некорректное число`);
  }
  return value;
}

function nullableNumber(value: unknown, label: string): number | null {
  return value === null ? null : number(value, label);
}

function nullableDigest(value: unknown, label: string): string | null {
  if (value === null) return null;
  const digest = string(value, label);
  if (!/^[a-f0-9]{64}$/.test(digest)) {
    throw new DatasetGatewayContractError(`${label}: некорректный SHA-256`);
  }
  return digest;
}

function integers(
  value: unknown,
  label: string,
  maximum: number,
): number[] {
  return array(value, label).map((item, index) => {
    if (
      typeof item !== "number"
      || !Number.isInteger(item)
      || item < 0
      || item > maximum
    ) {
      throw new DatasetGatewayContractError(`${label}[${index}]: некорректное число`);
    }
    return item;
  });
}

export function parseDatasetGatewayCatalog(
  value: unknown,
): DatasetGatewayCatalog {
  const source = record(value, "Dataset Gateway");
  if (
    source.schema_version !== "missioncore.dataset-gateway-catalog/v3"
    || source.access !== "read-only"
  ) {
    throw new DatasetGatewayContractError("Dataset Gateway contract несовместим");
  }
  const storage = record(source.storage, "storage");
  const storageStatus = storage.status;
  if (storageStatus !== "ready" && storageStatus !== "blocked-storage-policy") {
    throw new DatasetGatewayContractError("storage.status: неизвестное значение");
  }
  if (storage.path_exposed !== false) {
    throw new DatasetGatewayContractError("Dataset Gateway раскрыл локальный путь");
  }
  const storageAttestation = storage.attestation;
  if (
    storageAttestation !== "worker-manifest"
    && storageAttestation !== "local-worker-path"
    && storageAttestation !== "none"
  ) {
    throw new DatasetGatewayContractError("storage.attestation: неизвестное значение");
  }
  const sources = array(source.sources, "sources");
  if (sources.length !== 2) {
    throw new DatasetGatewayContractError("Ожидались два закреплённых dataset source");
  }
  const parsedSources = sources.map((value, index): DatasetSource => {
    const dataset = record(value, `sources[${index}]`);
    const sourceKind = string(dataset.source_kind, "source_kind", true);
    const sourceId = string(dataset.source_id, "source_id", true);
    if (
      (sourceKind !== "goose" || sourceId !== "goose-3d/v2025-08-22")
      && (sourceKind !== "rellis" || sourceId !== "rellis-3d/v1.1")
    ) {
      throw new DatasetGatewayContractError("Dataset source identity неизвестна");
    }
    if (dataset.frame_semantics !== "one-lidar-revolution") {
      throw new DatasetGatewayContractError("Dataset frame semantics несовместима");
    }
    const download = record(dataset.download, `sources[${index}].download`);
    const statistics = record(dataset.statistics, `sources[${index}].statistics`);
    const admission = record(dataset.admission, `sources[${index}].admission`);
    if (download.automatic !== false) {
      throw new DatasetGatewayContractError(
        "Большой dataset нельзя загружать автоматически",
      );
    }
    const admissionStatus = admission.status;
    if (
      admissionStatus !== "ready-for-download"
      && admissionStatus !== "ready-for-smoke"
      && admissionStatus !== "blocked-storage-policy"
      && admissionStatus !== "downloading"
      && admissionStatus !== "downloaded"
      && admissionStatus !== "verifying"
      && admissionStatus !== "verified"
      && admissionStatus !== "frame-ready"
      && admissionStatus !== "smoke-ready"
    ) {
      throw new DatasetGatewayContractError("source admission status неизвестен");
    }
    const archive = admission.archive === null
      ? null
      : record(admission.archive, `sources[${index}].admission.archive`);
    const archiveValue = archive
      ? {
          bytesTransferred: number(
            archive.bytes_transferred,
            "archive.bytes_transferred",
          ),
          totalBytes: number(archive.total_bytes, "archive.total_bytes"),
          sizeBytes: nullableNumber(archive.size_bytes, "archive.size_bytes"),
          sha256: nullableDigest(archive.sha256, "archive.sha256"),
          integrity: string(archive.integrity, "archive.integrity", true),
          vendorChecksumAvailable: (() => {
            if (archive.vendor_checksum_available !== false) {
              throw new DatasetGatewayContractError(
                "archive.vendor_checksum_available: ожидался false",
              );
            }
            return false as const;
          })(),
        }
      : null;
    const admittedFrame = admission.frame === null
      ? null
      : record(admission.frame, `sources[${index}].admission.frame`);
    const frameValue = admittedFrame
      ? {
          frameId: string(admittedFrame.frame_id, "frame.frame_id", true),
          pointCount: number(admittedFrame.point_count, "frame.point_count"),
          semanticClassCount: number(
            admittedFrame.semantic_class_count,
            "frame.semantic_class_count",
          ),
          groundTruthGroundFraction: number(
            admittedFrame.ground_truth_ground_fraction,
            "frame.ground_truth_ground_fraction",
          ),
          previewPointCount: number(
            admittedFrame.preview_point_count,
            "frame.preview_point_count",
          ),
          previewSha256: nullableDigest(
            admittedFrame.preview_sha256,
            "frame.preview_sha256",
          ),
          previewAvailable: (() => {
            if (admittedFrame.preview_available !== true) {
              throw new DatasetGatewayContractError(
                "frame.preview_available: ожидался true",
              );
            }
            return true as const;
          })(),
        }
      : null;
    if (
      sourceKind === "goose"
      && frameValue
      && frameValue.previewSha256 === null
    ) {
      throw new DatasetGatewayContractError("GOOSE frame.preview_sha256 отсутствует");
    }
    const commercialUse = dataset.commercial_use;
    if (
      commercialUse !== "allowed-with-share-alike"
      && commercialUse !== "research-only-license-review-required"
    ) {
      throw new DatasetGatewayContractError("Dataset license scope неизвестен");
    }
    return {
      sourceId,
      sourceKind,
      displayName: string(dataset.display_name, "display_name"),
      role: string(dataset.role, "role", true),
      license: string(dataset.license, "license"),
      commercialUse,
      format: string(dataset.format, "format", true),
      frameSemantics: "one-lidar-revolution",
      sensor: string(dataset.sensor, "sensor"),
      platforms: displayStrings(dataset.platforms, "platforms"),
      superclasses: strings(dataset.superclasses, "superclasses"),
      smokeExampleMb: nullableNumber(
        download.smoke_example_mb,
        "smoke_example_mb",
      ),
      primaryScanArchiveGb: number(
        download.primary_scan_archive_gb,
        "primary_scan_archive_gb",
      ),
      labelArchiveGb: nullableNumber(
        download.label_archive_gb,
        "label_archive_gb",
      ),
      posesArchiveGb: nullableNumber(
        download.poses_archive_gb,
        "poses_archive_gb",
      ),
      fragmentCount: number(statistics.fragment_count, "fragment_count"),
      annotatedScanCount: number(
        statistics.annotated_scan_count,
        "annotated_scan_count",
      ),
      admissionStatus,
      archive: archiveValue,
      frame: frameValue,
    };
  });
  if (
    parsedSources[0]?.sourceKind !== "goose"
    || parsedSources[1]?.sourceKind !== "rellis"
  ) {
    throw new DatasetGatewayContractError("Dataset source order несовместим");
  }
  const representations = array(
    source.representations,
    "representations",
  ).map((value, index) => {
    const item = record(value, `representations[${index}]`);
    const id = item.id;
    if (
      id !== "native-scan"
      && id !== "normalized-scan"
      && id !== "rolling-local-map"
    ) {
      throw new DatasetGatewayContractError("Неизвестная LiDAR representation");
    }
    const normalizedId: DatasetRepresentationId = id;
    return {
      id: normalizedId,
      title: string(item.title, "representation.title"),
      purpose: string(item.purpose, "representation.purpose", true),
      accumulation: boolean(item.accumulation, "representation.accumulation"),
    };
  });
  const pipeline = array(source.pipeline, "pipeline").map((value, index) => {
    const item = record(value, `pipeline[${index}]`);
    return {
      stage: string(item.stage, "pipeline.stage", true),
      requires: strings(item.requires, "pipeline.requires"),
      produces: string(item.produces, "pipeline.produces", true),
    };
  });
  const inputs = array(source.known_inputs, "known_inputs");
  const currentInput = record(inputs[0], "known_inputs[0]");
  if (
    currentInput.representation !== "vendor-mapped-increment"
    || currentInput.native_scan !== false
    || currentInput.per_point_time !== false
    || currentInput.ring_or_line !== false
    || currentInput.admitted_for_patchworkpp !== false
  ) {
    throw new DatasetGatewayContractError("Vendor-map boundary завышен");
  }
  return {
    storage: {
      configured: boolean(storage.configured, "storage.configured"),
      admitted: boolean(storage.admitted, "storage.admitted"),
      status: storageStatus,
      attestation: storageAttestation,
      manifestValid: boolean(storage.manifest_valid, "storage.manifest_valid"),
      requiredWindowsRoot: string(
        storage.required_windows_root,
        "storage.required_windows_root",
      ),
      requiredWslRoot: string(storage.required_wsl_root, "storage.required_wsl_root"),
    },
    sources: parsedSources,
    representations,
    pipeline,
    currentInput: {
      representation: "vendor-mapped-increment",
      nativeScan: false,
      perPointTime: false,
      ringOrLine: false,
      admittedForPatchworkpp: false,
      reason: string(currentInput.reason, "known_inputs.reason", true),
    },
    nextAction: string(source.next_action, "next_action", true),
  };
}

export function parseDatasetNativeScanPreview(
  value: unknown,
): DatasetNativeScanPreview {
  const source = record(value, "Dataset preview");
  const isGoose = source.schema_version === "missioncore.dataset-native-scan-preview/v1"
    && source.source_id === "goose-3d/v2025-08-22";
  const isRellis = source.schema_version === "missioncore.dataset-native-scan-preview/v2"
    && source.source_id === "rellis-3d/v1.1";
  if (
    (!isGoose && !isRellis)
    || source.representation !== "native-scan"
    || source.sampling !== "deterministic-even-index"
  ) {
    throw new DatasetGatewayContractError("Dataset preview contract несовместим");
  }
  const sourcePointCount = number(source.source_point_count, "source_point_count");
  const pointCount = number(source.point_count, "point_count");
  const pointsXyzM = array(source.points_xyz_m, "points_xyz_m").map(
    (value, index): [number, number, number] => {
      const point = array(value, `points_xyz_m[${index}]`);
      if (
        point.length !== 3
        || point.some((coordinate) =>
          typeof coordinate !== "number" || !Number.isFinite(coordinate)
        )
      ) {
        throw new DatasetGatewayContractError(
          `points_xyz_m[${index}]: некорректная точка`,
        );
      }
      return [point[0] as number, point[1] as number, point[2] as number];
    },
  );
  const remission0To255 = integers(
    source.remission_0_to_255,
    "remission_0_to_255",
    255,
  );
  const semanticLabelIds = integers(
    source.semantic_label_ids,
    "semantic_label_ids",
    65_535,
  );
  const semanticRgb0To255 = integers(
    source.semantic_rgb_0_to_255,
    "semantic_rgb_0_to_255",
    255,
  );
  const groundTruthGround = integers(
    source.ground_truth_ground,
    "ground_truth_ground",
    1,
  );
  const evaluationMask = isRellis
    ? integers(source.evaluation_mask, "evaluation_mask", 1)
    : Array.from({ length: pointCount }, () => 1);
  if (
    pointCount < 1
    || pointCount > 50_000
    || pointCount > sourcePointCount
    || pointsXyzM.length !== pointCount
    || remission0To255.length !== pointCount
    || semanticLabelIds.length !== pointCount
    || semanticRgb0To255.length !== pointCount * 3
    || groundTruthGround.length !== pointCount
    || evaluationMask.length !== pointCount
  ) {
    throw new DatasetGatewayContractError("Dataset preview arrays не выровнены");
  }
  const classes = array(source.classes, "classes").map((value, index) => {
    const item = record(value, `classes[${index}]`);
    const groundTarget = item.ground_target;
    if (
      groundTarget !== undefined
      && groundTarget !== "ground"
      && groundTarget !== "non-ground"
      && groundTarget !== "ignore"
    ) {
      throw new DatasetGatewayContractError("class.ground_target неизвестен");
    }
    const normalizedGroundTarget: "ground" | "non-ground" | "ignore" | null =
      groundTarget === undefined ? null : groundTarget;
    return {
      labelId: number(item.label_id, "class.label_id"),
      className: string(item.class_name, "class.class_name", true),
      hex: string(item.hex, "class.hex"),
      challengeCategoryId: item.challenge_category_id === undefined
        ? null
        : number(item.challenge_category_id, "class.challenge_category_id"),
      challengeCategoryName: item.challenge_category_name === undefined
        ? null
        : string(
            item.challenge_category_name,
            "class.challenge_category_name",
            true,
          ),
      groundTarget: normalizedGroundTarget,
      sourcePointCount: item.source_point_count === undefined
        ? null
        : number(item.source_point_count, "class.source_point_count"),
    };
  });
  const safety = record(source.safety, "safety");
  if (
    safety.visualization_only !== true
    || safety.navigation_or_safety_accepted !== false
    || (isRellis && safety.compatibility_smoke_only !== true)
  ) {
    throw new DatasetGatewayContractError("Dataset preview safety boundary нарушен");
  }
  const coordinateFrame = isRellis
    ? (() => {
        const frame = record(source.coordinate_frame, "coordinate_frame");
        if (
          frame.frame_id !== "sensor/lidar/os1"
          || frame.x !== "forward"
          || frame.y !== "left"
          || frame.z !== "up"
          || frame.handedness !== "right"
          || frame.transform_applied !== false
        ) {
          throw new DatasetGatewayContractError("RELLIS coordinate frame несовместим");
        }
        return {
          frameId: "sensor/lidar/os1",
          x: "forward" as const,
          y: "left" as const,
          z: "up" as const,
        };
      })()
    : null;
  return {
    sourceId: isRellis ? "rellis-3d/v1.1" : "goose-3d/v2025-08-22",
    frameId: string(source.frame_id, "frame_id", true),
    sourcePointCount,
    pointCount,
    pointsXyzM,
    remission0To255,
    semanticLabelIds,
    semanticRgb0To255,
    groundTruthGround,
    evaluationMask,
    classes,
    coordinateFrame,
    compatibilitySmokeOnly: isRellis,
  };
}

export function parseDatasetGroundComparison(
  value: unknown,
): DatasetGroundComparison {
  const source = record(value, "Ground comparison");
  if (
    source.schema_version !== "missioncore.dataset-ground-comparison-preview/v2"
    || source.source_id !== "goose-3d/v2025-08-22"
    || source.sampling !== "deterministic-even-index"
  ) {
    throw new DatasetGatewayContractError("Ground comparison contract несовместим");
  }
  const pointCount = number(source.point_count, "point_count");
  const currentGround = integers(source.current_ground, "current_ground", 1);
  const patchworkGround = integers(source.patchwork_ground, "patchwork_ground", 1);
  const patchworkAssigned = integers(
    source.patchwork_assigned,
    "patchwork_assigned",
    1,
  );
  const groundTruthGround = integers(
    source.ground_truth_ground,
    "ground_truth_ground",
    1,
  );
  const evaluated = integers(source.evaluated, "evaluated", 1);
  const currentDisagreement = integers(
    source.current_disagreement,
    "current_disagreement",
    1,
  );
  const patchworkDisagreement = integers(
    source.patchwork_disagreement,
    "patchwork_disagreement",
    1,
  );
  if (
    pointCount < 1
    || pointCount > 50_000
    || currentGround.length !== pointCount
    || patchworkGround.length !== pointCount
    || patchworkAssigned.length !== pointCount
    || groundTruthGround.length !== pointCount
    || evaluated.length !== pointCount
    || currentDisagreement.length !== pointCount
    || patchworkDisagreement.length !== pointCount
  ) {
    throw new DatasetGatewayContractError("Ground comparison arrays не выровнены");
  }
  const metrics = record(source.metrics, "metrics");
  const parseMetrics = (
    value: unknown,
    label: string,
  ): DatasetGroundMetrics => {
    const providerMetrics = record(value, label);
    const fraction = (key: string): number => {
      const result = number(providerMetrics[key], `${label}.${key}`);
      if (result > 1) {
        throw new DatasetGatewayContractError(`${label}.${key}: ожидалась доля`);
      }
      return result;
    };
    return {
      precision: fraction("precision"),
      recall: fraction("recall"),
      f1: fraction("f1"),
      groundIou: fraction("ground_iou"),
      accuracy: fraction("accuracy"),
      artificialGroundRecall: fraction("artificial_ground_recall"),
      naturalGroundRecall: fraction("natural_ground_recall"),
      obstacleNonGroundRecall: fraction("obstacle_non_ground_recall"),
    };
  };
  const currentMetrics = parseMetrics(metrics.current, "metrics.current");
  const patchworkMetrics = parseMetrics(
    metrics.patchworkpp,
    "metrics.patchworkpp",
  );
  const providers = record(source.providers, "providers");
  const currentProvider = record(providers.current, "providers.current");
  const patchworkProvider = record(providers.patchworkpp, "providers.patchworkpp");
  if (
    currentProvider.provider_id !== "missioncore-local-percentile-ground/v1"
    || currentProvider.ground_truth !== false
    || !/^[a-f0-9]{64}$/.test(
      string(
        currentProvider.implementation_sha256,
        "providers.current.implementation_sha256",
      ),
    )
    || patchworkProvider.provider_id !== "patchworkpp/v1.4.1"
    || patchworkProvider.source_tag !== "v1.4.1"
    || patchworkProvider.source_commit
      !== "3e6903a1d5537a4cc2ace897b0bbb98a92d6014c"
    || patchworkProvider.ground_truth !== false
    || !/^[a-f0-9]{64}$/.test(
      string(
        patchworkProvider.binary_sha256,
        "providers.patchworkpp.binary_sha256",
      ),
    )
  ) {
    throw new DatasetGatewayContractError("Ground comparison provider несовместим");
  }
  const latency = record(source.latency_ms, "latency_ms");
  const inputProfile = record(source.input_profile, "input_profile");
  const sensorFrame = record(inputProfile.sensor_frame, "input_profile.sensor_frame");
  const height = record(inputProfile.height, "input_profile.height");
  const scope = record(inputProfile.scope, "input_profile.scope");
  const sensorHeightM = number(
    height.sensor_above_ground_m,
    "height.sensor_above_ground_m",
  );
  if (
    inputProfile.schema_version !== "missioncore.goose-patchwork-profile/v1"
    || inputProfile.source_id !== "goose-3d/v2025-08-22"
    || inputProfile.representation !== "native-scan"
    || sensorFrame.frame_id !== "sensor/lidar/vls128_roof"
    || sensorFrame.handedness !== "right"
    || sensorFrame.x !== "forward"
    || sensorFrame.y !== "left"
    || sensorFrame.z !== "up"
    || sensorFrame.one_revolution !== true
    || height.base_link_above_ground_m !== 0.64
    || height.lidar_above_base_link_m !== 1.6
    || sensorHeightM !== 2.24
    || height.evidence !== "published-dimensioned-schematic"
    || scope.patchworkpp_eligible !== true
    || scope.normalized_scan_produced !== false
    || scope.complete_vehicle_transform_known !== false
    || scope.deskew_claimed !== false
  ) {
    throw new DatasetGatewayContractError("GOOSE Patchwork++ profile несовместим");
  }
  const patchworkAssignedFraction = number(
    source.patchwork_assigned_fraction,
    "patchwork_assigned_fraction",
  );
  if (patchworkAssignedFraction > 1) {
    throw new DatasetGatewayContractError(
      "patchwork_assigned_fraction: ожидалась доля",
    );
  }
  const safety = record(source.safety, "safety");
  if (
    safety.qualification_only !== true
    || safety.navigation_or_safety_accepted !== false
  ) {
    throw new DatasetGatewayContractError("Ground comparison safety boundary нарушен");
  }
  return {
    sourceId: "goose-3d/v2025-08-22",
    frameId: string(source.frame_id, "frame_id", true),
    pointCount,
    currentGround,
    patchworkGround,
    patchworkAssigned,
    groundTruthGround,
    evaluated,
    currentDisagreement,
    patchworkDisagreement,
    metrics: {
      current: currentMetrics,
      patchworkpp: patchworkMetrics,
    },
    latencyMs: {
      current: number(latency.current, "latency_ms.current"),
      patchworkpp: number(latency.patchworkpp, "latency_ms.patchworkpp"),
    },
    patchworkAssignedFraction,
    inputProfile: {
      frameId: "sensor/lidar/vls128_roof",
      sensorHeightM,
      heightEvidence: "published-dimensioned-schematic",
      normalizedScanProduced: false,
    },
  };
}

async function responseJson(response: Response): Promise<unknown> {
  if (!response.ok) {
    throw new Error(`Dataset Gateway HTTP ${response.status}`);
  }
  return response.json();
}

export async function fetchDatasetGatewayCatalog(
  options: { signal?: AbortSignal; fetcher?: DatasetFetch } = {},
): Promise<DatasetGatewayCatalog> {
  const fetcher = options.fetcher ?? fetch;
  const response = await fetcher("/api/v1/lidar/dataset-gateway", {
    method: "GET",
    headers: { Accept: "application/json" },
    signal: options.signal,
  });
  return parseDatasetGatewayCatalog(await responseJson(response));
}

export async function fetchDatasetNativeScanPreview(
  options: {
    sourceId?: DatasetNativeScanPreview["sourceId"];
    signal?: AbortSignal;
    fetcher?: DatasetFetch;
  } = {},
): Promise<DatasetNativeScanPreview> {
  const fetcher = options.fetcher ?? fetch;
  const sourceId = options.sourceId ?? "goose-3d/v2025-08-22";
  const response = await fetcher(
    `/api/v1/lidar/dataset-gateway/preview?source_id=${encodeURIComponent(sourceId)}`,
    {
    method: "GET",
    headers: { Accept: "application/json" },
    signal: options.signal,
    },
  );
  return parseDatasetNativeScanPreview(await responseJson(response));
}

export async function fetchDatasetGroundComparison(
  options: { signal?: AbortSignal; fetcher?: DatasetFetch } = {},
): Promise<DatasetGroundComparison> {
  const fetcher = options.fetcher ?? fetch;
  const response = await fetcher(
    "/api/v1/lidar/dataset-gateway/ground-comparison",
    {
      method: "GET",
      headers: { Accept: "application/json" },
      signal: options.signal,
    },
  );
  return parseDatasetGroundComparison(await responseJson(response));
}
