{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id":     "https://civitasconnect.digital/core/mapping/v1",
  "title":   "CORE Mapping",
  "description": "A declarative field-to-field mapping between two CORE DataStructures. Each exported Mapping document carries '$schema': 'https://civitasconnect.digital/core/mapping/v1' so it is self-describing.\n\nNote: the 'x-core-ref' annotations on 'source' and 'target' declare that each value is the CORE URN of a DataStructure (an Element URN is also accepted for backward compatibility); existence is checked by registry-aware services.",
  "type": "object",
  "required": ["$schema", "id", "fields"],
  "properties": {
    "$schema": {
      "type": "string",
      "enum": [
        "https://civitasconnect.digital/core/mapping/v1",
        "https://civitasconnect.digital/core-dataset/v1#/$defs/Mapping"
      ]
    },
    "id": {
      "type": "string",
      "description": "Versioned CORE URN of this Mapping.",
      "pattern": "^urn:core:[^:]+:[^:]+:mapping:[^:]+:[^:]+:[^:]+(:[^:]+)?$"
    },
    "title":       { "type": "string", "description": "Human-readable display name." },
    "description": { "type": "string" },
    "source": {
      "$ref": "#/$defs/StructureUrn",
      "description": "Versioned CORE URN of the source DataStructure (the structure being read from). An Element URN is also accepted for backward compatibility.",
      "x-core-ref": { "type": "urn:core:type:DataStructure" }
    },
    "target": {
      "$ref": "#/$defs/StructureUrn",
      "description": "Versioned CORE URN of the target DataStructure (the structure being written to). An Element URN is also accepted for backward compatibility.",
      "x-core-ref": { "type": "urn:core:type:DataStructure" }
    },
    "fields": {
      "type": "object",
      "description": "Map of target field paths to their field operations.",
      "additionalProperties": { "$ref": "#/$defs/MappingField" }
    }
  },
  "$defs": {
    "StructureUrn": {
      "description": "Versioned CORE URN of a DataStructure that a Mapping binds (an Element URN is also accepted for backward compatibility).",
      "type": "string",
      "pattern": "^urn:core:[^:]+:[^:]+:(datastructure|element):[^:]+:[^:]+:[^:]+(:[^:]+)?$"
    },
    "CopyFieldOperation": {
      "description": "Copies a value from an input field path. The path is given as 'sourcePath' (the mapping editor's field name) or 'input' (legacy alias); at least one must be present.",
      "type": "object",
      "required": ["op"],
      "properties": {
        "op":         { "type": "string", "const": "copy" },
        "input":      { "type": "string", "description": "JSONPath expression, e.g. '$.sensorId'." },
        "sourcePath": { "type": "string", "description": "JSONPath expression, e.g. '$.sensorId' (editor field name)." }
      },
      "anyOf": [
        { "required": ["input"] },
        { "required": ["sourcePath"] }
      ],
      "additionalProperties": false
    },
    "ConcatFieldOperation": {
      "description": "Concatenates multiple input field values with an optional separator.",
      "type": "object",
      "required": ["op", "inputs"],
      "properties": {
        "op":        { "type": "string", "const": "concat" },
        "inputs":    { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/MappingField" }, "description": "Inputs concatenated in order — each a JSONPath string or a nested operation." },
        "separator": { "type": "string", "default": "" }
      },
      "additionalProperties": false
    },
    "ConstFieldOperation": {
      "description": "Writes a constant literal value into the target field regardless of source data.",
      "type": "object",
      "required": ["op", "value"],
      "properties": {
        "op":        { "type": "string", "const": "const" },
        "value":     { "description": "The constant value. Can be a string, number, or boolean." },
        "valueType": { "type": "string", "description": "Optional editor hint for how to interpret 'value' (e.g. string/number/boolean)." }
      },
      "additionalProperties": false
    },
    "ToStringFieldOperation": {
      "description": "Converts the input value to its string representation.",
      "type": "object",
      "required": ["op", "input"],
      "properties": {
        "op":    { "type": "string", "const": "toString" },
        "input": { "$ref": "#/$defs/MappingField", "description": "Value to convert — a JSONPath string or a nested operation." }
      },
      "additionalProperties": false
    },
    "ToIntFieldOperation": {
      "description": "Parses the input value as an integer.",
      "type": "object",
      "required": ["op", "input"],
      "properties": {
        "op":    { "type": "string", "const": "toInt" },
        "input": { "$ref": "#/$defs/MappingField", "description": "Value to convert — a JSONPath string or a nested operation." }
      },
      "additionalProperties": false
    },
    "ToFloatFieldOperation": {
      "description": "Parses the input value as a floating-point number.",
      "type": "object",
      "required": ["op", "input"],
      "properties": {
        "op":    { "type": "string", "const": "toFloat" },
        "input": { "$ref": "#/$defs/MappingField", "description": "Value to convert — a JSONPath string or a nested operation." }
      },
      "additionalProperties": false
    },
    "ToUuidFieldOperation": {
      "description": "Converts the input string to a UUID.",
      "type": "object",
      "required": ["op", "input"],
      "properties": {
        "op":    { "type": "string", "const": "toUuid" },
        "input": { "$ref": "#/$defs/MappingField", "description": "Value to convert — a JSONPath string or a nested operation." }
      },
      "additionalProperties": false
    },
    "ToDateTimeFieldOperation": {
      "description": "Parses the input string into a date-time value using a date pattern.",
      "type": "object",
      "required": ["op", "input", "pattern"],
      "properties": {
        "op":      { "type": "string", "const": "toDateTime" },
        "input":   { "$ref": "#/$defs/MappingField", "description": "String value to parse — a JSONPath string or a nested operation." },
        "pattern": { "type": "string", "description": "Date pattern used to parse the input, e.g. \"yyyy-MM-dd'T'HH:mm:ssXXX\"." }
      },
      "additionalProperties": false
    },
    "ToDateFieldOperation": {
      "description": "Parses the input string into a date/time value using a date pattern.",
      "type": "object",
      "required": ["op", "input", "pattern"],
      "properties": {
        "op":      { "type": "string", "const": "toDate" },
        "input":   { "$ref": "#/$defs/MappingField", "description": "String value to parse — a JSONPath string or a nested operation." },
        "pattern": { "type": "string", "description": "Date pattern used to parse the input, e.g. 'yyyy-MM-dd'." }
      },
      "additionalProperties": false
    },
    "FormatFieldOperation": {
      "description": "Formats the input date/time value into a string using a date pattern.",
      "type": "object",
      "required": ["op", "input", "pattern"],
      "properties": {
        "op":      { "type": "string", "const": "format" },
        "input":   { "$ref": "#/$defs/MappingField", "description": "Date/time value to format — a JSONPath string or a nested operation." },
        "pattern": { "type": "string", "description": "Date pattern used to format the output, e.g. 'yyyy-MM-dd'." }
      },
      "additionalProperties": false
    },
    "GeoPointFieldOperation": {
      "description": "Builds a GeoJSON-style point from a longitude and latitude input.",
      "type": "object",
      "required": ["op", "lon", "lat"],
      "properties": {
        "op":  { "type": "string", "const": "geoPoint" },
        "lon": { "$ref": "#/$defs/MappingField", "description": "Longitude — a JSONPath string or a nested operation." },
        "lat": { "$ref": "#/$defs/MappingField", "description": "Latitude — a JSONPath string or a nested operation." }
      },
      "additionalProperties": false
    },
    "MappingOperation": {
      "description": "A single field mapping rule.",
      "oneOf": [
        { "$ref": "#/$defs/CopyFieldOperation" },
        { "$ref": "#/$defs/ConcatFieldOperation" },
        { "$ref": "#/$defs/ConstFieldOperation" },
        { "$ref": "#/$defs/ToStringFieldOperation" },
        { "$ref": "#/$defs/ToIntFieldOperation" },
        { "$ref": "#/$defs/ToFloatFieldOperation" },
        { "$ref": "#/$defs/ToUuidFieldOperation" },
        { "$ref": "#/$defs/ToDateTimeFieldOperation" },
        { "$ref": "#/$defs/ToDateFieldOperation" },
        { "$ref": "#/$defs/FormatFieldOperation" },
        { "$ref": "#/$defs/GeoPointFieldOperation" }
      ]
    },
    "MappingField": {
      "description": "A field mapping — either a shorthand input path string or a full operation object.",
      "oneOf": [
        { "type": "string", "description": "Shorthand for a copy operation: value is the input path." },
        { "$ref": "#/$defs/MappingOperation" }
      ]
    }
  }
}
