{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://nic01asfr.github.io/Widgets-Grist/schemas/formdef-1.0.schema.json",
  "title": "FormDef",
  "description": "Contrat canonique formulaires Grist (Form Builder)",
  "type": "object",
  "required": [
    "manifest_version",
    "id",
    "title",
    "tableId",
    "composeMode",
    "sections",
    "choices"
  ],
  "additionalProperties": false,
  "properties": {
    "manifest_version": {
      "type": "string",
      "description": "Version du contrat FormDef"
    },
    "id": {
      "type": "string",
      "description": "Identifiant stable du formulaire (slug)"
    },
    "title": {
      "type": "string",
      "description": "Titre affiché du formulaire"
    },
    "description": {
      "type": "string",
      "description": "Description optionnelle"
    },
    "classification": {
      "type": "string",
      "description": "Classification pour projection Survey Manifest / offre-de-service"
    },
    "successMessage": {
      "type": "string",
      "description": "Message affiché après soumission réussie"
    },
    "branding": {
      "type": "object",
      "description": "Logo / images Accueil et Fin",
      "additionalProperties": false,
      "properties": {
        "logoUrl": {
          "type": "string"
        },
        "logoAlt": {
          "type": "string"
        },
        "successImageUrl": {
          "type": "string"
        },
        "successImageAlt": {
          "type": "string"
        }
      }
    },
    "audience": {
      "type": "object",
      "description": "Ciblage personne/groupe via table existante du document (optionnel)",
      "additionalProperties": false,
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "none",
            "bind"
          ]
        },
        "tableId": {
          "type": "string"
        },
        "emailCol": {
          "type": "string"
        },
        "groupCol": {
          "type": "string"
        },
        "probe": {
          "type": "boolean",
          "description": "Reconnaître la personne connectée via formule user.Email (trigger à la création de ligne)"
        }
      }
    },
    "tableId": {
      "type": "string",
      "description": "Identifiant Grist de la table cible des réponses"
    },
    "composeMode": {
      "type": "string",
      "enum": [
        "bind",
        "ensureSchema"
      ],
      "description": "Mode de composition : colonnes existantes ou création/alignement du schéma"
    },
    "sections": {
      "type": "array",
      "description": "Sections (étapes) du formulaire",
      "items": {
        "$ref": "#/definitions/section"
      }
    },
    "choices": {
      "type": "object",
      "description": "Choix statiques indexés par clé (Choice / ChoiceList)",
      "additionalProperties": {
        "type": "array",
        "items": {
          "type": "string"
        }
      }
    }
  },
  "definitions": {
    "section": {
      "type": "object",
      "required": [
        "id",
        "label",
        "fields"
      ],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "description": "Identifiant stable de la section"
        },
        "label": {
          "type": "string",
          "description": "Libellé affiché de la section"
        },
        "gate": {
          "description": "Raccourci Bool (colId) ; équivalent condition field==true. Prefer section.condition.",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "string"
            }
          ]
        },
        "condition": {
          "description": "Condition d'affichage de l'étape (chemins) — règle simple ou AND/OU",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/definitions/condition"
            }
          ]
        },
        "fields": {
          "type": "array",
          "description": "Champs de la section",
          "items": {
            "$ref": "#/definitions/field"
          }
        }
      }
    },
    "field": {
      "type": "object",
      "required": [
        "colId",
        "label",
        "type",
        "widget"
      ],
      "additionalProperties": false,
      "properties": {
        "colId": {
          "type": "string",
          "description": "Identifiant de la colonne Grist cible"
        },
        "label": {
          "type": "string",
          "description": "Libellé affiché du champ"
        },
        "type": {
          "type": "string",
          "description": "Type Grist (Text, Int, Bool, Date, Choice, Ref:T, etc.)"
        },
        "widget": {
          "type": "string",
          "description": "Widget de rendu (text, textarea, select, checkbox, etc.)"
        },
        "required": {
          "type": "boolean",
          "description": "Champ obligatoire à la soumission"
        },
        "options": {
          "type": "object",
          "description": "Options widget / type (placeholder, refTable, visibleCol, step, etc.)",
          "additionalProperties": true
        },
        "condition": {
          "description": "Condition d'affichage { field, operator, value }",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/definitions/condition"
            }
          ]
        },
        "cascade": {
          "description": "Cascade Ref→Ref { parentField, parentRefCol }",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/definitions/cascade"
            }
          ]
        },
        "dynamicFilter": {
          "description": "Filtre dynamique { parentField, filterColumn }",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/definitions/dynamicFilter"
            }
          ]
        },
        "profile": {
          "description": "Profil survey (likert5, rank_place, polarity, etc.)",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "string"
            }
          ]
        },
        "theme": {
          "description": "Thème survey optionnel",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "string"
            }
          ]
        },
        "polarity": {
          "type": "string",
          "description": "Méta polarité survey (peut être vide)"
        }
      }
    },
    "condition": {
      "description": "Règle { field|path, operator, value, source?, bind? } ou composé { op, rules }",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "field": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "source": {
          "type": "string",
          "enum": [
            "field",
            "context",
            "audience"
          ]
        },
        "operator": {
          "type": "string"
        },
        "value": {},
        "bind": {
          "type": "object",
          "additionalProperties": true,
          "description": "Audience : tableId, emailCol, groupCol, groups[]"
        },
        "op": {
          "type": "string",
          "enum": [
            "and",
            "or"
          ]
        },
        "rules": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/condition"
          }
        }
      }
    },
    "cascade": {
      "type": "object",
      "required": [
        "parentField",
        "parentRefCol"
      ],
      "additionalProperties": false,
      "properties": {
        "parentField": {
          "type": "string",
          "description": "colId du champ parent Ref"
        },
        "parentRefCol": {
          "type": "string",
          "description": "Colonne Ref du parent utilisée pour le filtrage"
        }
      }
    },
    "dynamicFilter": {
      "type": "object",
      "required": [
        "parentField",
        "filterColumn"
      ],
      "additionalProperties": false,
      "properties": {
        "parentField": {
          "type": "string",
          "description": "colId du champ parent"
        },
        "filterColumn": {
          "type": "string",
          "description": "Colonne de la table liée enfant comparée à la valeur résolue"
        },
        "parentResolve": {
          "type": "string",
          "enum": [
            "value",
            "refRow"
          ],
          "description": "value = valeur brute du parent (défaut) ; refRow = lit parentValueColumn sur la ligne Ref"
        },
        "parentValueColumn": {
          "type": "string",
          "description": "Colonne lue sur la ligne du parent Ref (si parentResolve=refRow)"
        }
      }
    }
  }
}
