import assert from "node:assert/strict";
import {
  EXTERNAL_PROVIDER_CONTRACT_VERSION,
  FOUNDRY_BINDING_UPSERT_SCHEMA_VERSION,
  assertValid,
  validateCollectionProfile,
  validateConnectionProfile,
  validateDataProduct,
  validateFoundryBinding,
  validateFoundryBindingUpsert,
  validateIntakeBatch,
  validateProviderManifest,
} from "../src/index.mjs";
import { geliosPositionsCurrentExample } from "../examples/gelios-positions-current.v1.mjs";

assert.equal(validateProviderManifest(geliosPositionsCurrentExample.providerManifest).ok, true);
assert.equal(validateConnectionProfile(geliosPositionsCurrentExample.connection).ok, true);
assert.equal(validateConnectionProfile({
  ...geliosPositionsCurrentExample.connection,
  credentialRef: { ...geliosPositionsCurrentExample.connection.credentialRef, owner: "engine" },
}).errors.includes("credentialRef.owner_must_be_ndc_l2_credentials"), true);
assert.equal(validateCollectionProfile(geliosPositionsCurrentExample.collectionProfile).ok, true);
assert.equal(validateDataProduct(geliosPositionsCurrentExample.dataProduct).ok, true);
assert.equal(validateFoundryBinding(geliosPositionsCurrentExample.foundryBinding).ok, true);

const foundryBindingUpsert = {
  schemaVersion: FOUNDRY_BINDING_UPSERT_SCHEMA_VERSION,
  applicationId: "11111111-1111-4111-8111-111111111111",
  pageId: "map",
  idempotencyKey: "foundry-binding-0123456789abcdef0123456789abcdef",
  binding: {
    id: "fleet-live-points",
    dataProductId: "fleet.positions.current.v1",
    slotId: "points",
    semanticTypes: ["map.moving_object"],
    fieldProjection: ["name", "speed", "course"],
  },
};
assert.equal(validateFoundryBindingUpsert(foundryBindingUpsert).ok, true);
assert.equal(validateFoundryBindingUpsert({
  ...foundryBindingUpsert,
  binding: { ...foundryBindingUpsert.binding, semanticTypes: ["map.moving_object", "map.moving_object"] },
}).errors.includes("binding.semanticTypes_must_not_contain_duplicates"), true);
assert.equal(validateFoundryBindingUpsert({
  ...foundryBindingUpsert,
  binding: { ...foundryBindingUpsert.binding, accessToken: "forbidden" },
}).errors.includes("binding.accessToken_not_allowed"), true);
assert.equal(validateFoundryBindingUpsert({
  ...foundryBindingUpsert,
  binding: { ...foundryBindingUpsert.binding, fieldProjection: ["ndc_edprb_forbidden-reader-token"] },
}).errors.includes("binding_must_not_contain_secret_material"), true);

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

const neutralIntakeBatch = {
  schemaVersion: EXTERNAL_PROVIDER_CONTRACT_VERSION,
  source: {
    providerId: "example-provider",
    tenantId: "sample-tenant",
    connectionId: "sample-connection",
  },
  contract: {
    dataProductId: "fleet.positions.current.v1",
    ontologyRevision: "ontology.example-fleet.v1",
    version: "1.0.0",
  },
  batch: {
    runId: "run-20260714-001",
    sequence: 0,
    idempotencyKey: "run-20260714-001.batch-0",
    receivedAt: "2026-07-14T18:00:00.000Z",
  },
  raw: {
    contentType: "application/json",
    hash: "sha256:example",
    ref: "restricted://raw/run-20260714-001",
    retentionDays: 14,
  },
  facts: [{
    sourceId: "source-object-42",
    semanticType: "map.moving_object",
    observedAt: "2026-07-14T17:59:58.000Z",
    geometry: { type: "Point", coordinates: [37.6173, 55.7558] },
    attributes: { status: "active" },
  }],
};

assert.equal(validateIntakeBatch(neutralIntakeBatch).ok, true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  raw: { contentType: "application/json", payload: { safe: "fixture" } },
}).errors.includes("raw.inline_payload_not_supported"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  raw: { contentType: "application/json", payload: "unsafe-unstructured-inline-raw" },
}).errors.includes("raw.inline_payload_not_supported"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  raw: { contentType: "application/json", payload: { providerAccessToken: "must-never-be-stored" } },
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: { authorization: "Bearer must-never-be-stored" } }],
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: "ndc_edpwb_abcdefghijklmnopqrstuvwxyz0123456789ABCDE" } }],
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: "ndc_edprb_abcdefghijklmnopqrstuvwxyz0123456789ABCDE" } }],
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: `ndc_edppr_${"P".repeat(43)}` } }],
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  raw: { contentType: "application/json", hash: "prefix ndc_edpwb_abcdefghijklmnopqrstuvwxyz0123456789ABCDE suffix", ref: "restricted://raw/fixture" },
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: "Bearer opaque-secret-material" } }],
}).errors.includes("intake_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  raw: { contentType: "application/json", hash: "sha256:fixture", ref: "restricted://raw?access_token=must-never-be-stored" },
}).errors.includes("raw.ref_must_not_contain_secret_material"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], sourceId: "" }],
}).errors.includes("facts[0].sourceId_invalid"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  batch: { ...neutralIntakeBatch.batch, sequence: 2_147_483_647 },
}).ok, true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  batch: { ...neutralIntakeBatch.batch, sequence: 2_147_483_648 },
}).errors.includes("batch.sequence_must_be_integer_0_to_2147483647"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: attributesWithSerializedSize(64 * 1024) }],
}).ok, true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], attributes: attributesWithSerializedSize((64 * 1024) + 1) }],
}).errors.includes("facts[0].attributes_size_exceeded"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], geometry: { type: "Point", coordinates: [180.0001, 55.7558] } }],
}).errors.includes("facts[0].geometry.longitude_out_of_range"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  facts: [{ ...neutralIntakeBatch.facts[0], geometry: { type: "Point", coordinates: [37.6173, -90.0001] } }],
}).errors.includes("facts[0].geometry.latitude_out_of_range"), true);
assert.equal(validateIntakeBatch({
  ...neutralIntakeBatch,
  debug: true,
}).errors.includes("intakeBatch.debug_not_allowed"), true);

