export const PROVIDER_PACKAGE_SCHEMA_VERSION = "nodedc.external-provider-package/v1";
export const L2_TEMPLATE_SCHEMA_VERSION = "nodedc.l2-template/v1";
export const L2_CONNECTION_INSTANCE_SCHEMA_VERSION = "nodedc.l2-connection-instance/v1";
export const SEMANTIC_MAPPING_SCHEMA_VERSION = "nodedc.semantic-mapping/v1";

import { SECRET_LIKE_VALUE as SECRET_VALUE } from "./sensitive-field-policy.mjs";
import { isBoundedTelemetryReadings } from "./telemetry-readings.mjs";

const IDENTIFIER = /^[a-z][a-z0-9._:-]{2,127}$/;
const SEMVER = /^\d+\.\d+\.\d+(?:[-+][a-z0-9.-]+)?$/i;
const SOURCE_PATH = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
const SAFE_PATH_SEGMENT = /^(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[A-Fa-f0-9]{2})*$/;
const MAGIC_PATH_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
const SECRET_QUERY_KEY = /(?:token|secret|password|authorization|access[_-]?token|refresh[_-]?token|api[_-]?key)/i;
const SECRET_FIELD_NAME = /(?:token|secret|password|authorization|access[_-]?token|refresh[_-]?token|api[_-]?key)/i;
const CAPABILITY_CLASSIFICATIONS = new Set(["read", "metadata", "write", "destructive"]);
const CAPABILITY_STATUSES = new Set(["implemented", "catalogued", "disabled"]);
const AUTH_KINDS = new Set(["api_token", "oauth2", "basic", "custom"]);
const TOKEN_ARTIFACTS = new Set(["access", "refresh"]);
const TOKEN_REFRESH_MODES = new Set(["not_applicable", "operator_managed", "runtime_managed"]);
const COLLECTION_MODES = new Set(["realtime", "manual", "history", "weekly"]);
const DELIVERY_MODES = new Set(["snapshot", "snapshot+patch", "query"]);
const HISTORY_MODES = new Set(["none", "all", "sampled"]);
const FIELD_CONTRACT_TYPES = new Set(["string", "number", "boolean", "string_array", "telemetry_readings", "point", "geometry"]);
const L2_STEP_KINDS = new Set([
  "collection_trigger",
  "provider_request",
  "extract_items",
  "semantic_mapping",
  "data_product_publish",
]);
const L2_CONNECTION_PARAMETERS = Object.freeze([
  "tenant_id",
  "connection_id",
  "collection_profile_id",
  "provider_credential_ref",
]);

const PACKAGE_KEYS = new Set([
  "schemaVersion",
  "id",
  "providerId",
  "version",
  "manifest",
  "authModes",
  "capabilities",
  "fieldPolicies",
  "collectionProfiles",
  "dataProducts",
  "mappingContracts",
  "l2Templates",
]);
const MANIFEST_KEYS = new Set([
  "id",
  "providerId",
  "version",
  "ontology",
  "authModeIds",
  "capabilityIds",
  "fieldPolicyIds",
  "collectionProfileIds",
  "dataProductIds",
  "mappingContractIds",
  "l2TemplateIds",
]);
const AUTH_MODE_KEYS = new Set([
  "id",
  "kind",
  "credentialOwner",
  "bindingCardinality",
  "transport",
  "tokenLifecycle",
  "secretHandling",
]);
const AUTH_TRANSPORT_KEYS = new Set(["placement", "name"]);
const TOKEN_LIFECYCLE_KEYS = new Set(["artifacts", "requestArtifact", "refreshMode"]);
const SECRET_HANDLING_KEYS = new Set(["store", "exposeToGraph", "persistInPackage"]);
const CAPABILITY_KEYS = new Set([
  "id",
  "classification",
  "status",
  "authModeId",
  "request",
  "entityScope",
]);
const REQUEST_KEYS = new Set(["method", "baseUrl", "path", "query", "response"]);
const RESPONSE_KEYS = new Set(["collectionPaths", "pagination"]);
const OFFSET_PAGINATION_KEYS = new Set([
  "mode", "limitParameter", "offsetParameter", "pageSize", "itemsPath", "totalPath",
  "maxPages", "maxItems", "maxResponseBytes",
]);
const ENTITY_SCOPE_KEYS = new Set(["mode", "refresh", "businessEntityFilter"]);
const FIELD_POLICY_KEYS = new Set([
  "id",
  "version",
  "dataProductId",
  "targetFields",
  "unknownSourceFields",
  "dynamicSourceFields",
  "restrictedSourcePaths",
]);
const COLLECTION_PROFILE_KEYS = new Set([
  "id",
  "version",
  "mode",
  "schedule",
  "capabilityIds",
  "dataProductId",
  "mappingContractId",
  "fieldPolicyId",
  "l2TemplateId",
  "entityScope",
  "batching",
  "cardinality",
  "retry",
]);
const DATA_PRODUCT_KEYS = new Set([
  "id",
  "version",
  "ontologyRevision",
  "deliveryMode",
  "semanticTypes",
  "fields",
  "fieldContracts",
  "history",
]);
const FIELD_CONTRACT_KEYS = new Set(["type", "required", "enum", "minimum", "maximum"]);
const HISTORY_KEYS = new Set(["mode", "intervalMs", "strategy", "retentionDays"]);
const MAPPING_KEYS = new Set([
  "schemaVersion",
  "id",
  "version",
  "sourceCapabilityId",
  "fieldPolicyId",
  "target",
  "derivations",
  "fact",
]);
const MAPPING_TARGET_KEYS = new Set(["dataProductId", "version", "ontologyRevision", "semanticType"]);
const FACT_KEYS = new Set(["sourceId", "semanticType", "observedAt", "geometry", "attributes"]);
const EXPRESSION_KEYS = new Set([
  "strategy", "paths", "coerce", "prefix", "fallback", "constant", "derive",
  "omitIfMissing", "omitIfInvalid", "minimum", "maximum", "guardPath", "guardEquals",
]);
const GEOMETRY_KEYS = new Set([
  "type", "longitude", "latitude", "strategy", "paths", "allowedTypes", "omitIfInvalid",
  "kindPath", "polygonPointsPath", "corridorPointsPath", "radiusPath",
  "coordinateOrder", "circleSegments",
]);
const DERIVATION_KEYS = new Set(["kind", "rules", "default", "parameters"]);
const TEMPLATE_KEYS = new Set([
  "schemaVersion",
  "id",
  "version",
  "runtime",
  "instanceMode",
  "connectionParameters",
  "credentialBindings",
  "steps",
  "invariants",
]);
const CREDENTIAL_BINDING_KEYS = new Set(["role", "owner", "management", "authModeId", "nodeType"]);
const STEP_KEYS = new Set([
  "id",
  "kind",
  "collectionProfileDriven",
  "capabilityId",
  "mappingContractId",
  "dataProductId",
  "nodeType",
]);
const INVARIANT_KEYS = new Set([
  "oneConnectionPerInstance",
  "providerSecretByReferenceOnly",
  "publisherBindingControlPlaneManaged",
  "callerScopeForbidden",
  "providerSpecificServiceForbidden",
]);
const INSTANCE_OPTION_KEYS = new Set([
  "tenantId",
  "connectionId",
  "collectionProfileId",
  "providerCredentialRef",
]);

/**
 * Validates one immutable provider package. Runtime account state and secret
 * values deliberately live outside this artifact.
 */
