import assert from "node:assert/strict";
import {
  DATA_PRODUCT_HISTORY_SCHEMA_VERSION,
  DATA_PRODUCT_PATCH_SCHEMA_VERSION,
  DATA_PRODUCT_PUBLISH_SCHEMA_VERSION,
  DATA_PRODUCT_SNAPSHOT_SCHEMA_VERSION,
  validateDataProductHistory,
  validateDataProductPatch,
  validateDataProductPublish,
  validateDataProductSnapshot,
} from "../src/index.mjs";

const fact = {
  sourceId: "unit-42",
  semanticType: "map.moving_object",
  observedAt: "2026-07-15T10:00:00.000Z",
  attributes: { status: "online" },
  geometry: { type: "Point", coordinates: [37.6173, 55.7558] },
};

const attributesWithSerializedSize = (size) => ({
  blob: "x".repeat(size - Buffer.byteLength(JSON.stringify({ blob: "" }))),
});

const publish = {
  schemaVersion: DATA_PRODUCT_PUBLISH_SCHEMA_VERSION,
  batch: { runId: "execution-42", sequence: 0, idempotencyKey: "execution-42.node-01.chunk-0" },
  facts: [fact],
};
assert.equal(validateDataProductPublish(publish).ok, true);
assert.equal(validateDataProductPublish({
  ...publish,
  source: { tenantId: "forged" },
}).errors.includes("publish.source_not_allowed"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, attributes: { accessToken: "must-never-cross-the-boundary" } }],
}).errors.includes("publish_must_not_contain_secret_material"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, attributes: { status: `ndc_edppr_${"P".repeat(43)}` } }],
}).errors.includes("publish_must_not_contain_secret_material"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [fact, { ...fact, observedAt: "2026-07-15T10:00:01.000Z" }],
}).errors.includes("facts_duplicate_entity_key"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  batch: { ...publish.batch, sequence: 2_147_483_647 },
}).ok, true);
assert.equal(validateDataProductPublish({
  ...publish,
  batch: { ...publish.batch, sequence: 2_147_483_648 },
}).errors.includes("batch.sequence_must_be_integer_0_to_2147483647"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, attributes: attributesWithSerializedSize(64 * 1024) }],
}).ok, true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, attributes: attributesWithSerializedSize((64 * 1024) + 1) }],
}).errors.includes("facts[0].attributes_size_exceeded"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, attributes: attributesWithSerializedSize((64 * 1024) + 1) }],
}, { maxAttributesBytes: 1024 * 1024 }).errors.includes("facts[0].attributes_size_exceeded"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, geometry: { type: "Point", coordinates: [-180.0001, 55.7558] } }],
}).errors.includes("facts[0].geometry.longitude_out_of_range"), true);
assert.equal(validateDataProductPublish({
  ...publish,
  facts: [{ ...fact, geometry: { type: "Point", coordinates: [37.6173, 90.0001] } }],
}).errors.includes("facts[0].geometry.latitude_out_of_range"), true);
const zoneGenerationAt = "2026-07-15T10:10:00.000Z";
const zoneFact = {
  sourceId: "gelios-zone-42",
  semanticType: "map.zone",
  observedAt: zoneGenerationAt,
  attributes: { display_name: "Zone 42" },
  geometry: {
    type: "Polygon",
    coordinates: [[[37.60, 55.74], [37.62, 55.74], [37.62, 55.76], [37.60, 55.74]]],
  },
};
const replacement = {
  ...publish,
  batch: { ...publish.batch, mode: "replace", generationAt: zoneGenerationAt },
  facts: [zoneFact],
};
assert.equal(validateDataProductPublish(replacement).ok, true);
assert.equal(validateDataProductPublish({ ...replacement, facts: [] }).ok, true);
assert.equal(validateDataProductPublish({
  ...replacement,
  facts: [{ ...zoneFact, observedAt: "2026-07-15T10:09:59.000Z" }],
}).errors.includes("facts[0].observedAt_must_equal_generationAt"), true);
assert.equal(validateDataProductPublish({
  ...replacement,
  facts: [{ ...zoneFact, geometry: { ...zoneFact.geometry, coordinates: [[[37.60, 55.74], [37.62, 55.74], [37.62, 55.76], [37.61, 55.75]]] } }],
}).errors.includes("facts[0].geometry[0]_linear_ring_must_be_closed"), true);