assert.equal(validateConnectionProfile({
  ...geliosPositionsCurrentExample.connection,
  apiToken: "must-never-be-here",
}).ok, false);
assert.equal(validateConnectionProfile({
  ...geliosPositionsCurrentExample.connection,
  credentialRef: {
    ...geliosPositionsCurrentExample.connection.credentialRef,
    reference: "ndc_edpwb_not-allowed-even-in-a-non-secret-field",
  },
}).errors.includes("profile_must_not_contain_secret_material"), true);
assert.equal(validateConnectionProfile({
  ...geliosPositionsCurrentExample.connection,
  credentialRef: {
    ...geliosPositionsCurrentExample.connection.credentialRef,
    reference: "opaque-reference?access_token=must-not-cross-the-boundary",
  },
}).errors.includes("profile_must_not_contain_secret_material"), true);
assert.equal(validateConnectionProfile({
  ...geliosPositionsCurrentExample.connection,
  credentialRef: { ...geliosPositionsCurrentExample.connection.credentialRef, namespace: "unexpected" },
}).errors.includes("credentialRef.namespace_not_allowed"), true);
assert.equal(validateConnectionProfile({
  ...geliosPositionsCurrentExample.connection,
  scope: { ...geliosPositionsCurrentExample.connection.scope, providerSelector: "unexpected" },
}).errors.includes("scope.providerSelector_not_allowed"), true);
assert.equal(validateCollectionProfile({
  ...geliosPositionsCurrentExample.collectionProfile,
  mode: "manual",
}).errors.includes("manual_profile_must_not_define_intervalMs"), true);
assert.equal(validateCollectionProfile({
  ...geliosPositionsCurrentExample.collectionProfile,
  schedule: { intervalMs: 3000, jitterMs: 100 },
}).errors.includes("schedule.jitterMs_not_allowed"), true);
assert.equal(validateCollectionProfile({
  ...geliosPositionsCurrentExample.collectionProfile,
  capabilityIds: ["ndc_edprb_forbidden-reader-token"],
}).errors.includes("collectionProfile_must_not_contain_secret_material"), true);
assert.equal(validateFoundryBinding({
  ...geliosPositionsCurrentExample.foundryBinding,
  providerId: "gelios",
}).errors.includes("binding_must_reference_data_product_not_provider_transport"), true);
assert.equal(validateFoundryBinding({
  ...geliosPositionsCurrentExample.foundryBinding,
  applicationId: undefined,
}).errors.includes("applicationId_invalid"), true);
assert.equal(validateFoundryBinding({
  ...geliosPositionsCurrentExample.foundryBinding,
  pageId: undefined,
}).errors.includes("pageId_invalid"), true);
assert.equal(validateFoundryBinding({
  ...geliosPositionsCurrentExample.foundryBinding,
  templateId: undefined,
}).ok, true);
assert.equal(validateFoundryBinding({
  ...geliosPositionsCurrentExample.foundryBinding,
  rendererOptions: {},
}).errors.includes("foundryBinding.rendererOptions_not_allowed"), true);
assert.equal(validateFoundryBinding({
  ...geliosPositionsCurrentExample.foundryBinding,
  semanticType: "ndc_edprb_forbidden-reader-token",
}).errors.includes("binding_must_not_contain_secret_material"), true);

assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  tenantId: "must-not-live-in-provider-manifest",
}).errors.includes("manifest_must_not_contain_connection_runtime_state"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  apiToken: "must-never-be-here",
}).errors.includes("manifest_must_not_contain_secret_material"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  endpoint: "https://must-live-in-l2-template.invalid",
}).errors.includes("manifest_must_not_contain_provider_transport"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  capabilities: [{ id: "gelios.units.current.read", classification: "not-a-class" }],
}).errors.includes("capabilities[0].classification_invalid"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  metadata: {},
}).errors.includes("providerManifest.metadata_not_allowed"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  ontology: { ...geliosPositionsCurrentExample.providerManifest.ontology, transport: "unexpected" },
}).errors.includes("ontology.transport_not_allowed"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  capabilities: [{ id: "gelios.units.current.read", classification: "read", endpoint: "/units" }],
}).errors.includes("capabilities[0].endpoint_not_allowed"), true);
assert.equal(validateProviderManifest({
  ...geliosPositionsCurrentExample.providerManifest,
  dataProductIds: ["ndc_edpwb_forbidden-writer-token"],
}).errors.includes("manifest_must_not_contain_secret_material"), true);

assert.equal(validateDataProduct({
  ...geliosPositionsCurrentExample.dataProduct,
  delivery: { mode: "snapshot+patch", transport: "sse" },
}).errors.includes("delivery.transport_not_allowed"), true);

assert.throws(() => assertValid(validateDataProduct, {
  schemaVersion: EXTERNAL_PROVIDER_CONTRACT_VERSION,
  id: "fleet.positions.current.v1",
  version: "1.0.0",
  delivery: { mode: "snapshot" },
  semanticTypes: [],
  fields: [],
  access: { audience: "public" },
}));

console.log("external-provider-contract: ok");