export function validateProviderPackage(value) {
  const errors = [];
  if (!isPlainObject(value)) return result(["providerPackage_must_be_object"]);
  rejectUnknownKeys(value, PACKAGE_KEYS, "providerPackage", errors);
  if (value.schemaVersion !== PROVIDER_PACKAGE_SCHEMA_VERSION) errors.push("schemaVersion_mismatch");
  requiredIdentifier(value.id, "id", errors);
  requiredIdentifier(value.providerId, "providerId", errors);
  requiredSemver(value.version, "version", errors);

  validateManifest(value.manifest, errors);
  const authModes = validateArtifactArray(value.authModes, "authModes", validateAuthMode, errors);
  const capabilities = validateArtifactArray(value.capabilities, "capabilities", validateCapability, errors);
  const fieldPolicies = validateArtifactArray(value.fieldPolicies, "fieldPolicies", validateFieldPolicy, errors);
  const collectionProfiles = validateArtifactArray(value.collectionProfiles, "collectionProfiles", validateCollectionProfileTemplate, errors);
  const dataProducts = validateArtifactArray(value.dataProducts, "dataProducts", validateDataProductDefinition, errors);
  const mappingContracts = validateArtifactArray(value.mappingContracts, "mappingContracts", validateMappingContract, errors);
  const l2Templates = validateArtifactArray(value.l2Templates, "l2Templates", validateL2Template, errors);

  if (isPlainObject(value.manifest)) {
    if (value.manifest.providerId !== value.providerId) errors.push("manifest.providerId_must_match_package");
    if (value.manifest.version !== value.version) errors.push("manifest.version_must_match_package");
    assertExactReferences(value.manifest.authModeIds, authModes, "manifest.authModeIds", errors);
    assertExactReferences(value.manifest.capabilityIds, capabilities, "manifest.capabilityIds", errors);
    assertExactReferences(value.manifest.fieldPolicyIds, fieldPolicies, "manifest.fieldPolicyIds", errors);
    assertExactReferences(value.manifest.collectionProfileIds, collectionProfiles, "manifest.collectionProfileIds", errors);
    assertExactReferences(value.manifest.dataProductIds, dataProducts, "manifest.dataProductIds", errors);
    assertExactReferences(value.manifest.mappingContractIds, mappingContracts, "manifest.mappingContractIds", errors);
    assertExactReferences(value.manifest.l2TemplateIds, l2Templates, "manifest.l2TemplateIds", errors);
  }

  const authModeIds = ids(authModes);
  const capabilityIds = ids(capabilities);
  const fieldPolicyIds = ids(fieldPolicies);
  const collectionProfileIds = ids(collectionProfiles);
  const dataProductIds = ids(dataProducts);
  const mappingContractIds = ids(mappingContracts);
  const l2TemplateIds = ids(l2Templates);

  for (const capability of capabilities) {
    assertReference(capability.authModeId, authModeIds, `capability.${capability.id}.authModeId`, errors);
    const authMode = authModes.find((item) => item.id === capability.authModeId);
    if (authMode && Object.hasOwn(capability.request?.query || {}, authMode.transport?.name)) {
      errors.push(`capability.${capability.id}.request.query_must_not_embed_credential_parameter`);
    }
  }
  for (const fieldPolicy of fieldPolicies) {
    assertReference(fieldPolicy.dataProductId, dataProductIds, `fieldPolicy.${fieldPolicy.id}.dataProductId`, errors);
    const product = dataProducts.find((item) => item.id === fieldPolicy.dataProductId);
    if (product && !sameSet(fieldPolicy.targetFields, product.fields)) {
      errors.push(`fieldPolicy.${fieldPolicy.id}.targetFields_must_match_data_product_fields`);
    }
  }
  for (const profile of collectionProfiles) {
    const selectedCapabilityIds = Array.isArray(profile.capabilityIds) ? profile.capabilityIds : [];
    selectedCapabilityIds.forEach((id) => {
      assertReference(id, capabilityIds, `collectionProfile.${profile.id}.capabilityIds`, errors);
      const capability = capabilities.find((item) => item.id === id);
      if (capability && (capability.status !== "implemented" || !new Set(["read", "metadata"]).has(capability.classification))) {
        errors.push(`collectionProfile.${profile.id}.capabilityIds_must_reference_implemented_safe_read`);
      }
    });
    assertReference(profile.dataProductId, dataProductIds, `collectionProfile.${profile.id}.dataProductId`, errors);
    assertReference(profile.fieldPolicyId, fieldPolicyIds, `collectionProfile.${profile.id}.fieldPolicyId`, errors);
    assertReference(profile.mappingContractId, mappingContractIds, `collectionProfile.${profile.id}.mappingContractId`, errors);
    assertReference(profile.l2TemplateId, l2TemplateIds, `collectionProfile.${profile.id}.l2TemplateId`, errors);
    const mapping = mappingContracts.find((item) => item.id === profile.mappingContractId);
    const fieldPolicy = fieldPolicies.find((item) => item.id === profile.fieldPolicyId);
    const template = l2Templates.find((item) => item.id === profile.l2TemplateId);
    if (mapping) {
      if (!selectedCapabilityIds.includes(mapping.sourceCapabilityId)) {
        errors.push(`collectionProfile.${profile.id}.mapping_source_capability_must_be_selected`);
      }
      for (const derivation of Object.values(mapping.derivations || {})) {
        if (
          derivation?.kind === "bounded_response_lookup"
          && !selectedCapabilityIds.includes(derivation.parameters?.responseCapabilityId)
        ) {
          errors.push(`collectionProfile.${profile.id}.lookup_response_capability_must_be_selected`);
        }
      }
      if (mapping.fieldPolicyId !== profile.fieldPolicyId) errors.push(`collectionProfile.${profile.id}.mapping_field_policy_mismatch`);
      if (mapping.target?.dataProductId !== profile.dataProductId) errors.push(`collectionProfile.${profile.id}.mapping_data_product_mismatch`);
    }
    if (fieldPolicy && fieldPolicy.dataProductId !== profile.dataProductId) {
      errors.push(`collectionProfile.${profile.id}.field_policy_data_product_mismatch`);
    }
    if (template) {
      const steps = Array.isArray(template.steps) ? template.steps : [];
      const requestSteps = steps.filter((step) => step?.kind === "provider_request");
      const extractSteps = steps.filter((step) => step?.kind === "extract_items");
      const mappingSteps = steps.filter((step) => step?.kind === "semantic_mapping");
      const publishSteps = steps.filter((step) => step?.kind === "data_product_publish");
      const executedCapabilityIds = requestSteps.map((step) => step.capabilityId);
      if (!sameSet(selectedCapabilityIds, executedCapabilityIds)
        || extractSteps.length !== 1
        || extractSteps[0]?.capabilityId !== mapping?.sourceCapabilityId) {
        errors.push(`collectionProfile.${profile.id}.template_capability_chain_mismatch`);
      }
      if (mappingSteps.length !== 1 || mappingSteps[0]?.mappingContractId !== profile.mappingContractId) errors.push(`collectionProfile.${profile.id}.template_mapping_mismatch`);
      if (publishSteps.length !== 1 || publishSteps[0]?.dataProductId !== profile.dataProductId) errors.push(`collectionProfile.${profile.id}.template_data_product_mismatch`);
    }
  }
  for (const mapping of mappingContracts) {
    assertReference(mapping.sourceCapabilityId, capabilityIds, `mappingContract.${mapping.id}.sourceCapabilityId`, errors);
    for (const [derivationId, derivation] of Object.entries(mapping.derivations || {})) {
      if (derivation?.kind === "bounded_response_lookup") {
        assertReference(
          derivation.parameters?.responseCapabilityId,
          capabilityIds,
          `mappingContract.${mapping.id}.derivations.${derivationId}.parameters.responseCapabilityId`,
          errors,
        );
      }
    }
    assertReference(mapping.fieldPolicyId, fieldPolicyIds, `mappingContract.${mapping.id}.fieldPolicyId`, errors);
    assertReference(mapping.target?.dataProductId, dataProductIds, `mappingContract.${mapping.id}.target.dataProductId`, errors);
    const product = dataProducts.find((item) => item.id === mapping.target?.dataProductId);
    const fieldPolicy = fieldPolicies.find((item) => item.id === mapping.fieldPolicyId);
    if (product) validateMappingAgainstProduct(mapping, product, errors);
    if (fieldPolicy) {
      if (fieldPolicy.dataProductId !== mapping.target?.dataProductId) {
        errors.push(`mappingContract.${mapping.id}.field_policy_data_product_mismatch`);
      }
      const restricted = Array.isArray(fieldPolicy.restrictedSourcePaths)
        ? fieldPolicy.restrictedSourcePaths.filter(isCanonicalSourcePath)
        : [];
      if (mappingSourcePaths({
        fact: mapping.fact,
        derivations: mapping.derivations,
      }).some((path) => (
        restricted.some((restrictedPath) => (
          path === restrictedPath
          || path.startsWith(`${restrictedPath}.`)
          || restrictedPath.startsWith(`${path}.`)
        ))
      ))) {
        errors.push(`mappingContract.${mapping.id}.fact_source_path_restricted`);
      }
    }
  }
  for (const template of l2Templates) {
    for (const binding of Array.isArray(template.credentialBindings) ? template.credentialBindings : []) {
      if (!isPlainObject(binding)) continue;
      if (binding.authModeId !== undefined) {
        assertReference(binding.authModeId, authModeIds, `l2Template.${template.id}.credentialBindings.authModeId`, errors);
      }
    }
    for (const step of Array.isArray(template.steps) ? template.steps : []) {
      if (!isPlainObject(step)) continue;
      if (step.capabilityId !== undefined) {
        assertReference(step.capabilityId, capabilityIds, `l2Template.${template.id}.steps.capabilityId`, errors);
        if (step.kind === "provider_request") {
          const capability = capabilities.find((item) => item.id === step.capabilityId);
          if (capability && (capability.status !== "implemented" || !new Set(["read", "metadata"]).has(capability.classification))) {
            errors.push(`l2Template.${template.id}.provider_request_must_reference_implemented_safe_read`);
          }
        }
      }
      if (step.mappingContractId !== undefined) assertReference(step.mappingContractId, mappingContractIds, `l2Template.${template.id}.steps.mappingContractId`, errors);
      if (step.dataProductId !== undefined) assertReference(step.dataProductId, dataProductIds, `l2Template.${template.id}.steps.dataProductId`, errors);
    }
    const providerBinding = Array.isArray(template.credentialBindings)
      ? template.credentialBindings.find((binding) => binding?.role === "provider")
      : undefined;
    const providerRequests = Array.isArray(template.steps)
      ? template.steps.filter((step) => step?.kind === "provider_request")
      : [];
    for (const providerRequest of providerRequests) {
      const requestCapability = capabilities.find((capability) => capability.id === providerRequest?.capabilityId);
      if (providerBinding && requestCapability
        && providerBinding.authModeId !== requestCapability.authModeId) {
        errors.push(`l2Template.${template.id}.provider_credential_auth_mode_mismatch`);
      }
    }
  }

  if (value.tenantId !== undefined || value.connectionId !== undefined || value.credentialRef !== undefined) {
    errors.push("providerPackage_must_not_contain_connection_instance_state");
  }
  if (containsSecretValue(value)) errors.push("providerPackage_must_not_contain_secret_material");
  return result(errors);
}

