{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bein.ltd/schema/onl/v1/log.schema.json",
  "title": "Open Nutrition Log",
  "description": "A portable personal food and body-weight diary. Consumers MUST ignore members they do not recognise; no object in this schema is closed, which is what allows a minor version to add members without breaking existing readers.",
  "type": "object",
  "required": ["format", "specVersion", "exportedAt", "producer"],
  "properties": {
    "$schema": { "type": "string", "format": "uri" },
    "format": {
      "const": "open-nutrition-log",
      "description": "Identifies the format independently of file name or location."
    },
    "specVersion": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "Semver. Consumers accept any MINOR of a MAJOR they know and refuse a newer MAJOR."
    },
    "exportedAt": { "$ref": "#/$defs/timestamp" },
    "timeZone": {
      "type": "string",
      "description": "IANA zone name (e.g. Europe/Berlin). Offsets alone cannot resolve which local day an instant belongs to."
    },
    "producer": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": { "type": "string" },
        "version": { "type": "string" },
        "platform": { "type": "string" }
      }
    },
    "subject": { "$ref": "#/$defs/subject" },
    "goals": { "type": "array", "items": { "$ref": "#/$defs/goal" } },
    "days": { "type": "array", "items": { "$ref": "#/$defs/day" } },
    "bodyMeasurements": { "type": "array", "items": { "$ref": "#/$defs/measurement" } },
    "extensions": {
      "type": "object",
      "description": "Vendor-private data keyed by reverse-DNS namespace (e.g. ltd.bein.burnin). Consumers MUST ignore namespaces they do not own.",
      "propertyNames": { "pattern": "^[a-z0-9]+(\\.[a-z0-9-]+)+$" }
    }
  },
  "$defs": {
    "timestamp": {
      "type": "string",
      "description": "RFC 3339 with an explicit offset. UTC-normalised instants lose the local-day context.",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$"
    },
    "quantity": {
      "type": "object",
      "description": "A value with a UCUM unit code. Never a bare number.",
      "required": ["value", "unit"],
      "properties": {
        "value": { "type": "number" },
        "unit": {
          "type": "string",
          "description": "UCUM code: kcal, kJ, g, mg, ug, mL, kg, [lb_av], cm, [in_i], a, %, 1.",
          "examples": ["kcal", "g", "kg", "[lb_av]", "cm", "a"]
        }
      }
    },
    "subject": {
      "type": "object",
      "properties": {
        "sex": { "enum": ["male", "female", "other", "unknown"] },
        "age": { "$ref": "#/$defs/quantity", "description": "UCUM 'a' (years)." },
        "height": { "$ref": "#/$defs/quantity" },
        "preferredUnits": {
          "type": "object",
          "description": "Units the subject prefers to read. NOT the units used in this file.",
          "properties": {
            "mass": { "type": "string" },
            "energy": { "type": "string" }
          }
        }
      }
    },
    "goal": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": { "type": "string", "examples": ["targetWeight", "weightDirection"] },
        "value": { "$ref": "#/$defs/quantity" },
        "direction": { "enum": ["loss", "maintain", "gain"] }
      }
    },
    "day": {
      "type": "object",
      "required": ["date"],
      "properties": {
        "date": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "LOCAL calendar date. MUST NOT be derived by converting a timestamp to UTC."
        },
        "energyTarget": { "$ref": "#/$defs/quantity" },
        "energyConsumed": {
          "$ref": "#/$defs/quantity",
          "description": "As recorded. May legitimately differ from the sum of entries."
        },
        "entries": { "type": "array", "items": { "$ref": "#/$defs/foodEntry" } },
        "note": { "type": "string" }
      }
    },
    "foodEntry": {
      "type": "object",
      "required": ["loggedAt"],
      "properties": {
        "loggedAt": { "$ref": "#/$defs/timestamp" },
        "mealType": { "enum": ["breakfast", "lunch", "dinner", "snack"] },
        "name": { "type": "string", "description": "Plain text. The fallback identity when no identifier resolves." },
        "identifiers": {
          "type": "object",
          "description": "Any subset. A consumer resolves whichever it knows.",
          "properties": {
            "gtin": { "type": "string", "description": "Barcode: GTIN-8/12/13/14." },
            "fdcId": { "type": "integer", "description": "USDA FoodData Central id." },
            "offId": { "type": "string", "description": "Open Food Facts product code." }
          }
        },
        "quantity": { "$ref": "#/$defs/quantity" },
        "servingDescription": { "type": "string", "examples": ["1 cup", "2 slices"] },
        "nutrients": {
          "type": "object",
          "description": "FOR THE PORTION CONSUMED. Never per 100 g, never per serving.",
          "properties": {
            "energy": { "$ref": "#/$defs/quantity" },
            "protein": { "$ref": "#/$defs/quantity" },
            "carbohydrate": { "$ref": "#/$defs/quantity" },
            "fat": { "$ref": "#/$defs/quantity" }
          }
        },
        "source": {
          "enum": ["manual", "barcode", "database", "estimated"],
          "description": "How the nutrition data was obtained, not how it was typed."
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Only meaningful when source is 'estimated'."
        },
        "note": { "type": "string" }
      }
    },
    "measurement": {
      "type": "object",
      "required": ["observedAt", "type", "value"],
      "properties": {
        "observedAt": { "$ref": "#/$defs/timestamp" },
        "type": { "type": "string", "examples": ["bodyMass"] },
        "value": { "$ref": "#/$defs/quantity" }
      }
    }
  }
}
