{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.liteshop.me/schemas/site-v0.1.json",
  "title": "Liteshop Site Manifest v0.1",
  "type": "object",
  "additionalProperties": false,
  "required": ["$schema", "specVersion", "site", "frontend", "capabilities"],
  "properties": {
    "$schema": {
      "const": "https://docs.liteshop.me/schemas/site-v0.1.json"
    },
    "specVersion": {
      "const": "0.1"
    },
    "baseDeployment": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "site": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "defaultLocale", "supportedLocales", "timezone"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120
        },
        "defaultLocale": {
          "$ref": "#/$defs/locale"
        },
        "supportedLocales": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/locale"
          }
        },
        "timezone": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "description": "IANA timezone, for example Europe/Paris"
        }
      }
    },
    "frontend": {
      "type": "object",
      "additionalProperties": false,
      "required": ["entry"],
      "properties": {
        "entry": {
          "$ref": "#/$defs/htmlPath"
        },
        "notFound": {
          "$ref": "#/$defs/htmlPath"
        },
        "fallback": {
          "$ref": "#/$defs/htmlPath",
          "description": "HTML file served with status 200 for paths without a file extension that match no file, for client-side routers"
        }
      }
    },
    "capabilities": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "forms": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/form"
          }
        },
        "bookings": {
          "$ref": "#/$defs/bookings"
        },
        "catalogue": {
          "$ref": "#/$defs/catalogue"
        }
      }
    }
  },
  "$defs": {
    "id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]{0,62}$"
    },
    "locale": {
      "type": "string",
      "pattern": "^[a-z]{2}(?:-[A-Z]{2})?$"
    },
    "path": {
      "type": "string",
      "minLength": 1,
      "maxLength": 240,
      "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))[^\\\\]+$"
    },
    "htmlPath": {
      "allOf": [
        { "$ref": "#/$defs/path" },
        { "type": "string", "pattern": "\\.html$" }
      ]
    },
    "localizedText": {
      "type": "object",
      "minProperties": 1,
      "additionalProperties": {
        "type": "string",
        "minLength": 1,
        "maxLength": 200
      },
      "propertyNames": {
        "$ref": "#/$defs/locale"
      }
    },
    "option": {
      "type": "object",
      "additionalProperties": false,
      "required": ["value", "label"],
      "properties": {
        "value": {
          "$ref": "#/$defs/id"
        },
        "label": {
          "$ref": "#/$defs/localizedText"
        }
      }
    },
    "field": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "type", "label", "required"],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "type": {
          "enum": ["text", "email", "tel", "textarea", "number", "checkbox", "select"]
        },
        "label": {
          "$ref": "#/$defs/localizedText"
        },
        "required": {
          "type": "boolean"
        },
        "minLength": {
          "type": "integer",
          "minimum": 0,
          "maximum": 10000
        },
        "maxLength": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000
        },
        "minimum": {
          "type": "number"
        },
        "maximum": {
          "type": "number"
        },
        "options": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/option"
          }
        }
      }
    },
    "form": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "fields"],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "$ref": "#/$defs/localizedText"
        },
        "fields": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/field"
          }
        }
      }
    },
    "resource": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name"],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "$ref": "#/$defs/localizedText"
        },
        "quantity": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "description": "How many Bookings the Resource holds at the same time; defaults to 1"
        }
      }
    },
    "offer": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "consumes", "maximumGuests"],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "$ref": "#/$defs/localizedText"
        },
        "unit": {
          "enum": ["nightly", "daily", "timed"],
          "description": "nightly books stays by the night (the default), daily books whole days, timed books fixed-length slots"
        },
        "consumes": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "minimumNights": {
          "type": "integer",
          "minimum": 1,
          "maximum": 365
        },
        "maximumGuests": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000
        },
        "minimumAdvanceDays": {
          "type": "integer",
          "minimum": 0,
          "maximum": 730
        },
        "maximumAdvanceDays": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1095
        },
        "schedule": {
          "$ref": "#/$defs/schedule"
        }
      },
      "if": {
        "properties": { "unit": { "const": "timed" } },
        "required": ["unit"]
      },
      "then": {
        "required": ["schedule"],
        "properties": {
          "schedule": true,
          "minimumNights": false,
          "minimumAdvanceDays": false
        }
      },
      "else": {
        "properties": { "schedule": false },
        "if": {
          "properties": { "unit": { "const": "daily" } },
          "required": ["unit"]
        },
        "then": {
          "properties": { "minimumNights": false }
        },
        "else": {
          "required": ["minimumNights"],
          "properties": { "minimumNights": true }
        }
      }
    },
    "clockTime": {
      "type": "string",
      "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$",
      "description": "Wall-clock time in the Site timezone, HH:MM"
    },
    "schedule": {
      "type": "object",
      "additionalProperties": false,
      "required": ["durationMinutes", "hours"],
      "properties": {
        "durationMinutes": {
          "type": "integer",
          "minimum": 5,
          "maximum": 1440
        },
        "hours": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "items": {
            "$ref": "#/$defs/openingHours"
          }
        },
        "bufferBeforeMinutes": {
          "type": "integer",
          "minimum": 0,
          "maximum": 1440
        },
        "bufferAfterMinutes": {
          "type": "integer",
          "minimum": 0,
          "maximum": 1440
        },
        "minimumNoticeMinutes": {
          "type": "integer",
          "minimum": 0,
          "maximum": 43200
        }
      }
    },
    "openingHours": {
      "type": "object",
      "additionalProperties": false,
      "required": ["days", "from", "to"],
      "properties": {
        "days": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "enum": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
          }
        },
        "from": {
          "$ref": "#/$defs/clockTime"
        },
        "to": {
          "$ref": "#/$defs/clockTime"
        }
      }
    },
    "calendarConnection": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "provider", "mode", "resources", "configuredIn"],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "$ref": "#/$defs/localizedText"
        },
        "provider": {
          "const": "ical"
        },
        "mode": {
          "enum": ["import", "export", "bidirectional"]
        },
        "resources": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "configuredIn": {
          "const": "dashboard"
        }
      }
    },
    "bookings": {
      "type": "object",
      "additionalProperties": false,
      "required": ["resources", "offers", "requestFields", "calendarConnections"],
      "properties": {
        "resources": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/resource"
          }
        },
        "offers": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/offer"
          }
        },
        "requestFields": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/field"
          }
        },
        "calendarConnections": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/calendarConnection"
          }
        }
      }
    },
    "catalogueItem": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind", "name"],
      "not": {
        "properties": {
          "image": true,
          "images": true
        },
        "required": ["image", "images"]
      },
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "kind": {
          "enum": ["product", "service"]
        },
        "name": {
          "$ref": "#/$defs/localizedText"
        },
        "description": {
          "type": "object",
          "minProperties": 1,
          "additionalProperties": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "propertyNames": {
            "$ref": "#/$defs/locale"
          }
        },
        "price": {
          "type": "object",
          "additionalProperties": false,
          "required": ["amount", "currency"],
          "description": "Displayed price; Liteshop takes no payment",
          "properties": {
            "amount": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000000,
              "description": "Amount in minor units, for example cents"
            },
            "currency": {
              "type": "string",
              "pattern": "^[A-Z]{3}$",
              "description": "ISO 4217 currency code, for example EUR"
            }
          }
        },
        "image": {
          "$ref": "#/$defs/path",
          "description": "Bundle path of a PNG, JPEG, WebP or AVIF image; use images for several"
        },
        "images": {
          "type": "array",
          "minItems": 1,
          "maxItems": 8,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/path"
          },
          "description": "Bundle paths of PNG, JPEG, WebP or AVIF images, main image first"
        },
        "maximumQuantity": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Largest quantity a Visitor may request at once; defaults to 1"
        }
      }
    },
    "catalogueCollection": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "items"],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "$ref": "#/$defs/localizedText"
        },
        "items": {
          "type": "array",
          "maxItems": 200,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/id"
          }
        }
      }
    },
    "catalogue": {
      "type": "object",
      "additionalProperties": false,
      "required": ["items", "requestFields"],
      "properties": {
        "items": {
          "type": "array",
          "minItems": 1,
          "maxItems": 200,
          "items": {
            "$ref": "#/$defs/catalogueItem"
          }
        },
        "collections": {
          "type": "array",
          "maxItems": 50,
          "items": {
            "$ref": "#/$defs/catalogueCollection"
          }
        },
        "requestFields": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/field"
          }
        }
      }
    }
  }
}