/**
 * Materializes secret-free NDC L2 connection metadata from a provider package.
 * A new account is data, not source code: callers supply only the provider
 * credential reference. Internal publish binding resolution belongs to the
 * NDC L2 control plane and never crosses this input boundary.
 */
export function instantiateL2Connection(providerPackage, options) {
  const packageValidation = validateProviderPackage(providerPackage);
  if (!packageValidation.ok) {
    throw new Error(`provider_package_invalid:${packageValidation.errors.join(",")}`);
  }
  const errors = [];
  if (!isPlainObject(options)) throw new Error("l2_connection_options_must_be_object");
  rejectUnknownKeys(options, INSTANCE_OPTION_KEYS, "options", errors);
  requiredIdentifier(options.tenantId, "tenantId", errors);
  requiredIdentifier(options.connectionId, "connectionId", errors);
  requiredIdentifier(options.collectionProfileId, "collectionProfileId", errors);
  requiredOpaqueReference(options.providerCredentialRef, "providerCredentialRef", errors);
  if (containsSecretValue(options)) errors.push("l2_connection_options_must_use_opaque_references");

  const profile = providerPackage.collectionProfiles.find((item) => item.id === options.collectionProfileId);
  if (!profile) errors.push("collectionProfileId_not_in_provider_package");
  const template = providerPackage.l2Templates.find((item) => item.id === profile?.l2TemplateId);
  if (!template) errors.push("l2Template_missing");
  if (errors.length) throw new Error(`l2_connection_invalid:${[...new Set(errors)].join(",")}`);
  const publisherBinding = template.credentialBindings.find((binding) => binding.role === "publisher");

  return deepFreeze({
    schemaVersion: L2_CONNECTION_INSTANCE_SCHEMA_VERSION,
    package: {
      id: providerPackage.id,
      providerId: providerPackage.providerId,
      version: providerPackage.version,
    },
    tenantId: options.tenantId,
    connectionId: options.connectionId,
    l2TemplateId: template.id,
    collectionProfileId: profile.id,
    credentialRefs: {
      provider: { owner: "ndc_l2_credentials", reference: options.providerCredentialRef },
    },
    systemBindings: {
      publisher: {
        role: "publisher",
        owner: publisherBinding.owner,
        management: publisherBinding.management,
        nodeType: publisherBinding.nodeType,
        desiredState: "bound",
        status: "unresolved",
      },
    },
    scope: {
      mode: "all_visible_to_credential",
      refresh: "each_collection_run",
      capabilityIds: [...profile.capabilityIds],
    },
  });
}

function validateManifest(value, errors) {
  if (!isPlainObject(value)) {
    errors.push("manifest_must_be_object");
    return;
  }
  rejectUnknownKeys(value, MANIFEST_KEYS, "manifest", errors);
  requiredIdentifier(value.id, "manifest.id", errors);
  requiredIdentifier(value.providerId, "manifest.providerId", errors);
  requiredSemver(value.version, "manifest.version", errors);
  if (!isPlainObject(value.ontology)) {
    errors.push("manifest.ontology_must_be_object");
  } else {
    rejectUnknownKeys(value.ontology, new Set(["packageId", "revision"]), "manifest.ontology", errors);
    requiredIdentifier(value.ontology.packageId, "manifest.ontology.packageId", errors);
    requiredIdentifier(value.ontology.revision, "manifest.ontology.revision", errors);
  }
  for (const key of ["authModeIds", "capabilityIds", "fieldPolicyIds", "collectionProfileIds", "dataProductIds", "mappingContractIds", "l2TemplateIds"]) {
    requiredUniqueIdentifierArray(value[key], `manifest.${key}`, errors);
  }
}

function validateAuthMode(value, path, errors) {
  rejectUnknownKeys(value, AUTH_MODE_KEYS, path, errors);
  requiredIdentifier(value.id, `${path}.id`, errors);
  if (!AUTH_KINDS.has(value.kind)) errors.push(`${path}.kind_invalid`);
  if (value.credentialOwner !== "ndc_l2_credentials") errors.push(`${path}.credentialOwner_must_be_ndc_l2_credentials`);
  if (value.bindingCardinality !== "one_per_connection") errors.push(`${path}.bindingCardinality_must_be_one_per_connection`);
  if (!isPlainObject(value.transport)) {
    errors.push(`${path}.transport_must_be_object`);
  } else {
    rejectUnknownKeys(value.transport, AUTH_TRANSPORT_KEYS, `${path}.transport`, errors);
    if (!new Set(["header", "query", "body"]).has(value.transport.placement)) errors.push(`${path}.transport.placement_invalid`);
    requiredString(value.transport.name, `${path}.transport.name`, errors);
  }
  if (value.tokenLifecycle !== undefined) {
    validateTokenLifecycle(value.tokenLifecycle, `${path}.tokenLifecycle`, errors);
  }
  if (!isPlainObject(value.secretHandling)) {
    errors.push(`${path}.secretHandling_must_be_object`);
  } else {
    rejectUnknownKeys(value.secretHandling, SECRET_HANDLING_KEYS, `${path}.secretHandling`, errors);
    if (value.secretHandling.store !== "ndc_l2_credentials") errors.push(`${path}.secretHandling.store_must_be_ndc_l2_credentials`);
    if (value.secretHandling.exposeToGraph !== false) errors.push(`${path}.secretHandling.exposeToGraph_must_be_false`);
    if (value.secretHandling.persistInPackage !== false) errors.push(`${path}.secretHandling.persistInPackage_must_be_false`);
  }
}

function validateTokenLifecycle(value, path, errors) {
  if (!isPlainObject(value)) {
    errors.push(`${path}_must_be_object`);
    return;
  }
  rejectUnknownKeys(value, TOKEN_LIFECYCLE_KEYS, path, errors);
  if (!Array.isArray(value.artifacts) || value.artifacts.length === 0) {
    errors.push(`${path}.artifacts_must_be_nonempty_array`);
  } else {
    for (const artifact of value.artifacts) {
      if (!TOKEN_ARTIFACTS.has(artifact)) errors.push(`${path}.artifacts_invalid`);
    }
    if (new Set(value.artifacts).size !== value.artifacts.length) {
      errors.push(`${path}.artifacts_must_not_contain_duplicates`);
    }
  }
  if (value.requestArtifact !== "access") {
    errors.push(`${path}.requestArtifact_must_be_access`);
  } else if (Array.isArray(value.artifacts) && !value.artifacts.includes(value.requestArtifact)) {
    errors.push(`${path}.requestArtifact_must_reference_artifact`);
  }
  const hasRefresh = Array.isArray(value.artifacts) && value.artifacts.includes("refresh");
  if (!TOKEN_REFRESH_MODES.has(value.refreshMode)) {
    errors.push(`${path}.refreshMode_invalid`);
  } else if (hasRefresh && value.refreshMode === "not_applicable") {
    errors.push(`${path}.refreshMode_required_for_refresh_artifact`);
  } else if (!hasRefresh && value.refreshMode !== "not_applicable") {
    errors.push(`${path}.refreshMode_must_be_not_applicable_without_refresh_artifact`);
  }
}

function validateCapability(value, path, errors) {
  rejectUnknownKeys(value, CAPABILITY_KEYS, path, errors);
  requiredIdentifier(value.id, `${path}.id`, errors);
  if (!CAPABILITY_CLASSIFICATIONS.has(value.classification)) errors.push(`${path}.classification_invalid`);
  if (!CAPABILITY_STATUSES.has(value.status)) errors.push(`${path}.status_invalid`);
  requiredIdentifier(value.authModeId, `${path}.authModeId`, errors);
  if (!isPlainObject(value.request)) {
    errors.push(`${path}.request_must_be_object`);
  } else {
    rejectUnknownKeys(value.request, REQUEST_KEYS, `${path}.request`, errors);
    if (value.request.method !== "GET") errors.push(`${path}.request.method_must_be_GET_for_v1`);
    validateProviderBaseUrl(value.request.baseUrl, `${path}.request.baseUrl`, errors);
    validateProviderRequestPath(value.request.path, `${path}.request.path`, errors);
    validateStaticQuery(value.request.query, `${path}.request.query`, errors);
    if (!isPlainObject(value.request.response)) {
      errors.push(`${path}.request.response_must_be_object`);
    } else {
      rejectUnknownKeys(value.request.response, RESPONSE_KEYS, `${path}.request.response`, errors);
      requiredCollectionPathArray(value.request.response.collectionPaths, `${path}.request.response.collectionPaths`, errors);
      validateResponsePagination(value.request.response.pagination, value.request.query, `${path}.request.response.pagination`, errors);
    }
  }
  validateCredentialVisibleScope(value.entityScope, `${path}.entityScope`, errors);
}

