import {
  L2_TEMPLATE_SCHEMA_VERSION,
  PROVIDER_PACKAGE_SCHEMA_VERSION,
  SEMANTIC_MAPPING_SCHEMA_VERSION,
} from "../../../src/provider-package.mjs";

export const GELIOS_PROVIDER_PACKAGE_ID = "gelios.provider.v1";
export const GELIOS_PROVIDER_PACKAGE_VERSION = "1.0.0";
export const GELIOS_POSITIONS_DATA_PRODUCT_ID = "fleet.positions.current.v1";
export const GELIOS_POSITIONS_DATA_PRODUCT_VERSION = "1.0.0";
export const GELIOS_POSITIONS_ONTOLOGY_REVISION = "ontology.map.moving_object.v1";
export const GELIOS_UNIT_SOURCE_ID_PREFIX = "gelios-unit-";

const targetFields = Object.freeze([
  "course_degrees",
  "display_name",
  "elevation_meters",
  "geometry",
  "hdop",
  "horizontal_accuracy_meters",
  "object_kind",
  "operational_status",
  "position_source",
  "position_valid",
  "quality_flags",
  "satellite_count",
  "speed_kph",
]);

export const geliosProviderPackageV1 = deepFreeze({
  schemaVersion: PROVIDER_PACKAGE_SCHEMA_VERSION,
  id: GELIOS_PROVIDER_PACKAGE_ID,
  providerId: "gelios",
  version: GELIOS_PROVIDER_PACKAGE_VERSION,
  manifest: {
    id: "gelios.provider.manifest.v1",
    providerId: "gelios",
    version: GELIOS_PROVIDER_PACKAGE_VERSION,
    ontology: {
      packageId: "gelios",
      revision: "ontology.gelios.v1",
    },
    authModeIds: ["gelios.rest-bearer.v1"],
    capabilityIds: ["gelios.units.current.read"],
    fieldPolicyIds: ["gelios.positions.current.fields.v1"],
    collectionProfileIds: [
      "gelios.positions.current.realtime.v1",
      "gelios.positions.current.manual.v1",
    ],
    dataProductIds: [GELIOS_POSITIONS_DATA_PRODUCT_ID],
    mappingContractIds: ["gelios.units.to.fleet.positions.current.v1"],
    l2TemplateIds: ["gelios.positions.current.l2.v1"],
  },
  authModes: [{
    id: "gelios.rest-bearer.v1",
    kind: "api_token",
    credentialOwner: "ndc_l2_credentials",
    bindingCardinality: "one_per_connection",
    transport: {
      placement: "header",
      name: "Authorization",
    },
    tokenLifecycle: {
      artifacts: ["access", "refresh"],
      requestArtifact: "access",
      refreshMode: "operator_managed",
    },
    secretHandling: {
      store: "ndc_l2_credentials",
      exposeToGraph: false,
      persistInPackage: false,
    },
  }],
  capabilities: [{
    id: "gelios.units.current.read",
    classification: "read",
    status: "implemented",
    authModeId: "gelios.rest-bearer.v1",
    request: {
      method: "GET",
      baseUrl: "https://api.geliospro.com",
      path: "/api/v1/units",
      query: {},
      response: {
        collectionPaths: ["units", "data", "data.units", "result", "$"],
        pagination: "single_bounded_response",
      },
    },
    entityScope: {
      mode: "all_visible_to_credential",
      refresh: "each_collection_run",
      businessEntityFilter: "forbidden",
    },
  }],
  fieldPolicies: [{
    id: "gelios.positions.current.fields.v1",
    version: "1.0.0",
    dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
    targetFields: [...targetFields],
    unknownSourceFields: "drop",
    dynamicSourceFields: "drop_until_classified",
    restrictedSourcePaths: [
      "decrypt_key",
      "hw_id",
      "imei",
      "info",
      "lmsg.address",
      "lmsg.params",
      "lmsg.sensors",
      "lastMsg.address",
      "lastMsg.params",
      "lastMsg.sensors",
      "lastMessage.address",
      "lastMessage.params",
      "lastMessage.sensors",
      "phone",
      "phone2",
      "sensors",
    ],
  }],
  collectionProfiles: [{
    id: "gelios.positions.current.realtime.v1",
    version: "1.0.0",
    mode: "realtime",
    schedule: { intervalMs: 10000 },
    capabilityIds: ["gelios.units.current.read"],
    dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
    mappingContractId: "gelios.units.to.fleet.positions.current.v1",
    fieldPolicyId: "gelios.positions.current.fields.v1",
    l2TemplateId: "gelios.positions.current.l2.v1",
    entityScope: {
      mode: "all_visible_to_credential",
      refresh: "each_collection_run",
      businessEntityFilter: "forbidden",
    },
    batching: { maxFacts: 5000 },
    cardinality: {
      maxCurrentEntities: 5000,
      onExceed: "require_partitioned_data_product",
    },
    retry: {
      maxAttempts: 4,
      backoff: "exponential_with_jitter",
    },
  }, {
    id: "gelios.positions.current.manual.v1",
    version: "1.0.0",
    mode: "manual",
    capabilityIds: ["gelios.units.current.read"],
    dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
    mappingContractId: "gelios.units.to.fleet.positions.current.v1",
    fieldPolicyId: "gelios.positions.current.fields.v1",
    l2TemplateId: "gelios.positions.current.l2.v1",
    entityScope: {
      mode: "all_visible_to_credential",
      refresh: "each_collection_run",
      businessEntityFilter: "forbidden",
    },
    batching: { maxFacts: 5000 },
    cardinality: {
      maxCurrentEntities: 5000,
      onExceed: "require_partitioned_data_product",
    },
    retry: {
      maxAttempts: 4,
      backoff: "exponential_with_jitter",
    },
  }],
  dataProducts: [{
    id: GELIOS_POSITIONS_DATA_PRODUCT_ID,
    version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
    ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
    deliveryMode: "snapshot+patch",
    semanticTypes: ["map.moving_object"],
    fields: [...targetFields],
    history: {
      mode: "sampled",
      intervalMs: 60000,
      strategy: "latest-per-entity-per-bucket",
      retentionDays: 90,
    },
  }],
  mappingContracts: [{
    schemaVersion: SEMANTIC_MAPPING_SCHEMA_VERSION,
    id: "gelios.units.to.fleet.positions.current.v1",
    version: "1.0.0",
    sourceCapabilityId: "gelios.units.current.read",
    fieldPolicyId: "gelios.positions.current.fields.v1",
    target: {
      dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
      version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
      ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
      semanticType: "map.moving_object",
    },
    derivations: {
      position_valid: {
        kind: "boolean_rule",
        rules: [
          "geometry.coordinates_finite_wgs84",
          "provider.gps_valid_not_false",
        ],
        default: false,
      },
      operational_status: {
        kind: "ordered_rules",
        rules: [
          "position_invalid.no_position",
          "observed_age_gt_limit.stale",
          "otherwise.active",
        ],
        default: "active",
        parameters: { staleAfterMs: 60000 },
      },
      quality_flags: {
        kind: "flag_set",
        rules: [
          "geometry_missing.missing_geometry",
          "provider_gps_false.gps_invalid",
          "satellite_count_zero.no_satellites",
          "observed_age_gt_limit.stale",
        ],
        default: [],
        parameters: { staleAfterMs: 60000 },
      },
    },
    fact: {
      sourceId: {
        strategy: "first_non_empty",
        paths: ["id", "unit_id", "unitId"],
        coerce: "string",
        prefix: GELIOS_UNIT_SOURCE_ID_PREFIX,
      },
      semanticType: { constant: "map.moving_object" },
      observedAt: {
        strategy: "first_non_empty",
        paths: [
          "lmsg.time",
          "lmsg.ts",
          "lmsg.timestamp",
          "last_msg.time",
          "last_msg.timestamp",
          "lastMsg.time",
          "lastMessage.time",
          "last_msg_time",
          "lmsg_time",
        ],
        coerce: "unix_or_iso_timestamp",
        fallback: "collection_received_at",
      },
      geometry: {
        type: "Point",
        longitude: {
          strategy: "first_non_empty",
          paths: ["lmsg.lon", "lmsg.lng", "lmsg.longitude", "last_msg.lon", "lastMsg.lon", "lastMsg.lng", "lastMsg.longitude", "lastMessage.lon", "lastMessage.lng", "lastMessage.longitude", "lon", "lng", "longitude"],
          coerce: "number",
        },
        latitude: {
          strategy: "first_non_empty",
          paths: ["lmsg.lat", "lmsg.latitude", "last_msg.lat", "lastMsg.lat", "lastMsg.latitude", "lastMessage.lat", "lastMessage.latitude", "lat", "latitude"],
          coerce: "number",
        },
        omitIfInvalid: true,
      },
      attributes: {
        course_degrees: {
          strategy: "first_non_empty",
          paths: ["lmsg.course", "lmsg.angle", "last_msg.course", "lastMsg.course", "lastMsg.angle", "lastMessage.course", "lastMessage.angle", "course"],
          coerce: "number",
          omitIfMissing: true,
        },
        display_name: {
          strategy: "first_non_empty",
          paths: ["name", "unit_name", "title", "label"],
          coerce: "string",
        },
        elevation_meters: {
          strategy: "first_non_empty",
          paths: ["lmsg.height", "lmsg.altitude", "last_msg.height", "lastMsg.height", "lastMsg.altitude", "lastMessage.height", "lastMessage.altitude", "height", "altitude"],
          coerce: "number",
          omitIfMissing: true,
        },
        hdop: {
          strategy: "first_non_empty",
          paths: ["lmsg.hdop", "last_msg.hdop", "lastMsg.hdop", "lastMessage.hdop", "gps_hdop", "hdop"],
          coerce: "number",
          omitIfMissing: true,
        },
        horizontal_accuracy_meters: {
          strategy: "first_non_empty",
          paths: ["lmsg.accuracy", "last_msg.accuracy", "lastMsg.accuracy", "lastMessage.accuracy", "gps_accuracy_m", "accuracy_m", "accuracy"],
          coerce: "number",
          omitIfMissing: true,
        },
        object_kind: { constant: "tracked_unit" },
        operational_status: { derive: "operational_status" },
        position_source: { constant: "gelios" },
        position_valid: { derive: "position_valid" },
        quality_flags: { derive: "quality_flags" },
        satellite_count: {
          strategy: "first_non_empty",
          paths: ["lmsg.sats", "lmsg.satellites", "last_msg.sats", "lastMsg.sats", "lastMsg.satellites", "lastMessage.sats", "lastMessage.satellites", "gps_sats", "sats", "satellites"],
          coerce: "number",
          omitIfMissing: true,
        },
        speed_kph: {
          strategy: "first_non_empty",
          paths: ["lmsg.speed", "last_msg.speed", "lastMsg.speed", "lastMessage.speed", "speed"],
          coerce: "number",
          omitIfMissing: true,
        },
      },
    },
  }],
  l2Templates: [{
    schemaVersion: L2_TEMPLATE_SCHEMA_VERSION,
    id: "gelios.positions.current.l2.v1",
    version: "1.0.0",
    runtime: "ndc_l2",
    instanceMode: "one_connection_per_workflow",
    connectionParameters: [
      "tenant_id",
      "connection_id",
      "collection_profile_id",
      "provider_credential_ref",
    ],
    credentialBindings: [{
      role: "provider",
      owner: "ndc_l2_credentials",
      management: "connection_input",
      authModeId: "gelios.rest-bearer.v1",
    }, {
      role: "publisher",
      owner: "ndc_l2_credentials",
      management: "control_plane_managed",
      nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
    }],
    steps: [{
      id: "collection.trigger",
      kind: "collection_trigger",
      collectionProfileDriven: true,
    }, {
      id: "provider.fetch",
      kind: "provider_request",
      capabilityId: "gelios.units.current.read",
    }, {
      id: "provider.extract-items",
      kind: "extract_items",
      capabilityId: "gelios.units.current.read",
    }, {
      id: "ontology.map",
      kind: "semantic_mapping",
      mappingContractId: "gelios.units.to.fleet.positions.current.v1",
    }, {
      id: "data-product.publish",
      kind: "data_product_publish",
      dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
      nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
    }],
    invariants: {
      oneConnectionPerInstance: true,
      providerSecretByReferenceOnly: true,
      publisherBindingControlPlaneManaged: true,
      callerScopeForbidden: true,
      providerSpecificServiceForbidden: true,
    },
  }],
});

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;
}