const canonicalFact = { ...fact, receivedAt: "2026-07-15T10:00:01.000Z" };
const snapshot = {
  schemaVersion: DATA_PRODUCT_SNAPSHOT_SCHEMA_VERSION,
  dataProduct: { id: "fleet.positions.current.v1", version: "1.0.0" },
  generatedAt: "2026-07-15T10:00:01.000Z",
  cursor: "12",
  facts: [canonicalFact],
};
assert.equal(validateDataProductSnapshot(snapshot).ok, true);
assert.equal(validateDataProductSnapshot({ ...snapshot, transport: "sse" }).errors.includes("snapshot.transport_not_allowed"), true);
assert.equal(validateDataProductSnapshot({
  ...snapshot,
  facts: [{ ...canonicalFact, attributes: attributesWithSerializedSize((64 * 1024) + 1) }],
}).errors.includes("facts[0].attributes_size_exceeded"), true);
assert.equal(validateDataProductSnapshot({
  ...snapshot,
  facts: [{ ...canonicalFact, attributes: { status: "ndc_edprb_forbidden-reader-token" } }],
}).errors.includes("snapshot_must_not_contain_secret_material"), true);

const history = {
  schemaVersion: DATA_PRODUCT_HISTORY_SCHEMA_VERSION,
  dataProduct: { id: "fleet.positions.current.v1", version: "1.0.0" },
  generatedAt: "2026-07-15T10:05:00.000Z",
  query: {
    from: "2026-07-15T10:00:00.000Z",
    to: "2026-07-15T10:05:00.000Z",
    resolutionMs: 60_000,
    sourceIds: ["unit-42"],
    order: "asc",
  },
  facts: [{ ...canonicalFact, bucketStart: "2026-07-15T10:00:00.000Z" }],
  nextCursor: "opaque_cursor",
};
assert.equal(validateDataProductHistory(history).ok, true);
assert.equal(validateDataProductHistory({
  ...history,
  query: { ...history.query, order: "desc" },
}).errors.includes("query.order_must_be_asc"), true);
assert.equal(validateDataProductHistory({
  ...history,
  query: { ...history.query, from: history.query.to, to: history.query.from },
}).errors.includes("query.range_invalid"), true);
assert.equal(validateDataProductHistory({
  ...history,
  query: { ...history.query, sourceIds: ["unit-42", "unit-42"] },
}).errors.includes("query.sourceIds_must_be_unique_and_sorted"), true);
assert.equal(validateDataProductHistory({
  ...history,
  facts: [
    { ...history.facts[0], sourceId: "unit-43" },
    history.facts[0],
  ],
}).errors.includes("facts[1]_not_strictly_ordered"), true);
assert.equal(validateDataProductHistory({
  ...history,
  facts: [{ ...history.facts[0], attributes: { token: "forbidden" } }],
}).errors.includes("history_must_not_contain_secret_material"), true);
assert.equal(validateDataProductSnapshot({
  ...snapshot,
  facts: [{ ...canonicalFact, attributes: { status: `ndc_edppr_${"P".repeat(43)}` } }],
}).errors.includes("snapshot_must_not_contain_secret_material"), true);

const patch = {
  schemaVersion: DATA_PRODUCT_PATCH_SCHEMA_VERSION,
  dataProduct: { id: "fleet.positions.current.v1", version: "1.0.0" },
  cursor: "13",
  previousCursor: "12",
  emittedAt: "2026-07-15T10:00:02.000Z",
  operations: [{ op: "upsert", fact: canonicalFact }],
};
assert.equal(validateDataProductPatch(patch).ok, true);
assert.equal(validateDataProductPatch({
  ...patch,
  operations: [{ op: "delete", fact: canonicalFact }],
}).errors.includes("operations[0].op_invalid"), true);
assert.equal(validateDataProductPatch({
  ...patch,
  operations: [{ op: "remove", sourceId: "gelios-zone-42", semanticType: "map.zone" }],
}).ok, true);
assert.equal(validateDataProductPatch({
  ...patch,
  operations: [{ op: "upsert", fact: { ...canonicalFact, attributes: attributesWithSerializedSize((64 * 1024) + 1) } }],
}).errors.includes("operations[0].fact.attributes_size_exceeded"), true);
assert.equal(validateDataProductPatch({
  ...patch,
  operations: [{ op: "upsert", fact: { ...canonicalFact, attributes: { status: "ndc_edpwb_forbidden-writer-token" } } }],
}).errors.includes("patch_must_not_contain_secret_material"), true);

console.log("data-product-contract: ok");