function validateResponsePagination(value, query, path, errors) {
  if (value === "single_bounded_response") return;
  if (!isPlainObject(value)) {
    errors.push(`${path}_invalid`);
    return;
  }
  rejectUnknownKeys(value, OFFSET_PAGINATION_KEYS, path, errors);
  if (value.mode !== "offset") errors.push(`${path}.mode_must_be_offset`);
  for (const field of ["limitParameter", "offsetParameter"]) {
    if (typeof value[field] !== "string" || !/^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(value[field])) {
      errors.push(`${path}.${field}_invalid`);
    }
  }
  if (!Number.isInteger(value.pageSize) || value.pageSize < 1 || value.pageSize > 500) {
    errors.push(`${path}.pageSize_invalid`);
  }
  if (!Number.isInteger(value.maxPages) || value.maxPages < 1 || value.maxPages > 1000) {
    errors.push(`${path}.maxPages_invalid`);
  }
  if (!Number.isInteger(value.maxItems) || value.maxItems < 1 || value.maxItems > 5000) {
    errors.push(`${path}.maxItems_invalid`);
  }
  if (!Number.isInteger(value.maxResponseBytes) || value.maxResponseBytes < 1024 || value.maxResponseBytes > 32 * 1024 * 1024) {
    errors.push(`${path}.maxResponseBytes_invalid`);
  }
  for (const field of ["itemsPath", "totalPath"]) {
    if (!isCanonicalSourcePath(value[field])) errors.push(`${path}.${field}_invalid`);
  }
  if (isPlainObject(query)) {
    if (query[value.limitParameter] !== value.pageSize) errors.push(`${path}.pageSize_must_match_static_query_limit`);
    if (query[value.offsetParameter] !== 0) errors.push(`${path}.offset_query_must_start_at_zero`);
  }
  if (Number.isInteger(value.maxPages) && Number.isInteger(value.pageSize)
    && Number.isInteger(value.maxItems) && value.maxPages * value.pageSize < value.maxItems) {
    errors.push(`${path}.maxPages_cannot_cover_maxItems`);
  }
}

function validateFieldPolicy(value, path, errors) {
  rejectUnknownKeys(value, FIELD_POLICY_KEYS, path, errors);
  requiredIdentifier(value.id, `${path}.id`, errors);
  requiredSemver(value.version, `${path}.version`, errors);
  requiredIdentifier(value.dataProductId, `${path}.dataProductId`, errors);
  requiredUniqueIdentifierArray(value.targetFields, `${path}.targetFields`, errors);
  if (Array.isArray(value.targetFields) && value.targetFields.some((field) => SECRET_FIELD_NAME.test(String(field)))) {
    errors.push(`${path}.targetFields_must_not_contain_secret_fields`);
  }
  if (value.unknownSourceFields !== "drop") errors.push(`${path}.unknownSourceFields_must_be_drop`);
  if (value.dynamicSourceFields !== "drop_until_classified") errors.push(`${path}.dynamicSourceFields_must_be_drop_until_classified`);
  requiredSourcePathArray(value.restrictedSourcePaths, `${path}.restrictedSourcePaths`, errors);
}

function validateCollectionProfileTemplate(value, path, errors) {
  rejectUnknownKeys(value, COLLECTION_PROFILE_KEYS, path, errors);
  requiredIdentifier(value.id, `${path}.id`, errors);
  requiredSemver(value.version, `${path}.version`, errors);
  if (!COLLECTION_MODES.has(value.mode)) errors.push(`${path}.mode_invalid`);
  requiredUniqueIdentifierArray(value.capabilityIds, `${path}.capabilityIds`, errors);
  requiredIdentifier(value.dataProductId, `${path}.dataProductId`, errors);
  requiredIdentifier(value.mappingContractId, `${path}.mappingContractId`, errors);
  requiredIdentifier(value.fieldPolicyId, `${path}.fieldPolicyId`, errors);
  requiredIdentifier(value.l2TemplateId, `${path}.l2TemplateId`, errors);
  if (value.mode === "realtime") {
    if (!isPlainObject(value.schedule)) errors.push(`${path}.schedule_must_be_object`);
    else if (!Number.isInteger(value.schedule.intervalMs) || value.schedule.intervalMs < 1000) errors.push(`${path}.schedule.intervalMs_invalid`);
  } else if (value.schedule !== undefined) {
    errors.push(`${path}.schedule_not_allowed_for_non_realtime_profile`);
  }
  validateCredentialVisibleScope(value.entityScope, `${path}.entityScope`, errors);
  if (!isPlainObject(value.batching) || !Number.isInteger(value.batching.maxFacts) || value.batching.maxFacts < 1 || value.batching.maxFacts > 5000) {
    errors.push(`${path}.batching.maxFacts_must_be_integer_1_to_5000`);
  } else {
    rejectUnknownKeys(value.batching, new Set(["maxFacts"]), `${path}.batching`, errors);
  }
  if (!isPlainObject(value.cardinality)) {
    errors.push(`${path}.cardinality_must_be_object`);
  } else {
    rejectUnknownKeys(value.cardinality, new Set(["maxCurrentEntities", "onExceed"]), `${path}.cardinality`, errors);
    if (value.cardinality.maxCurrentEntities !== 5000) errors.push(`${path}.cardinality.maxCurrentEntities_must_be_5000`);
    if (value.cardinality.onExceed !== "require_partitioned_data_product") errors.push(`${path}.cardinality.onExceed_invalid`);
  }
  if (!isPlainObject(value.retry)) {
    errors.push(`${path}.retry_must_be_object`);
  } else {
    rejectUnknownKeys(value.retry, new Set(["maxAttempts", "backoff", "delayMs"]), `${path}.retry`, errors);
    if (!Number.isInteger(value.retry.maxAttempts) || value.retry.maxAttempts < 1 || value.retry.maxAttempts > 10) errors.push(`${path}.retry.maxAttempts_invalid`);
    if (!["exponential_with_jitter", "fixed_delay"].includes(value.retry.backoff)) {
      errors.push(`${path}.retry.backoff_invalid`);
    }
    if (value.retry.backoff === "fixed_delay") {
      if (!Number.isInteger(value.retry.delayMs) || value.retry.delayMs < 100 || value.retry.delayMs > 60_000) {
        errors.push(`${path}.retry.delayMs_invalid`);
      }
    } else if (value.retry.delayMs !== undefined) {
      errors.push(`${path}.retry.delayMs_requires_fixed_delay`);
    }
  }
}

function validateDataProductDefinition(value, path, errors) {
  rejectUnknownKeys(value, DATA_PRODUCT_KEYS, path, errors);
  requiredIdentifier(value.id, `${path}.id`, errors);
  requiredSemver(value.version, `${path}.version`, errors);
  requiredIdentifier(value.ontologyRevision, `${path}.ontologyRevision`, errors);
  if (!DELIVERY_MODES.has(value.deliveryMode)) errors.push(`${path}.deliveryMode_invalid`);
  requiredUniqueIdentifierArray(value.semanticTypes, `${path}.semanticTypes`, errors);
  requiredUniqueIdentifierArray(value.fields, `${path}.fields`, errors);
  if (Array.isArray(value.fields) && value.fields.some((field) => SECRET_FIELD_NAME.test(String(field)))) {
    errors.push(`${path}.fields_must_not_contain_secret_fields`);
  }
  validateFieldContracts(value.fieldContracts, value.fields, `${path}.fieldContracts`, errors);
  if (!isPlainObject(value.history)) {
    errors.push(`${path}.history_must_be_object`);
  } else {
    rejectUnknownKeys(value.history, HISTORY_KEYS, `${path}.history`, errors);
    if (!HISTORY_MODES.has(value.history.mode)) errors.push(`${path}.history.mode_invalid`);
    if (value.history.mode === "sampled") {
      if (!Number.isInteger(value.history.intervalMs)
        || value.history.intervalMs < 1000
        || value.history.intervalMs > 24 * 60 * 60 * 1000) {
        errors.push(`${path}.history.intervalMs_invalid`);
      }
      if (value.history.strategy !== "latest-per-entity-per-bucket") {
        errors.push(`${path}.history.strategy_invalid`);
      }
    } else if (value.history.intervalMs !== undefined || value.history.strategy !== undefined) {
      errors.push(`${path}.history.sampling_fields_forbidden`);
    }
    if (!Number.isInteger(value.history.retentionDays)
      || value.history.retentionDays < 1
      || value.history.retentionDays > 3650) {
      errors.push(`${path}.history.retentionDays_invalid`);
    }
  }
}

