{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cpaikr.github.io/fs/schema/0.2/fs-document.schema.json",
  "title": "FS 0.2 document",
  "type": "object",
  "additionalProperties": false,
  "required": ["formatVersion", "entity", "scope", "units", "periods", "statements"],
  "properties": {
    "$schema": {
      "const": "https://cpaikr.github.io/fs/schema/0.2/fs-document.schema.json"
    },
    "formatVersion": { "const": "0.2" },
    "documentId": { "$ref": "#/$defs/id" },
    "entity": { "$ref": "#/$defs/entity" },
    "scope": { "$ref": "#/$defs/scope" },
    "units": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/unit" }
    },
    "periods": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/period" }
    },
    "statements": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/statement" }
    },
    "validationSnapshot": { "$ref": "#/$defs/validationSnapshot" }
  },
  "$defs": {
    "id": {
      "type": "string",
      "pattern": "^[A-Za-z][A-Za-z0-9._-]*$"
    },
    "nonEmptyString": {
      "type": "string",
      "minLength": 1
    },
    "decimal": {
      "type": "string",
      "pattern": "^(?:0|-?(?:[1-9][0-9]*)(?:\\.[0-9]*[1-9])?|-?0\\.[0-9]*[1-9])$"
    },
    "nonNegativeDecimal": {
      "type": "string",
      "pattern": "^(?:0|(?:[1-9][0-9]*)(?:\\.[0-9]*[1-9])?|0\\.[0-9]*[1-9])$"
    },
    "entity": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "name": { "$ref": "#/$defs/nonEmptyString" }
      }
    },
    "scope": {
      "type": "object",
      "additionalProperties": false,
      "required": ["label"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "label": { "$ref": "#/$defs/nonEmptyString" }
      }
    },
    "unit": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "label", "measure", "scale"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "label": { "$ref": "#/$defs/nonEmptyString" },
        "measure": { "$ref": "#/$defs/nonEmptyString" },
        "scale": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "defaultTolerance": { "$ref": "#/$defs/nonNegativeDecimal" }
      }
    },
    "date": {
      "type": "string",
      "pattern": "^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])$"
    },
    "instantPeriod": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind", "date"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "kind": { "const": "instant" },
        "date": { "$ref": "#/$defs/date" }
      }
    },
    "durationPeriod": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind", "start", "end"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "kind": { "const": "duration" },
        "start": { "$ref": "#/$defs/date" },
        "end": { "$ref": "#/$defs/date" }
      }
    },
    "period": {
      "if": {
        "type": "object",
        "properties": { "kind": { "const": "instant" } },
        "required": ["kind"]
      },
      "then": { "$ref": "#/$defs/instantPeriod" },
      "else": { "$ref": "#/$defs/durationPeriod" }
    },
    "unavailableValue": {
      "type": "object",
      "additionalProperties": false,
      "required": ["unavailable"],
      "properties": {
        "unavailable": { "const": true }
      }
    },
    "valueCell": {
      "if": { "type": "string" },
      "then": { "$ref": "#/$defs/decimal" },
      "else": {
        "if": { "type": "null" },
        "then": { "type": "null" },
        "else": { "$ref": "#/$defs/unavailableValue" }
      }
    },
    "item": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "label", "unit", "values"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "label": { "$ref": "#/$defs/nonEmptyString" },
        "description": { "$ref": "#/$defs/nonEmptyString" },
        "unit": { "$ref": "#/$defs/id" },
        "values": {
          "type": "object",
          "propertyNames": { "$ref": "#/$defs/id" },
          "additionalProperties": { "$ref": "#/$defs/valueCell" }
        },
        "rollupTo": { "$ref": "#/$defs/id" }
      }
    },
    "statement": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "label", "periods", "items"],
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "label": { "$ref": "#/$defs/nonEmptyString" },
        "periods": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/id" }
        },
        "items": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/item" }
        }
      }
    },
    "applicationKey": {
      "type": "object",
      "additionalProperties": false,
      "required": ["statement", "parent", "period"],
      "properties": {
        "statement": { "$ref": "#/$defs/id" },
        "parent": { "$ref": "#/$defs/id" },
        "period": { "$ref": "#/$defs/id" }
      }
    },
    "cellIdentity": {
      "type": "object",
      "additionalProperties": false,
      "required": ["statement", "item", "period"],
      "properties": {
        "statement": { "$ref": "#/$defs/id" },
        "item": { "$ref": "#/$defs/id" },
        "period": { "$ref": "#/$defs/id" }
      }
    },
    "satisfiedApplication": {
      "type": "object",
      "additionalProperties": false,
      "required": ["key", "status", "actual", "expected", "difference", "tolerance"],
      "properties": {
        "key": { "$ref": "#/$defs/applicationKey" },
        "status": { "const": "satisfied" },
        "actual": { "$ref": "#/$defs/decimal" },
        "expected": { "$ref": "#/$defs/decimal" },
        "difference": { "$ref": "#/$defs/decimal" },
        "tolerance": { "$ref": "#/$defs/nonNegativeDecimal" }
      }
    },
    "unsatisfiedApplication": {
      "type": "object",
      "additionalProperties": false,
      "required": ["key", "status", "actual", "expected", "difference", "tolerance"],
      "properties": {
        "key": { "$ref": "#/$defs/applicationKey" },
        "status": { "const": "unsatisfied" },
        "actual": { "$ref": "#/$defs/decimal" },
        "expected": { "$ref": "#/$defs/decimal" },
        "difference": { "$ref": "#/$defs/decimal" },
        "tolerance": { "$ref": "#/$defs/nonNegativeDecimal" }
      }
    },
    "errorApplication": {
      "type": "object",
      "additionalProperties": false,
      "required": ["key", "status", "reason", "cell"],
      "properties": {
        "key": { "$ref": "#/$defs/applicationKey" },
        "status": { "const": "error" },
        "reason": { "enum": ["missing-value", "unavailable-value"] },
        "cell": { "$ref": "#/$defs/cellIdentity" }
      }
    },
    "applicationResult": {
      "if": {
        "type": "object",
        "properties": { "status": { "const": "satisfied" } },
        "required": ["status"]
      },
      "then": { "$ref": "#/$defs/satisfiedApplication" },
      "else": {
        "if": {
          "type": "object",
          "properties": { "status": { "const": "unsatisfied" } },
          "required": ["status"]
        },
        "then": { "$ref": "#/$defs/unsatisfiedApplication" },
        "else": { "$ref": "#/$defs/errorApplication" }
      }
    },
    "validationSnapshot": {
      "type": "object",
      "additionalProperties": false,
      "required": ["conformance", "calculations", "applications"],
      "properties": {
        "conformance": { "enum": ["conforming", "nonconforming"] },
        "calculations": { "enum": ["not-run", "not-defined", "consistent", "inconsistent"] },
        "applications": {
          "type": "array",
          "items": { "$ref": "#/$defs/applicationResult" }
        }
      },
      "allOf": [
        {
          "if": {
            "properties": { "conformance": { "const": "nonconforming" } },
            "required": ["conformance"]
          },
          "then": {
            "properties": { "calculations": { "const": "not-run" } }
          },
          "else": {
            "properties": {
              "calculations": { "enum": ["not-defined", "consistent", "inconsistent"] }
            }
          }
        },
        {
          "if": {
            "properties": { "calculations": { "enum": ["not-run", "not-defined"] } },
            "required": ["calculations"]
          },
          "then": {
            "properties": { "applications": { "type": "array", "maxItems": 0 } }
          }
        },
        {
          "if": {
            "properties": { "calculations": { "const": "consistent" } },
            "required": ["calculations"]
          },
          "then": {
            "properties": {
              "applications": {
                "type": "array",
                "minItems": 1,
                "items": { "$ref": "#/$defs/satisfiedApplication" }
              }
            }
          }
        },
        {
          "if": {
            "properties": { "calculations": { "const": "inconsistent" } },
            "required": ["calculations"]
          },
          "then": {
            "properties": {
              "applications": {
                "type": "array",
                "minItems": 1,
                "contains": {
                  "type": "object",
                  "properties": { "status": { "enum": ["unsatisfied", "error"] } },
                  "required": ["status"]
                },
                "minContains": 1
              }
            }
          }
        }
      ]
    }
  }
}
