{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/matamata/docs/schema.json",
  "title": "Knockout stage source document",
  "type": "object",
  "required": ["rounds"],
  "additionalProperties": false,
  "properties": {
    "tournament": { "type": "string", "minLength": 1 },
    "season": { "type": "string" },
    "render": { "$ref": "#/$defs/render" },
    "rounds": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/round" }
    }
  },
  "$defs": {
    "render": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "max_label_chars": {
          "type": "integer",
          "minimum": 1,
          "default": 22
        },
        "box_width": {
          "oneOf": [
            { "type": "integer", "minimum": 60 },
            { "const": "auto" }
          ],
          "default": "auto",
          "comment": "SVG match-box width: a number in SVG units, or 'auto' (default) to size boxes to their widest content (label capped at max_label_chars, plus crest and score). SVG-only"
        },
        "crest_shape": {
          "type": "string",
          "enum": ["square", "flag"],
          "default": "square"
        },
        "show_metadata": {
          "type": "boolean",
          "default": true,
          "comment": "draw the per-match metadata line (id, then each leg's dt/venue)"
        },
        "dt_format": {
          "type": "string",
          "minLength": 1,
          "default": "dd/MM HH:mm",
          "comment": "Babel/LDML pattern for each leg's dt (parsed as GMT y-m-d HH:MM, then localized by the render language); e.g. 'EEEE dd MMMM, HH:mm'"
        },
        "layout": {
          "type": "string",
          "enum": ["linear", "symmetric"],
          "default": "symmetric",
          "comment": "SVG diagram arrangement: 'symmetric' (default) is the FIFA-style mirrored bracket — halves expanding outward, semifinals meeting in the centre, final lifted above and third place below; 'linear' flows left to right (final last). SVG-only; the HTML table ignores it"
        }
      }
    },
    "round": {
      "type": "object",
      "required": ["name", "matches"],
      "additionalProperties": false,
      "properties": {
        "name": { "type": "string", "minLength": 1 },
        "matches": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/match" }
        }
      }
    },
    "match": {
      "type": "object",
      "additionalProperties": false,
      "comment": "two sides, 1 = top, 2 = bottom; winnerof{n} wires advancement (loserof{n} the third-place mirror, the loser of the referenced match), team{n} names a known side. id is optional (omit it on a match nothing references, e.g. the final or a third-place match). dt/venue here are a fallback used only when there are no legs to carry them",
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "winnerof1": { "type": "string", "minLength": 1 },
        "winnerof2": { "type": "string", "minLength": 1 },
        "loserof1": { "type": "string", "minLength": 1 },
        "loserof2": { "type": "string", "minLength": 1 },
        "team1": { "type": "string", "minLength": 1 },
        "team2": { "type": "string", "minLength": 1 },
        "id1": { "type": ["string", "integer"] },
        "id2": { "type": ["string", "integer"] },
        "legs": {
          "type": "array",
          "items": { "$ref": "#/$defs/leg" }
        },
        "dt": { "type": "string", "minLength": 1 },
        "venue": { "type": "string", "minLength": 1 },
        "winner": { "enum": [1, 2] },
        "settle": {
          "const": false,
          "comment": "opts this match out of KnockoutStage.apply_results' settling"
        }
      }
    },
    "leg": {
      "type": "object",
      "additionalProperties": false,
      "comment": "every field optional: {} is a scheduled leg; 'ref' points at the real game and may coexist with a baked result (live host data wins at render); a leg without team names is read as tie-oriented (1 = the match's top side)",
      "properties": {
        "ref": { "type": ["string", "integer"] },
        "team1": { "type": "string", "minLength": 1 },
        "goals1": { "type": "integer", "minimum": 0 },
        "id1": { "type": ["string", "integer"] },
        "pen1": { "type": "integer", "minimum": 0 },
        "team2": { "type": "string", "minLength": 1 },
        "goals2": { "type": "integer", "minimum": 0 },
        "id2": { "type": ["string", "integer"] },
        "pen2": { "type": "integer", "minimum": 0 },
        "dt": { "type": "string", "minLength": 1 },
        "venue": { "type": "string", "minLength": 1 }
      }
    }
  }
}