function validateMappingContract(value, path, errors) {
  rejectUnknownKeys(value, MAPPING_KEYS, path, errors);
  if (value.schemaVersion !== SEMANTIC_MAPPING_SCHEMA_VERSION) errors.push(`${path}.schemaVersion_mismatch`);
  requiredIdentifier(value.id, `${path}.id`, errors);
  requiredSemver(value.version, `${path}.version`, errors);
  requiredIdentifier(value.sourceCapabilityId, `${path}.sourceCapabilityId`, errors);
  requiredIdentifier(value.fieldPolicyId, `${path}.fieldPolicyId`, errors);
  if (!isPlainObject(value.target)) {
    errors.push(`${path}.target_must_be_object`);
  } else {
    rejectUnknownKeys(value.target, MAPPING_TARGET_KEYS, `${path}.target`, errors);
    requiredIdentifier(value.target.dataProductId, `${path}.target.dataProductId`, errors);
    requiredSemver(value.target.version, `${path}.target.version`, errors);
    requiredIdentifier(value.target.ontologyRevision, `${path}.target.ontologyRevision`, errors);
    requiredIdentifier(value.target.semanticType, `${path}.target.semanticType`, errors);
  }
  if (!isPlainObject(value.fact)) {
    errors.push(`${path}.fact_must_be_object`);
    return;
  }
  rejectUnknownKeys(value.fact, FACT_KEYS, `${path}.fact`, errors);
  validateExpression(value.fact.sourceId, `${path}.fact.sourceId`, errors);
  validateExpression(value.fact.semanticType, `${path}.fact.semanticType`, errors);
  validateExpression(value.fact.observedAt, `${path}.fact.observedAt`, errors);
  if (value.fact.geometry !== undefined && !isPlainObject(value.fact.geometry)) {
    errors.push(`${path}.fact.geometry_must_be_object`);
  } else if (isPlainObject(value.fact.geometry)) {
    rejectUnknownKeys(value.fact.geometry, GEOMETRY_KEYS, `${path}.fact.geometry`, errors);
    if (value.fact.geometry.type === "Point") {
      validateExpression(value.fact.geometry.longitude, `${path}.fact.geometry.longitude`, errors);
      validateExpression(value.fact.geometry.latitude, `${path}.fact.geometry.latitude`, errors);
      if (value.fact.geometry.omitIfInvalid !== true) errors.push(`${path}.fact.geometry.omitIfInvalid_must_be_true`);
      if (value.fact.geometry.strategy !== undefined || value.fact.geometry.paths !== undefined || value.fact.geometry.allowedTypes !== undefined) {
        errors.push(`${path}.fact.geometry.point_shape_invalid`);
      }
    } else if (value.fact.geometry.type === "GeoJSON") {
      if (value.fact.geometry.strategy !== "bounded_zone_geometry_v1") errors.push(`${path}.fact.geometry.strategy_invalid`);
      for (const key of ["kindPath", "polygonPointsPath", "corridorPointsPath", "radiusPath"]) {
        if (!isCanonicalSourcePath(value.fact.geometry[key])) {
          errors.push(`${path}.fact.geometry.${key}_invalid`);
        }
      }
      if (!["latitude_longitude", "longitude_latitude"].includes(value.fact.geometry.coordinateOrder)) {
        errors.push(`${path}.fact.geometry.coordinateOrder_invalid`);
      }
      if (!Number.isInteger(value.fact.geometry.circleSegments)
        || value.fact.geometry.circleSegments < 16
        || value.fact.geometry.circleSegments > 256) {
        errors.push(`${path}.fact.geometry.circleSegments_invalid`);
      }
      if (!sameSet(value.fact.geometry.allowedTypes, ["Polygon", "MultiPolygon"])) {
        errors.push(`${path}.fact.geometry.allowedTypes_must_be_polygon_or_multipolygon`);
      }
      if (value.fact.geometry.omitIfInvalid !== false) errors.push(`${path}.fact.geometry.omitIfInvalid_must_be_false`);
      if (
        value.fact.geometry.longitude !== undefined
        || value.fact.geometry.latitude !== undefined
        || value.fact.geometry.paths !== undefined
      ) {
        errors.push(`${path}.fact.geometry.geojson_shape_invalid`);
      }
    } else {
      errors.push(`${path}.fact.geometry.type_invalid`);
    }
  }
  if (value.fact.attributes !== undefined && !isPlainObject(value.fact.attributes)) {
    errors.push(`${path}.fact.attributes_must_be_object`);
  } else if (isPlainObject(value.fact.attributes)) {
    for (const [attribute, expression] of Object.entries(value.fact.attributes)) {
      requiredIdentifier(attribute, `${path}.fact.attributes.${attribute}`, errors);
      if (SECRET_FIELD_NAME.test(attribute)) errors.push(`${path}.fact.attributes.${attribute}_secret_field_not_allowed`);
      validateExpression(expression, `${path}.fact.attributes.${attribute}`, errors);
    }
  }
  const derivationReferences = mappingDerivationReferences(value.fact);
  if (value.derivations !== undefined && !isPlainObject(value.derivations)) {
    errors.push(`${path}.derivations_must_be_object`);
  } else if (isPlainObject(value.derivations)) {
    for (const [id, derivation] of Object.entries(value.derivations)) {
      requiredIdentifier(id, `${path}.derivations.${id}`, errors);
      validateDerivation(derivation, `${path}.derivations.${id}`, errors);
    }
    const derivationIds = new Set(Object.keys(value.derivations));
    for (const id of derivationReferences) {
      if (!derivationIds.has(id)) errors.push(`${path}.derive_${id}_missing_definition`);
    }
    for (const id of derivationIds) {
      if (!derivationReferences.has(id)) errors.push(`${path}.derivation_${id}_unused`);
    }
  } else if (derivationReferences.size) {
    for (const id of derivationReferences) errors.push(`${path}.derive_${id}_missing_definition`);
  }
}

function validateL2Template(value, path, errors) {
  rejectUnknownKeys(value, TEMPLATE_KEYS, path, errors);
  if (value.schemaVersion !== L2_TEMPLATE_SCHEMA_VERSION) errors.push(`${path}.schemaVersion_mismatch`);
  requiredIdentifier(value.id, `${path}.id`, errors);
  requiredSemver(value.version, `${path}.version`, errors);
  if (value.runtime !== "ndc_l2") errors.push(`${path}.runtime_must_be_ndc_l2`);
  if (value.instanceMode !== "one_connection_per_workflow") errors.push(`${path}.instanceMode_must_be_one_connection_per_workflow`);
  requiredStringArray(value.connectionParameters, `${path}.connectionParameters`, errors);
  if (!sameArray(value.connectionParameters, L2_CONNECTION_PARAMETERS)) {
    errors.push(`${path}.connectionParameters_must_match_v1_contract`);
  }
  if (!Array.isArray(value.credentialBindings) || value.credentialBindings.length !== 2) {
    errors.push(`${path}.credentialBindings_must_have_provider_and_publisher`);
  } else {
    const roles = new Set();
    value.credentialBindings.forEach((binding, index) => {
      const bindingPath = `${path}.credentialBindings[${index}]`;
      if (!isPlainObject(binding)) return errors.push(`${bindingPath}_must_be_object`);
      rejectUnknownKeys(binding, CREDENTIAL_BINDING_KEYS, bindingPath, errors);
      if (!new Set(["provider", "publisher"]).has(binding.role)) errors.push(`${bindingPath}.role_invalid`);
      roles.add(binding.role);
      if (binding.owner !== "ndc_l2_credentials") errors.push(`${bindingPath}.owner_must_be_ndc_l2_credentials`);
      if (!new Set(["connection_input", "control_plane_managed"]).has(binding.management)) {
        errors.push(`${bindingPath}.management_invalid`);
      }
      if (binding.authModeId !== undefined) requiredIdentifier(binding.authModeId, `${bindingPath}.authModeId`, errors);
      if (binding.nodeType !== undefined) requiredString(binding.nodeType, `${bindingPath}.nodeType`, errors);
      if (binding.role === "provider" && (
        binding.authModeId === undefined
        || binding.nodeType !== undefined
        || binding.management !== "connection_input"
      )) {
        errors.push(`${bindingPath}.provider_binding_shape_invalid`);
      }
      if (binding.role === "publisher" && (
        binding.authModeId !== undefined
        || binding.nodeType !== "n8n-nodes-ndc.ndcDataProductPublish"
        || binding.management !== "control_plane_managed"
      )) {
        errors.push(`${bindingPath}.publisher_binding_shape_invalid`);
      }
    });
    if (!roles.has("provider") || !roles.has("publisher")) errors.push(`${path}.credentialBindings_roles_invalid`);
  }
  if (!Array.isArray(value.steps) || value.steps.length < 5) {
    errors.push(`${path}.steps_must_define_complete_boundary`);
  } else {
    const stepIds = new Set();
    value.steps.forEach((step, index) => {
      const stepPath = `${path}.steps[${index}]`;
      if (!isPlainObject(step)) return errors.push(`${stepPath}_must_be_object`);
      rejectUnknownKeys(step, STEP_KEYS, stepPath, errors);
      requiredIdentifier(step.id, `${stepPath}.id`, errors);
      if (stepIds.has(step.id)) errors.push(`${path}.steps_ids_must_be_unique`);
      stepIds.add(step.id);
      if (!L2_STEP_KINDS.has(step.kind)) errors.push(`${stepPath}.kind_invalid`);
    });
    const requestSteps = value.steps.filter((step) => step?.kind === "provider_request");
    const extractSteps = value.steps.filter((step) => step?.kind === "extract_items");
    const mappingSteps = value.steps.filter((step) => step?.kind === "semantic_mapping");
    const publishSteps = value.steps.filter((step) => step?.kind === "data_product_publish");
    const requestStart = 1;
    const extractIndex = value.steps.findIndex((step) => step?.kind === "extract_items");
    const expectedSequence = (
      value.steps[0]?.kind === "collection_trigger"
      && requestSteps.length >= 1
      && value.steps.slice(requestStart, extractIndex).every((step) => step?.kind === "provider_request")
      && extractSteps.length === 1
      && mappingSteps.length === 1
      && publishSteps.length === 1
      && value.steps.at(-3)?.kind === "extract_items"
      && value.steps.at(-2)?.kind === "semantic_mapping"
      && value.steps.at(-1)?.kind === "data_product_publish"
    );
    if (!expectedSequence) errors.push(`${path}.steps_sequence_invalid`);
    if (value.steps[0]?.collectionProfileDriven !== true) errors.push(`${path}.collection_trigger_must_be_profile_driven`);
    for (const step of requestSteps) {
      if (!step?.capabilityId || step?.mappingContractId !== undefined || step?.dataProductId !== undefined) {
        errors.push(`${path}.provider_request_shape_invalid`);
      }
    }
    const extractStep = extractSteps[0];
    if (!extractStep?.capabilityId || extractStep?.mappingContractId !== undefined || extractStep?.dataProductId !== undefined) {
      errors.push(`${path}.extract_items_shape_invalid`);
    }
    if (!requestSteps.some((step) => step.capabilityId === extractStep?.capabilityId)) errors.push(`${path}.request_extract_capability_mismatch`);
    const mappingStep = mappingSteps[0];
    if (!mappingStep?.mappingContractId || mappingStep?.capabilityId !== undefined || mappingStep?.dataProductId !== undefined) {
      errors.push(`${path}.semantic_mapping_shape_invalid`);
    }
    const publishStep = publishSteps[0];
    if (!publishStep?.dataProductId || publishStep?.nodeType !== "n8n-nodes-ndc.ndcDataProductPublish") {
      errors.push(`${path}.data_product_publish_shape_invalid`);
    }
  }
  if (!isPlainObject(value.invariants)) {
    errors.push(`${path}.invariants_must_be_object`);
  } else {
    rejectUnknownKeys(value.invariants, INVARIANT_KEYS, `${path}.invariants`, errors);
    for (const key of INVARIANT_KEYS) {
      if (value.invariants[key] !== true) errors.push(`${path}.invariants.${key}_must_be_true`);
    }
  }
}

