{
  "openapi": "3.1.0",
  "info": {
    "title": "API Almanach — Optimiseur de congés",
    "version": "2.4.0",
    "description": "Calcule des périodes de congés optimisées pour une personne, un couple ou une famille selon le calendrier français ou territorial, les rythmes hebdomadaires ou tournants, les demi-journées, les compteurs, les contraintes temporelles, les vacances scolaires, la garde et les prévisions Bison Futé. Les règles employeur et leur avertissement RH sont rendus séparément pour chaque adulte."
  },
  "servers": [{ "url": "https://almanach-moderne.pages.dev" }],
  "paths": {
    "/api/optimize": {
      "get": {
        "summary": "Décrire l'API",
        "responses": { "200": { "description": "Description et exemple" } }
      },
      "post": {
        "operationId": "optimizeLeave",
        "summary": "Calculer un plan de congés",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OptimizeRequest" } } }
        },
        "responses": {
          "200": { "description": "Plan optimisé", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OptimizeResponse" } } } },
          "400": { "description": "Paramètres invalides" }
        }
      }
    },
    "/api/couple": {
      "get": {
        "summary": "Décrire l'optimisation en couple",
        "responses": { "200": { "description": "Objectifs et exemple à deux profils" } }
      },
      "post": {
        "operationId": "optimizeCoupleLeave",
        "summary": "Coordonner les congés de deux adultes",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CoupleRequest" } } }
        },
        "responses": {
          "200": { "description": "Plan commun, résultats individuels, équité et compromis", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CoupleResponse" } } } },
          "400": { "description": "Paramètres invalides ou second profil absent" }
        }
      }
    },
    "/api/family": {
      "get": {
        "summary": "Décrire l'optimisation familiale",
        "responses": { "200": { "description": "Objectifs, garde et exemple de foyer" } }
      },
      "post": {
        "operationId": "optimizeFamilyLeave",
        "summary": "Coordonner les congés, les enfants et la garde",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FamilyRequest" } } }
        },
        "responses": {
          "200": { "description": "Vacances communes, couverture, équité et affectations", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FamilyResponse" } } } },
          "400": { "description": "Paramètres invalides, adulte ou enfant absent" }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "health",
        "summary": "Vérifier la disponibilité de l'API",
        "responses": { "200": { "description": "Service disponible" } }
      }
    }
  },
  "components": {
    "schemas": {
      "LeaveType": {
        "type": "object",
        "required": ["name", "days"],
        "properties": {
          "id": { "type": "string", "description": "Identifiant stable du compteur", "examples": ["cp"] },
          "name": { "type": "string", "examples": ["CP"] },
          "days": { "type": "number", "minimum": 0, "maximum": 366, "multipleOf": 0.5, "examples": [25, 12.5] },
          "validFrom": { "type": "string", "format": "date", "description": "Premier jour où le compteur peut être utilisé ; début de l'année par défaut" },
          "expiresOn": { "type": "string", "format": "date", "description": "Dernier jour où le compteur peut être utilisé ; fin de l'année par défaut" },
          "principalEligible": { "type": "boolean", "description": "Autoriser ce compteur pour construire le congé principal", "default": false }
        }
      },
      "PlannedLeave": {
        "type": "object",
        "required": ["type", "start", "end"],
        "properties": {
          "type": { "type": "string" },
          "counterId": { "type": "string", "description": "Identifiant du compteur débité ; type reste accepté pour compatibilité" },
          "start": { "type": "string", "format": "date" },
          "end": { "type": "string", "format": "date" }
        }
      },
      "DatePeriod": {
        "type": "object",
        "required": ["start", "end"],
        "properties": {
          "start": { "type": "string", "format": "date" },
          "end": { "type": "string", "format": "date" }
        }
      },
      "WorkWeek": {
        "type": "object",
        "required": ["workingDays"],
        "properties": {
          "workingDays": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "description": "0=dimanche, 6=samedi" },
          "halfDays": { "type": "array", "uniqueItems": true, "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "default": [], "description": "Sous-ensemble de workingDays débité à 0,5 jour." }
        }
      },
      "WorkSchedule": {
        "type": "object",
        "required": ["type", "anchorDate", "cycle"],
        "properties": {
          "type": { "type": "string", "enum": ["weekly", "rotation"] },
          "anchorDate": { "type": "string", "format": "date", "description": "Début de la semaine de référence du roulement." },
          "cycle": { "type": "array", "minItems": 1, "maxItems": 12, "items": { "$ref": "#/components/schemas/WorkWeek" } }
        }
      },
      "CompanyRules": {
        "type": "object",
        "properties": {
          "countingMode": { "type": "string", "enum": ["scheduled", "full_day"], "default": "scheduled" },
          "approvalRequired": { "type": "boolean", "default": true },
          "minimumNoticeDays": { "type": "integer", "minimum": 0, "maximum": 366, "default": 30 },
          "maxConsecutiveChargedDays": { "type": ["number", "null"], "minimum": 0.5, "maximum": 366, "multipleOf": 0.5, "default": null }
        }
      },
      "SeasonSignal": {
        "allOf": [
          { "$ref": "#/components/schemas/DatePeriod" },
          { "type": "object", "required": ["kind"], "properties": { "kind": { "type": "string", "enum": ["low", "high"] }, "source": { "type": "string" } } }
        ]
      },
      "SchoolPeriod": {
        "type": "object",
        "required": ["start", "endExclusive", "zones"],
        "properties": {
          "start": { "type": "string", "format": "date" },
          "endExclusive": { "type": "string", "format": "date", "description": "Premier jour hors vacances." },
          "label": { "type": "string" },
          "zones": { "type": "array", "items": { "type": "string", "enum": ["A", "B", "C"] } },
          "official": { "type": "boolean", "default": true },
          "source": { "type": "string", "default": "Éducation nationale" }
        }
      },
      "TrafficEvent": {
        "type": "object",
        "required": ["date", "direction", "level", "zones"],
        "properties": {
          "date": { "type": "string", "format": "date" },
          "period": { "type": "string" },
          "direction": { "type": "string", "enum": ["departure", "return"] },
          "level": { "type": "integer", "minimum": 1, "maximum": 3 },
          "zones": { "type": "array", "items": { "type": "integer", "minimum": 1, "maximum": 6 } },
          "official": { "type": "boolean" },
          "source": { "type": "string" },
          "sourceUrl": { "type": ["string", "null"], "format": "uri" }
        }
      },
      "LeaveAssignment": {
        "type": "object",
        "required": ["date", "counterId", "counterName"],
        "properties": {
          "date": { "type": "string", "format": "date" },
          "counterId": { "type": "string" },
          "counterName": { "type": "string" },
          "days": { "type": "number", "multipleOf": 0.5, "description": "Charge exacte de cette date." },
          "units": { "type": "integer", "description": "Charge interne en demi-journées : 1=0,5 jour, 2=1 jour." },
          "counterParts": { "type": "array", "description": "Répartition éventuelle d'une date entre plusieurs compteurs." },
          "telework": { "type": "boolean", "description": "Indique que la date est télétravaillée ; elle reste travaillée." },
          "personId": { "type": "string", "description": "Identifiant de l'adulte en mode couple" },
          "personName": { "type": "string", "description": "Nom affiché de l'adulte en mode couple ou famille" },
          "purpose": { "type": "string", "enum": ["childcare"], "description": "Présent lorsque le jour est posé spécifiquement pour la garde." }
        }
      },
      "CounterSummary": {
        "type": "object",
        "required": ["id", "name", "initialDays", "bookedDays", "availableDays", "recommendedDays", "remainingDays"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "initialDays": { "type": "number", "multipleOf": 0.5 },
          "bookedDays": { "type": "number", "multipleOf": 0.5 },
          "availableDays": { "type": "number", "multipleOf": 0.5 },
          "recommendedDays": { "type": "number", "multipleOf": 0.5 },
          "remainingDays": { "type": "number", "multipleOf": 0.5 },
          "validFrom": { "type": "string", "format": "date" },
          "expiresOn": { "type": "string", "format": "date" },
          "principalEligible": { "type": "boolean" }
        }
      },
      "OptimizedPeriod": {
        "type": "object",
        "properties": {
          "start": { "type": "string", "format": "date" },
          "end": { "type": "string", "format": "date" },
          "calendarDays": { "type": "integer" },
          "ouvrableDays": { "type": "integer" },
          "principal": { "type": "boolean", "description": "Cette période couvre le congé principal légal d'au moins 12 jours ouvrables continus entre mai et octobre." },
          "leaveDays": { "type": "array", "items": { "type": "string", "format": "date" } },
          "leaveAssignments": { "type": "array", "items": { "$ref": "#/components/schemas/LeaveAssignment" } },
          "leaveDaysCount": { "type": "number", "multipleOf": 0.5 },
          "efficiency": { "type": "number" },
          "preferenceMatches": {
            "type": "object",
            "properties": {
              "preferredDays": { "type": "integer" },
              "festiveDays": { "type": "integer" },
              "festive": { "type": "array", "items": { "type": "string", "enum": ["christmas", "new_year"] } },
              "avoidedDays": { "type": "integer" },
              "schoolDays": { "type": "integer" },
              "trafficEvents": { "type": "integer" }
            }
          },
          "scoreEffects": {
            "type": "object",
            "properties": {
              "school": { "type": "number" },
              "traffic": { "type": "number" }
            }
          },
          "trafficMatches": { "type": "array", "items": { "$ref": "#/components/schemas/TrafficEvent" } },
          "dataSources": { "type": "object", "description": "Provenance et caractère officiel ou estimé des données ayant influencé la période." },
          "reasons": { "type": "array", "items": { "type": "string" } }
        }
      },
      "OptimizeRequest": {
        "type": "object",
        "properties": {
          "year": { "type": "integer", "minimum": 2020, "maximum": 2035, "default": 2026 },
          "optimizeAll": { "type": "boolean", "default": true, "description": "Utiliser tout le solde optimisable ; false conserve le reliquat et ne retient que les meilleures périodes." },
          "workingDays": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "default": [1, 2, 3, 4, 5], "description": "Rythme hebdomadaire simple ; 0=dimanche, 6=samedi." },
          "halfDays": { "type": "array", "uniqueItems": true, "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "default": [], "description": "Demi-journées du rythme simple, incluses dans workingDays." },
          "workSchedule": { "$ref": "#/components/schemas/WorkSchedule", "description": "Cycle hebdomadaire ou roulement de 1 à 12 semaines. Remplace le rythme simple lorsqu'il est fourni." },
          "territory": { "type": "string", "enum": ["metropolitan", "alsace_moselle", "guadeloupe", "martinique", "guyane", "reunion", "mayotte", "saint_barthelemy", "saint_martin"], "default": "metropolitan" },
          "includeGoodFriday": { "type": "boolean", "default": false, "description": "Alsace-Moselle uniquement, pour les communes où le Vendredi saint est férié." },
          "teleworkDays": { "type": "array", "uniqueItems": true, "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "default": [], "description": "Jours télétravaillés hebdomadaires ; ils restent travaillés." },
          "teleworkPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "default": [] },
          "companyRules": { "$ref": "#/components/schemas/CompanyRules" },
          "leaveTypes": { "type": "array", "items": { "$ref": "#/components/schemas/LeaveType" } },
          "planned": { "type": "array", "items": { "$ref": "#/components/schemas/PlannedLeave" } },
          "mandatoryPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/PlannedLeave" }, "default": [], "description": "Périodes obligatoires facultatives, par exemple une fermeture d'entreprise. Leurs jours travaillés sont déduits du solde et exclus des nouvelles suggestions." },
          "preferredPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "default": [], "description": "Périodes que le classement doit privilégier sans les rendre obligatoires." },
          "festivePreferences": { "type": "array", "uniqueItems": true, "items": { "type": "string", "enum": ["christmas", "new_year"] }, "default": [], "description": "Fêtes à privilégier. L'interface envoie christmas et new_year par défaut. new_year peut prolonger une coupure jusqu'au premier dimanche de janvier sans débiter de congé sur l'année suivante." },
          "avoidedPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "default": [], "description": "Périodes pénalisées dans le classement mais encore utilisables si le plan global l'exige." },
          "forbiddenPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "default": [], "description": "Périodes fortes dont aucun jour travaillé ne peut être proposé." },
          "reserveDays": { "type": "number", "minimum": 0, "maximum": 366, "multipleOf": 0.5, "default": 0, "description": "Jours à conserver hors optimisation. Le moteur consomme tout le solde disponible moins cette réserve." },
          "targetPeriodCount": { "type": ["integer", "null"], "minimum": 1, "maximum": 12, "default": null, "description": "Nombre souhaité de périodes." },
          "minPeriodDays": { "type": ["integer", "null"], "minimum": 3, "maximum": 366, "default": null, "description": "Durée calendaire minimale de chaque coupure." },
          "maxPeriodDays": { "type": ["integer", "null"], "minimum": 3, "maximum": 366, "default": null, "description": "Durée calendaire maximale de chaque coupure." },
          "targetLongestPeriodDays": { "type": ["integer", "null"], "minimum": 3, "maximum": 366, "default": null, "description": "Durée souhaitée de la plus longue coupure." },
          "schoolPreference": { "type": "string", "enum": ["seek", "neutral", "avoid"], "default": "neutral", "description": "Rechercher les vacances communes à toutes les zones sélectionnées, ignorer le calendrier scolaire ou éviter toute période appartenant à l'une des zones. Avec festivePreferences, les semaines de Noël et du Nouvel An sélectionnées sont des exceptions explicites à l'évitement ; elles sont détaillées dans dataPreferences.school.festiveExceptionDays et festiveExceptions." },
          "schoolZones": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "enum": ["A", "B", "C"] }, "default": ["A"], "description": "Une à trois zones scolaires sélectionnées simultanément." },
          "schoolPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/SchoolPeriod" }, "default": [], "description": "Périodes officielles normalisées. L'interface les charge depuis l'open data de l'Éducation nationale." },
          "trafficPreference": { "type": "string", "enum": ["seek", "neutral", "avoid"], "default": "neutral", "description": "Rechercher, ignorer ou éviter les événements routiers filtrés." },
          "trafficDirection": { "type": "string", "enum": ["both", "departure", "return"], "default": "both" },
          "trafficZone": { "oneOf": [{ "type": "string", "const": "all" }, { "type": "integer", "minimum": 1, "maximum": 6 }], "default": "all" },
          "trafficMinimumLevel": { "type": "integer", "minimum": 1, "maximum": 3, "default": 1, "description": "1=difficile, 2=très difficile, 3=extrêmement difficile." },
          "trafficEvents": { "type": "array", "items": { "$ref": "#/components/schemas/TrafficEvent" }, "description": "Remplacement facultatif du calendrier Bison Futé embarqué, notamment pour des tests ou une source externe." },
          "seasonPreference": { "type": "string", "enum": ["seek_low", "neutral", "avoid_high"], "default": "neutral" },
          "seasonSignals": { "type": "array", "items": { "$ref": "#/components/schemas/SeasonSignal" }, "default": [], "description": "Signaux facultatifs et sourcés de basse saison ou haute fréquentation." },
          "legalRule": { "type": "boolean", "default": true, "description": "Contrôler un congé principal d'au moins 12 jours ouvrables continus entre le 1er mai et le 31 octobre." },
          "strategy": {
            "type": "string",
            "enum": ["efficiency", "continuous", "hybrid"],
            "default": "hybrid",
            "description": "efficiency maximise les jours gagnés avec des périodes courtes réparties ; continuous privilégie les longues périodes ; hybrid équilibre rendement et durée."
          }
        }
      },
      "CoupleAdult": {
        "allOf": [
          { "$ref": "#/components/schemas/OptimizeRequest" },
          {
            "type": "object",
            "required": ["id", "name", "workingDays", "leaveTypes"],
            "properties": {
              "id": { "type": "string", "minLength": 1 },
              "name": { "type": "string", "minLength": 1, "maxLength": 100 }
            }
          }
        ]
      },
      "CoupleRequest": {
        "type": "object",
        "required": ["year", "adults"],
        "properties": {
          "year": { "type": "integer", "minimum": 2020, "maximum": 2035 },
          "objective": {
            "type": "string",
            "enum": ["maximum_common", "long_common", "balanced"],
            "default": "maximum_common",
            "description": "Maximum de coupures communes, plus longue coupure commune ou compromis équitable."
          },
          "adults": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": { "$ref": "#/components/schemas/CoupleAdult" }
          }
        }
      },
      "CommonPeriod": {
        "type": "object",
        "properties": {
          "start": { "type": "string", "format": "date" },
          "end": { "type": "string", "format": "date" },
          "calendarDays": { "type": "integer" },
          "leaveAssignments": { "type": "array", "items": { "$ref": "#/components/schemas/LeaveAssignment" } },
          "leaveDaysByAdult": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string", "format": "date" } } }
        }
      },
      "CoupleResponse": {
        "type": "object",
        "properties": {
          "year": { "type": "integer" },
          "objective": { "type": "string" },
          "adults": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "name": { "type": "string" },
                "result": { "$ref": "#/components/schemas/OptimizeResponse" }
              }
            }
          },
          "commonPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/CommonPeriod" } },
          "commonDays": { "type": "integer" },
          "longestCommonDays": { "type": "integer" },
          "individualPeriods": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/OptimizedPeriod" } } },
          "equity": { "type": "object", "description": "Score sur 100, contributions individuelles et explication." },
          "compromises": { "type": "array", "items": { "type": "string" } },
          "compromiseDetails": { "type": "array", "description": "Compromis classés par cause et gravité.", "items": { "$ref": "#/components/schemas/CompromiseDetail" } },
          "exportResult": { "$ref": "#/components/schemas/OptimizeResponse" },
          "disclaimer": { "type": "string" }
        }
      },
      "FamilyChild": {
        "type": "object",
        "required": ["id", "name", "schoolZones", "schoolPeriods", "closures", "custody"],
        "properties": {
          "id": { "type": "string", "minLength": 1 },
          "name": { "type": "string", "minLength": 1, "maxLength": 100 },
          "schoolZones": { "type": "array", "items": { "type": "string", "enum": ["A", "B", "C"] }, "description": "Zone propre à cet enfant." },
          "schoolPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "description": "Vacances inclusives applicables à cet enfant." },
          "closures": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "description": "Dates où une garde doit être assurée." },
          "custody": {
            "type": "object",
            "required": ["type"],
            "properties": {
              "type": { "type": "string", "enum": ["full", "alternating"] },
              "referenceDate": { "type": "string", "format": "date", "description": "Date située dans une semaine de référence pour la garde alternée." },
              "presentOnReferenceWeek": { "type": "boolean", "default": true }
            }
          },
          "custodyPeriods": {
            "type": "array",
            "items": {
              "allOf": [
                { "$ref": "#/components/schemas/DatePeriod" },
                { "type": "object", "required": ["present"], "properties": { "present": { "type": "boolean" } } }
              ]
            },
            "description": "Exceptions prioritaires au rythme de garde habituel."
          }
        }
      },
      "FamilyRequest": {
        "type": "object",
        "required": ["year", "adults", "children"],
        "properties": {
          "year": { "type": "integer", "minimum": 2020, "maximum": 2035 },
          "objective": { "type": "string", "enum": ["family_vacation", "coverage", "family_hybrid"], "default": "family_hybrid" },
          "adults": { "type": "array", "minItems": 2, "maxItems": 2, "items": { "$ref": "#/components/schemas/CoupleAdult" } },
          "children": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/FamilyChild" } },
          "thirdPartyCare": { "type": "array", "items": { "$ref": "#/components/schemas/DatePeriod" }, "default": [], "description": "Périodes où une garde externe couvre les fermetures sans congé." }
        }
      },
      "CoverageAssignment": {
        "type": "object",
        "properties": {
          "date": { "type": "string", "format": "date" },
          "coveredBy": { "type": "string", "enum": ["adult", "third_party"] },
          "adultId": { "type": "string" },
          "adultName": { "type": "string" },
          "childIds": { "type": "array", "items": { "type": "string" } },
          "leaveRequired": { "type": "boolean" },
          "counterId": { "type": "string" },
          "counterName": { "type": "string" }
        }
      },
      "CompromiseDetail": {
        "type": "object",
        "required": ["code", "severity", "title", "detail"],
        "properties": {
          "code": { "type": "string", "description": "Code stable pour l'interface et les intégrations." },
          "severity": { "type": "string", "enum": ["warning", "error"] },
          "title": { "type": "string" },
          "detail": { "type": "string" },
          "adults": { "type": "array", "items": { "type": "string" } },
          "childId": { "type": "string" },
          "dates": { "type": "array", "items": { "type": "string", "format": "date" } }
        }
      },
      "ChildCoverage": {
        "type": "object",
        "required": ["childId", "requiredDays", "coveredDays", "uncoveredDates", "coveragePercent"],
        "properties": {
          "childId": { "type": "string" },
          "childName": { "type": "string" },
          "requiredDays": { "type": "integer" },
          "coveredDays": { "type": "integer" },
          "uncoveredDates": { "type": "array", "items": { "type": "string", "format": "date" } },
          "coveragePercent": { "type": "integer", "minimum": 0, "maximum": 100 }
        }
      },
      "FamilyResponse": {
        "type": "object",
        "properties": {
          "year": { "type": "integer" },
          "objective": { "type": "string" },
          "adults": { "type": "array", "items": { "type": "object" } },
          "children": { "type": "array", "items": { "type": "object" } },
          "familyPeriods": { "type": "array", "items": { "$ref": "#/components/schemas/CommonPeriod" } },
          "familyDays": { "type": "integer" },
          "longestFamilyDays": { "type": "integer" },
          "coverage": {
            "type": "object",
            "properties": {
              "requiredDays": { "type": "integer" },
              "coveredDays": { "type": "integer" },
              "uncoveredDates": { "type": "array", "items": { "type": "string", "format": "date" } },
              "feasible": { "type": "boolean" },
              "assignments": { "type": "array", "items": { "$ref": "#/components/schemas/CoverageAssignment" } },
              "equity": { "type": "object" },
              "children": { "type": "array", "items": { "$ref": "#/components/schemas/ChildCoverage" } }
            }
          },
          "couple": { "type": "object", "description": "Résultats communs et individuels des deux adultes." },
          "compromises": { "type": "array", "items": { "type": "string" } },
          "compromiseDetails": { "type": "array", "items": { "$ref": "#/components/schemas/CompromiseDetail" } },
          "exportResult": { "$ref": "#/components/schemas/OptimizeResponse" },
          "disclaimer": { "type": "string" }
        }
      },
      "OptimizeResponse": {
        "type": "object",
        "properties": {
          "year": { "type": "integer" },
          "strategy": { "type": "string" },
          "availableLeaveDays": { "type": "number", "multipleOf": 0.5 },
          "optimizeAll": { "type": "boolean", "description": "Indique si le moteur a été autorisé à consommer tout le solde optimisable." },
          "optimizableLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Solde disponible après retrait de la réserve demandée." },
          "usedLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Nombre exact de jours ou demi-journées proposés par le moteur." },
          "totalRestDays": { "type": "integer" },
          "gainedDays": { "type": "integer" },
          "remainingLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Solde total restant, réserve comprise." },
          "reservedLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Réserve effectivement conservée." },
          "unallocatedLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Part du solde optimisable impossible à placer sans enfreindre une contrainte forte." },
          "bookedLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Charge exacte déjà immobilisée, tous compteurs confondus." },
          "mandatoryLeaveDays": { "type": "number", "multipleOf": 0.5, "description": "Charge exacte immobilisée par les périodes obligatoires." },
          "workSchedule": { "$ref": "#/components/schemas/WorkSchedule" },
          "territorialCalendar": { "type": "object", "description": "Territoire sélectionné et fériés réellement exclus." },
          "telework": { "type": "object", "description": "Configuration de télétravail, sans transformation en repos." },
          "companyValidation": { "type": "object", "description": "Règles d'entreprise, violations, avertissements et rappel de validation RH." },
          "counters": { "type": "array", "items": { "$ref": "#/components/schemas/CounterSummary" } },
          "periods": { "type": "array", "items": { "$ref": "#/components/schemas/OptimizedPeriod" } },
          "legalRule": { "type": "object" },
          "constraints": {
            "type": "object",
            "description": "État explicable des contraintes et préférences.",
            "properties": {
              "feasible": { "type": "boolean" },
              "satisfied": { "type": "array", "items": { "type": "string" } },
              "violations": { "type": "array", "items": { "type": "string" } },
              "compromises": { "type": "array", "items": { "type": "string" } }
            }
          },
          "dataPreferences": {
            "type": "object",
            "description": "Préférences festives retenues, intentions, filtres, disponibilité des sources et nombre de correspondances scolaires/routières réellement retenues."
          },
          "disclaimer": { "type": "string" }
        }
      }
    }
  }
}
