{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://quadruper.invalid/contracts/geoapi-common.v1.schema.json",
  "title": "GeoApiCommon",
  "description": "Shared definitions for the geo-api /v2 contract.",
  "$defs": {
    "lnglat": {
      "title": "LngLat",
      "type": "array",
      "prefixItems": [
        { "type": "number", "minimum": -180, "maximum": 180 },
        { "type": "number", "minimum": -90, "maximum": 90 }
      ],
      "minItems": 2,
      "maxItems": 2,
      "items": false
    },
    "bbox": {
      "title": "BBox",
      "description": "[minLng, minLat, maxLng, maxLat]",
      "type": "array",
      "prefixItems": [
        { "type": "number", "minimum": -180, "maximum": 180 },
        { "type": "number", "minimum": -90, "maximum": 90 },
        { "type": "number", "minimum": -180, "maximum": 180 },
        { "type": "number", "minimum": -90, "maximum": 90 }
      ],
      "minItems": 4,
      "maxItems": 4,
      "items": false
    },
    "profile": {
      "title": "Profile",
      "description": "Any short name is accepted here; geo-api answers unsupported_profile for names the deployment does not offer.",
      "type": "string",
      "pattern": "^[a-z][a-z_]{0,31}$"
    },
    "nullableString": {
      "title": "NullableString",
      "oneOf": [{ "type": "string" }, { "type": "null" }]
    },
    "attribution": {
      "title": "Attribution",
      "type": "array",
      "minItems": 1,
      "items": {
        "title": "AttributionEntry",
        "type": "object",
        "properties": {
          "source": { "enum": ["osm", "local"] },
          "text": { "type": "string", "minLength": 1 },
          "license": { "type": "string", "minLength": 1 }
        },
        "required": ["source", "text", "license"],
        "additionalProperties": false
      }
    },
    "conditions": {
      "title": "Conditions",
      "type": "object",
      "properties": {
        "assessed": { "type": "boolean", "const": false },
        "reason": { "enum": ["not_configured"] }
      },
      "required": ["assessed", "reason"],
      "additionalProperties": false
    },
    "address": {
      "title": "Address",
      "type": "object",
      "properties": {
        "house_number": { "type": "string" },
        "street": { "type": "string" },
        "locality": { "type": "string" },
        "district": { "type": "string" },
        "city": { "type": "string" },
        "county": { "type": "string" },
        "state": { "type": "string" },
        "postcode": { "type": "string" },
        "country": { "type": "string" },
        "country_code": { "type": "string", "pattern": "^[a-z]{2}$" }
      },
      "additionalProperties": false
    },
    "place": {
      "title": "Place",
      "type": "object",
      "properties": {
        "source": { "enum": ["osm", "local"] },
        "id": {
          "oneOf": [
            { "type": "string", "pattern": "^[A-Za-z0-9/_-]{1,80}$" },
            { "type": "null" }
          ]
        },
        "name": { "$ref": "#/$defs/nullableString" },
        "label": { "type": "string", "minLength": 1 },
        "location": { "$ref": "#/$defs/lnglat" },
        "category": { "$ref": "#/$defs/nullableString" },
        "type": { "$ref": "#/$defs/nullableString" },
        "address": { "$ref": "#/$defs/address" }
      },
      "required": ["source", "id", "name", "label", "location", "category", "type", "address"],
      "additionalProperties": false
    },
    "ring": {
      "title": "LinearRing",
      "type": "array",
      "minItems": 4,
      "items": { "$ref": "#/$defs/lnglat" }
    },
    "polygonal": {
      "title": "Polygonal",
      "oneOf": [
        {
          "title": "Polygon",
          "type": "object",
          "properties": {
            "type": { "const": "Polygon" },
            "coordinates": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/ring" } }
          },
          "required": ["type", "coordinates"],
          "additionalProperties": false
        },
        {
          "title": "MultiPolygon",
          "type": "object",
          "properties": {
            "type": { "const": "MultiPolygon" },
            "coordinates": {
              "type": "array",
              "minItems": 1,
              "items": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/ring" } }
            }
          },
          "required": ["type", "coordinates"],
          "additionalProperties": false
        }
      ]
    },
    "step": {
      "title": "Step",
      "type": "object",
      "properties": {
        "type": {
          "enum": [
            "depart", "arrive", "waypoint", "continue",
            "turn_left", "turn_right", "turn_slight_left", "turn_slight_right",
            "turn_sharp_left", "turn_sharp_right", "keep_left", "keep_right",
            "u_turn", "roundabout"
          ]
        },
        "instruction": { "type": "string" },
        "name": { "$ref": "#/$defs/nullableString" },
        "distance_m": { "type": "number", "minimum": 0 },
        "duration_s": { "type": "number", "minimum": 0 },
        "location": { "$ref": "#/$defs/lnglat" },
        "geometry_index": {
          "type": "array",
          "prefixItems": [{ "type": "integer", "minimum": 0 }, { "type": "integer", "minimum": 0 }],
          "minItems": 2,
          "maxItems": 2,
          "items": false
        },
        "exit": { "type": "integer", "minimum": 1 }
      },
      "required": ["type", "instruction", "name", "distance_m", "duration_s", "location", "geometry_index"],
      "additionalProperties": false,
      "if": { "type": "object", "properties": { "type": { "const": "roundabout" } }, "required": ["type"] },
      "then": { "required": ["exit"] },
      "else": { "not": { "required": ["exit"] } }
    },
    "leg": {
      "title": "Leg",
      "type": "object",
      "properties": {
        "distance_m": { "type": "number", "minimum": 0 },
        "duration_s": { "type": "number", "minimum": 0 },
        "steps": { "type": "array", "items": { "$ref": "#/$defs/step" } }
      },
      "required": ["distance_m", "duration_s", "steps"],
      "additionalProperties": false
    },
    "waypoint": {
      "title": "Waypoint",
      "type": "object",
      "properties": {
        "location": { "$ref": "#/$defs/lnglat" },
        "snapped_distance_m": { "type": "number", "minimum": 0 }
      },
      "required": ["location", "snapped_distance_m"],
      "additionalProperties": false
    },
    "matrixPoint": {
      "title": "MatrixPoint",
      "type": "object",
      "properties": {
        "location": { "$ref": "#/$defs/lnglat" },
        "snapped": { "type": "boolean" }
      },
      "required": ["location", "snapped"],
      "additionalProperties": false
    },
    "matrixRow": {
      "title": "MatrixRow",
      "type": "array",
      "minItems": 1,
      "maxItems": 25,
      "items": { "oneOf": [{ "type": "number", "minimum": 0 }, { "type": "null" }] }
    }
  }
}