function validateMappingAgainstProduct(mapping, product, errors) {
  const path = `mappingContract.${mapping.id}`;
  if (mapping.target.version !== product.version) errors.push(`${path}.target.version_must_match_data_product`);
  if (mapping.target.ontologyRevision !== product.ontologyRevision) errors.push(`${path}.target.ontologyRevision_must_match_data_product`);
  const semanticTypes = Array.isArray(product.semanticTypes) ? product.semanticTypes : [];
  if (!semanticTypes.includes(mapping.target.semanticType)) errors.push(`${path}.target.semanticType_not_allowed`);
  if (mapping.fact?.semanticType?.constant !== mapping.target.semanticType) {
    errors.push(`${path}.fact.semanticType_must_equal_target_constant`);
  }
  const mappedFields = [...Object.keys(mapping.fact?.attributes || {}), ...(mapping.fact?.geometry ? ["geometry"] : [])];
  if (!sameSet(mappedFields, product.fields)) errors.push(`${path}.fact_fields_must_match_data_product_fields`);
  const contracts = isPlainObject(product.fieldContracts) ? product.fieldContracts : {};
  for (const [field, contract] of Object.entries(contracts)) {
    if (!isPlainObject(contract)) continue;
    if (field === "geometry") {
      if (contract.type === "point" && mapping.fact?.geometry?.type !== "Point") {
        errors.push(`${path}.fact.geometry_contract_must_be_point`);
      }
      if (contract.type === "geometry" && mapping.fact?.geometry?.type !== "GeoJSON") {
        errors.push(`${path}.fact.geometry_contract_must_be_geojson`);
      }
      if (!new Set(["point", "geometry"]).has(contract.type)) errors.push(`${path}.fact.geometry_contract_invalid`);
      if (contract.required === true && mapping.fact?.geometry?.omitIfInvalid === true) {
        errors.push(`${path}.fact.geometry_required_but_mapping_can_omit`);
      }
      continue;
    }
    const expression = mapping.fact?.attributes?.[field];
    if (!isPlainObject(expression)) continue;
    validateExpressionAgainstFieldContract(expression, contract, mapping.derivations, `${path}.fact.attributes.${field}`, errors);
  }
}

function validateFieldContracts(value, fields, path, errors) {
  if (value === undefined) return;
  if (!isPlainObject(value)) return errors.push(`${path}_must_be_object`);
  const names = Object.keys(value);
  if (names.length && !sameSet(names, fields)) errors.push(`${path}_must_exactly_match_fields`);
  for (const [field, contract] of Object.entries(value)) {
    requiredIdentifier(field, `${path}.${field}`, errors);
    if (SECRET_FIELD_NAME.test(field)) errors.push(`${path}.${field}_secret_field_not_allowed`);
    validateFieldContract(contract, `${path}.${field}`, errors);
  }
}

function validateFieldContract(value, path, errors) {
  if (!isPlainObject(value)) return errors.push(`${path}_must_be_object`);
  rejectUnknownKeys(value, FIELD_CONTRACT_KEYS, path, errors);
  if (!FIELD_CONTRACT_TYPES.has(value.type)) errors.push(`${path}.type_invalid`);
  if (typeof value.required !== "boolean") errors.push(`${path}.required_must_be_boolean`);
  if (value.enum !== undefined) {
    if (!Array.isArray(value.enum) || value.enum.length === 0 || new Set(value.enum.map(stableLiteral)).size !== value.enum.length) {
      errors.push(`${path}.enum_must_be_nonempty_unique_array`);
    } else if (new Set(["point", "geometry", "string_array", "telemetry_readings"]).has(value.type)
      || value.enum.some((item) => !fieldContractValueMatchesType(item, value.type))) {
      errors.push(`${path}.enum_value_type_invalid`);
    }
  }
  if (value.minimum !== undefined || value.maximum !== undefined) {
    if (value.type !== "number") errors.push(`${path}.range_only_allowed_for_number`);
    if (value.minimum !== undefined && !Number.isFinite(value.minimum)) errors.push(`${path}.minimum_invalid`);
    if (value.maximum !== undefined && !Number.isFinite(value.maximum)) errors.push(`${path}.maximum_invalid`);
    if (Number.isFinite(value.minimum) && Number.isFinite(value.maximum) && value.minimum > value.maximum) {
      errors.push(`${path}.range_invalid`);
    }
    if (Array.isArray(value.enum) && value.enum.some((item) => (
      typeof item === "number"
      && ((Number.isFinite(value.minimum) && item < value.minimum)
        || (Number.isFinite(value.maximum) && item > value.maximum))
    ))) errors.push(`${path}.enum_value_out_of_range`);
  }
}

function validateExpressionAgainstFieldContract(expression, contract, derivations, path, errors) {
  if (contract.required === true && expression.omitIfMissing === true) {
    errors.push(`${path}_required_but_mapping_can_omit`);
  }
  if (contract.required === true && expression.omitIfInvalid === true) {
    errors.push(`${path}_required_but_mapping_can_omit_invalid`);
  }
  if (expression.paths !== undefined) {
    if (contract.required === false && expression.omitIfMissing !== true) {
      errors.push(`${path}_optional_source_field_must_omit_missing`);
    }
    const expectedCoerce = new Map([["string", "string"], ["number", "number"], ["boolean", "boolean"]]).get(contract.type);
    if (expectedCoerce && expression.coerce !== expectedCoerce) errors.push(`${path}.coerce_must_match_field_contract`);
    if (Array.isArray(contract.enum)) errors.push(`${path}.enum_field_must_use_constant_or_derivation`);
    if (contract.type === "number") {
      if (!Object.is(expression.minimum, contract.minimum)) errors.push(`${path}.minimum_must_match_field_contract`);
      if (!Object.is(expression.maximum, contract.maximum)) errors.push(`${path}.maximum_must_match_field_contract`);
      if ((contract.minimum !== undefined || contract.maximum !== undefined) && expression.omitIfInvalid !== true) {
        errors.push(`${path}.bounded_number_must_omit_invalid`);
      }
    }
  }
  if (expression.constant !== undefined) {
    validateMappedValueAgainstFieldContract(expression.constant, contract, `${path}.constant`, errors);
  }
  if (typeof expression.derive === "string") {
    const derivation = isPlainObject(derivations) ? derivations[expression.derive] : undefined;
    if (!isPlainObject(derivation)) return;
    validateMappedValueAgainstFieldContract(derivation.default, contract, `${path}.derivation.default`, errors);
    if (Array.isArray(contract.enum) && Array.isArray(derivation.rules)) {
      for (const [index, rule] of derivation.rules.entries()) {
        const output = typeof rule === "string" ? rule.slice(rule.lastIndexOf(".") + 1) : undefined;
        validateMappedValueAgainstFieldContract(output, contract, `${path}.derivation.rules[${index}]`, errors);
      }
    }
  }
}

function validateMappedValueAgainstFieldContract(value, contract, path, errors) {
  if (!fieldContractValueMatchesType(value, contract.type)) errors.push(`${path}_type_mismatch`);
  if (Array.isArray(contract.enum) && !contract.enum.some((allowed) => Object.is(allowed, value))) {
    errors.push(`${path}_not_in_field_contract_enum`);
  }
  if (typeof value === "number") {
    if (Number.isFinite(contract.minimum) && value < contract.minimum) errors.push(`${path}_below_field_contract_minimum`);
    if (Number.isFinite(contract.maximum) && value > contract.maximum) errors.push(`${path}_above_field_contract_maximum`);
  }
}

function fieldContractValueMatchesType(value, type) {
  if (type === "string_array") return Array.isArray(value) && value.every((item) => typeof item === "string");
  if (type === "telemetry_readings") return isBoundedTelemetryReadings(value);
  if (type === "point") {
    return isPlainObject(value)
      && value.type === "Point"
      && Array.isArray(value.coordinates)
      && value.coordinates.length === 2
      && value.coordinates.every(Number.isFinite);
  }
  if (type === "geometry") {
    return isPlainObject(value)
      && new Set(["Point", "LineString", "Polygon", "MultiPolygon"]).has(value.type)
      && Array.isArray(value.coordinates);
  }
  return typeof value === type && (type !== "number" || Number.isFinite(value));
}

function stableLiteral(value) {
  return `${typeof value}:${JSON.stringify(value)}`;
}

function validateExpression(value, path, errors) {
  if (!isPlainObject(value)) {
    errors.push(`${path}_must_be_mapping_expression`);
    return;
  }
  rejectUnknownKeys(value, EXPRESSION_KEYS, path, errors);
  const selectors = [Array.isArray(value.paths) && value.paths.length > 0, value.constant !== undefined, typeof value.derive === "string"].filter(Boolean).length;
  if (selectors !== 1) errors.push(`${path}_must_define_exactly_one_source`);
  if (value.paths !== undefined) requiredSourcePathArray(value.paths, `${path}.paths`, errors);
  if (value.strategy !== undefined && value.strategy !== "first_non_empty") errors.push(`${path}.strategy_invalid`);
  if (value.coerce !== undefined && !new Set(["string", "number", "boolean", "unix_or_iso_timestamp"]).has(value.coerce)) {
    errors.push(`${path}.coerce_invalid`);
  }
  if (value.prefix !== undefined && (typeof value.prefix !== "string" || !value.prefix || value.paths === undefined)) {
    errors.push(`${path}.prefix_invalid`);
  }
  if (value.constant !== undefined && !isMappingLiteral(value.constant)) {
    errors.push(`${path}.constant_must_be_scalar_or_scalar_array`);
  }
  if (value.fallback !== undefined && value.fallback !== "collection_received_at") errors.push(`${path}.fallback_invalid`);
  if (value.derive !== undefined) requiredIdentifier(value.derive, `${path}.derive`, errors);
  if (value.omitIfMissing !== undefined && typeof value.omitIfMissing !== "boolean") errors.push(`${path}.omitIfMissing_must_be_boolean`);
  if (value.omitIfInvalid !== undefined && typeof value.omitIfInvalid !== "boolean") errors.push(`${path}.omitIfInvalid_must_be_boolean`);
  const hasGuardPath = value.guardPath !== undefined;
  const hasGuardEquals = value.guardEquals !== undefined;
  if (hasGuardPath !== hasGuardEquals) errors.push(`${path}.guard_requires_path_and_equals`);
  if (hasGuardPath) {
    if (!isCanonicalSourcePath(value.guardPath)) {
      errors.push(`${path}.guardPath_must_be_canonical_dot_path`);
    }
    if (!["string", "number", "boolean"].includes(typeof value.guardEquals)) {
      errors.push(`${path}.guardEquals_must_be_scalar`);
    }
    if (value.omitIfMissing !== true) errors.push(`${path}.guard_requires_omitIfMissing`);
  }
  if (value.minimum !== undefined || value.maximum !== undefined) {
    if (value.coerce !== "number") errors.push(`${path}.range_requires_number_coercion`);
    if (value.minimum !== undefined && !Number.isFinite(value.minimum)) errors.push(`${path}.minimum_invalid`);
    if (value.maximum !== undefined && !Number.isFinite(value.maximum)) errors.push(`${path}.maximum_invalid`);
    if (Number.isFinite(value.minimum) && Number.isFinite(value.maximum) && value.minimum > value.maximum) {
      errors.push(`${path}.range_invalid`);
    }
    if (value.omitIfInvalid !== true) errors.push(`${path}.range_requires_omitIfInvalid`);
  } else if (value.omitIfInvalid !== undefined) {
    errors.push(`${path}.omitIfInvalid_requires_range`);
  }
}

function validateDerivation(value, path, errors) {
  if (!isPlainObject(value)) return errors.push(`${path}_must_be_object`);
  rejectUnknownKeys(value, DERIVATION_KEYS, path, errors);
  if (!new Set([
    "boolean_rule",
    "ordered_rules",
    "flag_set",
    "bounded_readings",
    "bounded_response_lookup",
    "bounded_string_list",
    "bounded_named_values",
  ]).has(value.kind)) errors.push(`${path}.kind_invalid`);
  requiredUniqueIdentifierArray(value.rules, `${path}.rules`, errors);
  if (value.default === undefined) {
    errors.push(`${path}.default_required`);
  } else if (!isMappingLiteral(value.default)) {
    errors.push(`${path}.default_must_be_scalar_or_scalar_array`);
  }
  if (value.parameters !== undefined) {
    if (!isPlainObject(value.parameters)) {
      errors.push(`${path}.parameters_must_be_object`);
    } else {
      for (const [key, item] of Object.entries(value.parameters)) {
        if (!/^[a-z][a-zA-Z0-9]{1,63}$/.test(key)) errors.push(`${path}.parameters.${key}_name_invalid`);
        if (SECRET_FIELD_NAME.test(key)) errors.push(`${path}.parameters.${key}_secret_field_not_allowed`);
        if (!["string", "number", "boolean"].includes(typeof item)) errors.push(`${path}.parameters.${key}_value_invalid`);
      }
    }
  }
  if (value.kind === "bounded_string_list") {
    validateBoundedStringListDerivation(value, path, errors);
  }
  if (value.kind === "bounded_named_values") {
    validateBoundedNamedValuesDerivation(value, path, errors);
  }
  if (value.kind === "bounded_response_lookup") {
    validateBoundedResponseLookupDerivation(value, path, errors);
  }
}

function validateBoundedResponseLookupDerivation(value, path, errors) {
  const parameters = isPlainObject(value.parameters) ? value.parameters : {};
  const allowed = new Set([
    "responseCapabilityId",
    "responseCollectionPath",
    "responseKeyPath",
    "sourceArrayPath",
    "sourceKeyPath",
    "valuePath",
    "resultMode",
    "maxResponseItems",
    "maxSourceKeys",
    "maxItems",
    "maxItemLength",
  ]);
  if (Object.keys(parameters).some((key) => !allowed.has(key))) {
    errors.push(`${path}.parameters_shape_invalid`);
  }
  if (!IDENTIFIER.test(String(parameters.responseCapabilityId || ""))) {
    errors.push(`${path}.parameters.responseCapabilityId_invalid`);
  }
  for (const key of [
    "responseCollectionPath",
    "responseKeyPath",
    "sourceKeyPath",
    "valuePath",
  ]) {
    if (!isCanonicalSourcePath(parameters[key])) {
      errors.push(`${path}.parameters.${key}_must_be_canonical_dot_path`);
    }
  }
  if (SECRET_FIELD_NAME.test(String(parameters.valuePath || ""))) {
    errors.push(`${path}.parameters.valuePath_secret_field_not_allowed`);
  }
  if (
    parameters.sourceArrayPath !== undefined
    && !isCanonicalSourcePath(parameters.sourceArrayPath)
  ) {
    errors.push(`${path}.parameters.sourceArrayPath_must_be_canonical_dot_path`);
  }
  if (!new Set(["first", "list"]).has(parameters.resultMode)) {
    errors.push(`${path}.parameters.resultMode_invalid`);
  }
  if (
    !Number.isInteger(parameters.maxResponseItems)
    || parameters.maxResponseItems < 1
    || parameters.maxResponseItems > 5000
  ) {
    errors.push(`${path}.parameters.maxResponseItems_invalid`);
  }
  if (
    !Number.isInteger(parameters.maxSourceKeys)
    || parameters.maxSourceKeys < 1
    || parameters.maxSourceKeys > 256
  ) {
    errors.push(`${path}.parameters.maxSourceKeys_invalid`);
  }
  if (
    !Number.isInteger(parameters.maxItems)
    || parameters.maxItems < 1
    || parameters.maxItems > 256
  ) {
    errors.push(`${path}.parameters.maxItems_invalid`);
  }
  if (
    !Number.isInteger(parameters.maxItemLength)
    || parameters.maxItemLength < 1
    || parameters.maxItemLength > 512
  ) {
    errors.push(`${path}.parameters.maxItemLength_invalid`);
  }
  if (parameters.resultMode === "list") {
    if (!Array.isArray(value.default) || value.default.length !== 0) {
      errors.push(`${path}.default_must_be_empty_array_for_list`);
    }
  } else if (value.default !== "") {
    errors.push(`${path}.default_must_be_empty_string_for_first`);
  }
}

function validateBoundedStringListDerivation(value, path, errors) {
  const parameters = isPlainObject(value.parameters) ? value.parameters : {};
  const allowed = new Set(["arrayPath", "valuePath", "maxItems", "maxItemLength"]);
  if (Object.keys(parameters).some((key) => !allowed.has(key))) {
    errors.push(`${path}.parameters_shape_invalid`);
  }
  for (const key of ["arrayPath", "valuePath"]) {
    if (!isCanonicalSourcePath(parameters[key])) {
      errors.push(`${path}.parameters.${key}_must_be_canonical_dot_path`);
    }
  }
  if (!Number.isInteger(parameters.maxItems) || parameters.maxItems < 1 || parameters.maxItems > 256) {
    errors.push(`${path}.parameters.maxItems_invalid`);
  }
  if (
    !Number.isInteger(parameters.maxItemLength)
    || parameters.maxItemLength < 1
    || parameters.maxItemLength > 512
  ) {
    errors.push(`${path}.parameters.maxItemLength_invalid`);
  }
  if (!Array.isArray(value.default) || value.default.length !== 0) {
    errors.push(`${path}.default_must_be_empty_array`);
  }
}

function validateBoundedNamedValuesDerivation(value, path, errors) {
  const parameters = isPlainObject(value.parameters) ? value.parameters : {};
  const allowed = new Set([
    "arrayPath",
    "namePath",
    "valuePath",
    "maxItems",
    "maxItemLength",
  ]);
  if (Object.keys(parameters).some((key) => !allowed.has(key))) {
    errors.push(`${path}.parameters_shape_invalid`);
  }
  for (const key of ["arrayPath", "namePath", "valuePath"]) {
    if (!isCanonicalSourcePath(parameters[key])) {
      errors.push(`${path}.parameters.${key}_must_be_canonical_dot_path`);
    }
  }
  if (!Number.isInteger(parameters.maxItems) || parameters.maxItems < 1 || parameters.maxItems > 256) {
    errors.push(`${path}.parameters.maxItems_invalid`);
  }
  if (
    !Number.isInteger(parameters.maxItemLength)
    || parameters.maxItemLength < 1
    || parameters.maxItemLength > 512
  ) {
    errors.push(`${path}.parameters.maxItemLength_invalid`);
  }
  if (!Array.isArray(value.default) || value.default.length !== 0) {
    errors.push(`${path}.default_must_be_empty_array`);
  }
}

function mappingDerivationReferences(value, found = new Set()) {
  if (Array.isArray(value)) {
    value.forEach((item) => mappingDerivationReferences(item, found));
    return found;
  }
  if (!isPlainObject(value)) return found;
  if (typeof value.derive === "string") found.add(value.derive);
  Object.values(value).forEach((item) => mappingDerivationReferences(item, found));
  return found;
}

function mappingSourcePaths(value, found = []) {
  if (Array.isArray(value)) {
    value.forEach((item) => mappingSourcePaths(item, found));
    return found;
  }
  if (!isPlainObject(value)) return found;
  if (Array.isArray(value.paths)) {
    value.paths.forEach((path) => {
      if (isCanonicalSourcePath(path)) found.push(path);
    });
  }
  for (const key of ["kindPath", "polygonPointsPath", "corridorPointsPath", "radiusPath"]) {
    if (isCanonicalSourcePath(value[key])) found.push(value[key]);
  }
  Object.entries(value).forEach(([key, item]) => {
    if (key !== "paths" && key.endsWith("Path") && isCanonicalSourcePath(item)) {
      found.push(item);
    } else if (key !== "paths" && !key.endsWith("Path")) {
      mappingSourcePaths(item, found);
    }
  });
  return found;
}

function validateCredentialVisibleScope(value, path, errors) {
  if (!isPlainObject(value)) {
    errors.push(`${path}_must_be_object`);
    return;
  }
  rejectUnknownKeys(value, ENTITY_SCOPE_KEYS, path, errors);
  if (value.mode !== "all_visible_to_credential") errors.push(`${path}.mode_must_be_all_visible_to_credential`);
  if (value.refresh !== "each_collection_run") errors.push(`${path}.refresh_must_be_each_collection_run`);
  if (value.businessEntityFilter !== "forbidden") errors.push(`${path}.businessEntityFilter_must_be_forbidden`);
}

function validateArtifactArray(value, path, validator, errors) {
  if (!Array.isArray(value) || value.length === 0) {
    errors.push(`${path}_must_be_nonempty_array`);
    return [];
  }
  const seen = new Set();
  value.forEach((item, index) => {
    if (!isPlainObject(item)) return errors.push(`${path}[${index}]_must_be_object`);
    validator(item, `${path}[${index}]`, errors);
    if (seen.has(item.id)) errors.push(`${path}_ids_must_be_unique`);
    seen.add(item.id);
  });
  return value.filter(isPlainObject);
}

function assertExactReferences(references, artifacts, path, errors) {
  if (!sameSet(references, artifacts.map((item) => item.id))) errors.push(`${path}_must_exactly_reference_package_artifacts`);
}

function assertReference(value, allowed, path, errors) {
  if (!allowed.has(value)) errors.push(`${path}_unknown_reference`);
}

function requiredIdentifier(value, path, errors) {
  if (typeof value !== "string" || !IDENTIFIER.test(value)) errors.push(`${path}_invalid`);
}

function requiredSemver(value, path, errors) {
  if (typeof value !== "string" || !SEMVER.test(value)) errors.push(`${path}_must_be_semver`);
}

function requiredString(value, path, errors) {
  if (typeof value !== "string" || !value.trim()) errors.push(`${path}_required`);
}

function requiredOpaqueReference(value, path, errors) {
  if (typeof value !== "string" || !/^ndc-credref:[A-Za-z0-9][A-Za-z0-9._:-]{7,240}$/.test(value)) errors.push(`${path}_invalid`);
}

function validateProviderBaseUrl(value, path, errors) {
  if (typeof value !== "string") return errors.push(`${path}_must_be_https_origin`);
  try {
    const parsed = new URL(value);
    if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.pathname !== "/") {
      errors.push(`${path}_must_be_https_origin`);
    }
  } catch {
    errors.push(`${path}_must_be_https_origin`);
  }
}

function validateProviderRequestPath(value, path, errors) {
  if (typeof value !== "string"
    || value.length < 1
    || value.length > 2048
    || !value.startsWith("/")
    || /[\u0000-\u001f\u007f\\?#]/.test(value)
    || value.split("/").some((segment) => !SAFE_PATH_SEGMENT.test(segment))) {
    errors.push(`${path}_invalid`);
  }
}

function validateStaticQuery(value, path, errors) {
  if (!isPlainObject(value)) return errors.push(`${path}_must_be_object`);
  for (const [key, item] of Object.entries(value)) {
    if (!/^[A-Za-z][A-Za-z0-9._-]{0,63}$/.test(key)) errors.push(`${path}.${key}_name_invalid`);
    if (SECRET_QUERY_KEY.test(key)) errors.push(`${path}.${key}_credential_parameter_not_allowed`);
    if (!["string", "number", "boolean"].includes(typeof item) || (typeof item === "string" && item.length > 2048)) {
      errors.push(`${path}.${key}_value_invalid`);
    }
    if (containsSecretValue(item)) errors.push(`${path}.${key}_must_not_contain_secret_material`);
  }
}

function requiredStringArray(value, path, errors) {
  if (!Array.isArray(value) || value.length === 0) {
    errors.push(`${path}_must_be_nonempty_array`);
    return;
  }
  value.forEach((item, index) => requiredString(item, `${path}[${index}]`, errors));
  if (new Set(value).size !== value.length) errors.push(`${path}_must_not_contain_duplicates`);
}

function requiredSourcePathArray(value, path, errors) {
  requiredStringArray(value, path, errors);
  if (!Array.isArray(value)) return;
  value.forEach((item, index) => {
    if (typeof item === "string" && !isCanonicalSourcePath(item)) {
      errors.push(`${path}[${index}]_must_be_canonical_dot_path`);
    }
  });
}

function requiredCollectionPathArray(value, path, errors) {
  requiredStringArray(value, path, errors);
  if (!Array.isArray(value)) return;
  value.forEach((item, index) => {
    if (item !== "$" && !isCanonicalSourcePath(item)) {
      errors.push(`${path}[${index}]_invalid`);
    }
  });
}

function isCanonicalSourcePath(value) {
  return typeof value === "string"
    && SOURCE_PATH.test(value)
    && value.split(".").every((segment) => !MAGIC_PATH_SEGMENTS.has(segment));
}

function requiredUniqueIdentifierArray(value, path, errors) {
  if (!Array.isArray(value) || value.length === 0) {
    errors.push(`${path}_must_be_nonempty_array`);
    return;
  }
  value.forEach((item, index) => requiredIdentifier(item, `${path}[${index}]`, errors));
  if (new Set(value).size !== value.length) errors.push(`${path}_must_not_contain_duplicates`);
}

function rejectUnknownKeys(value, allowed, path, errors) {
  if (!isPlainObject(value)) return;
  for (const key of Object.keys(value)) {
    if (!allowed.has(key)) errors.push(`${path}.${key}_not_allowed`);
  }
}

function ids(values) {
  return new Set(values.map((item) => item.id));
}

function sameSet(left, right) {
  if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
  const leftSet = new Set(left);
  return leftSet.size === left.length && right.every((item) => leftSet.has(item));
}

function sameArray(left, right) {
  return Array.isArray(left)
    && Array.isArray(right)
    && left.length === right.length
    && left.every((item, index) => item === right[index]);
}

function containsSecretValue(value) {
  if (typeof value === "string") return SECRET_VALUE.test(value);
  if (Array.isArray(value)) return value.some(containsSecretValue);
  if (!isPlainObject(value)) return false;
  return Object.values(value).some(containsSecretValue);
}

function isMappingLiteral(value) {
  if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
  return Array.isArray(value)
    && value.length <= 256
    && value.every((item) => item === null || ["string", "number", "boolean"].includes(typeof item));
}

function isPlainObject(value) {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

function deepFreeze(value) {
  if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
  Object.freeze(value);
  for (const child of Object.values(value)) deepFreeze(child);
  return value;
}

function result(errors) {
  const unique = [...new Set(errors)];
  return Object.freeze({ ok: unique.length === 0, errors: Object.freeze(unique) });
}
