openapi: 3.1.0
info:
  title: tevaxia.lu API
  version: "1.0.0"
  description: |
    API publique de tevaxia.lu — estimation immobilière TEGOVA-conforme
    couvrant 100 communes luxembourgeoises et outils connexes.

    **Authentification** : toutes les requêtes nécessitent une clé API
    transmise via l'en-tête `X-API-Key` ou `Authorization: Bearer <key>`.
    Créez et gérez vos clés dans votre profil tevaxia.lu > Tableau de
    bord API.

    **Rate limiting** : chaque clé est soumise à un quota selon son tier
    (free, pro, enterprise, sandbox). Les en-têtes de réponse
    `X-RateLimit-Remaining` et `X-RateLimit-Reset` renseignent l'état.
  contact:
    name: Support tevaxia.lu
    email: contact@tevaxia.lu
    url: https://www.tevaxia.lu
  license:
    name: Proprietary
servers:
  - url: https://www.tevaxia.lu
    description: Production
  - url: https://preview.tevaxia.lu
    description: Preview (environnement de pré-production Vercel)
security:
  - ApiKeyHeader: []
  - BearerAuth: []
tags:
  - name: Estimation
    description: Estimation de valeur immobilière (TEGOVA EVS 2025 + hédonique)
  - name: Agencies
    description: Endpoints B2B pour agences immobilières
  - name: PropCalc
    description: Rendement et cashflow multi-pays
  - name: Evaluation
    description: MLV, DCF, capitalisation directe (méthodes EVS 2025)
  - name: Costs
    description: Frais d'acquisition et taxes
  - name: Fiscal
    description: Plus-values, impôts sur revenus locatifs
  - name: Data
    description: Données de marché (STATEC, BCL, ICV, Euribor)
paths:
  /api/v1/estimation:
    options:
      summary: Pré-vol CORS
      responses:
        "204":
          description: CORS OK
    post:
      tags: [Estimation]
      summary: Estimation d'un bien unique
      description: |
        Calcule la valeur estimée d'un bien immobilier luxembourgeois à partir
        de sa commune (ou quartier), sa surface et plusieurs caractéristiques
        d'ajustement. Retourne une fourchette basse/centrale/haute et un
        indice de confiance.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EstimationInput"
            examples:
              luxembourg-belair:
                summary: Appartement Belair 90m²
                value:
                  commune: "Luxembourg"
                  quartier: "Belair"
                  surface: 90
                  nbChambres: 2
                  etage: "entre"
                  etat: "renove"
                  exterieur: "balcon"
                  parking: true
                  classeEnergie: "B"
                  typeBien: "appartement"
                  estNeuf: false
      responses:
        "200":
          description: Estimation calculée
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EstimationResponse"
        "400":
          description: Paramètres invalides
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Non authentifié
        "404":
          description: Commune non trouvée
        "429":
          description: Quota dépassé
  /api/v1/estimation/batch:
    options:
      summary: Pré-vol CORS
      responses:
        "204":
          description: CORS OK
    post:
      tags: [Estimation]
      summary: Estimation en lot (jusqu'à 1000 biens)
      description: |
        Traite un tableau de biens en une seule requête. Chaque item est
        calculé indépendamment ; les erreurs individuelles n'interrompent
        pas le traitement global. Idéal pour une réévaluation périodique
        d'un portefeuille hypothécaire.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [items]
              properties:
                items:
                  type: array
                  maxItems: 1000
                  items:
                    $ref: "#/components/schemas/EstimationInput"
      responses:
        "200":
          description: Lot traité (succès partiels possibles dans results[].error)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  count: { type: integer }
                  succeeded: { type: integer }
                  failed: { type: integer }
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        index: { type: integer }
                        success: { type: boolean }
                        data: { type: object }
                        error: { type: string }
        "413":
          description: Lot trop volumineux (> 1000 items)
  /api/v1/agences/pdf:
    post:
      tags: [Agencies]
      summary: Rapport d'estimation co-brandé (PDF)
      description: |
        Génère un PDF d'estimation à l'image d'une agence, incluant
        estimation + frais d'acquisition + aides applicables. Auth
        API key ou session avec org_id admin.
      responses:
        "200":
          description: PDF binaire (application/pdf)
        "401":
          description: Non autorisé
  /api/v1/ai/analyze:
    post:
      tags: [AI]
      summary: Analyse IA d'un résultat (commentaire expert)
      description: |
        Génère un commentaire professionnel par LLM (Cerebras Llama 3.3
        par défaut, ou BYOK OpenAI/Anthropic) à partir d'un contexte
        structuré (inputs + résultats d'un calcul) et d'un prompt métier.
        Rate-limit : 5/jour gratuit pour JWT, tier clé API pour X-API-Key.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [context, prompt]
              properties:
                context:
                  type: string
                  description: Inputs + résultats formatés (plain text multiligne)
                prompt:
                  type: string
                  description: Consigne métier pour le LLM
      responses:
        "200":
          description: Analyse IA générée
          content:
            application/json:
              schema:
                type: object
                properties:
                  text: { type: string }
                  model: { type: string }
                  provider: { type: string, enum: [cerebras, groq, openai, anthropic] }
                  remaining: { type: integer, description: "-1 si illimité" }
        "401": { description: Auth requise }
        "429": { description: Quota quotidien atteint }
        "502": { description: Erreur fournisseur IA }
  /api/v1/ai/chat:
    post:
      tags: [AI]
      summary: Conversation multi-tour avec l'assistant immobilier LU
      description: |
        Endpoint conversationnel spécialisé immobilier Luxembourg
        (fiscalité LIR, EVS/TEGOVA, VEFA, copropriété, hôtellerie, KYC).
        Accepte jusqu'à 20 messages par requête.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messages]
              properties:
                messages:
                  type: array
                  maxItems: 20
                  items:
                    type: object
                    required: [role, content]
                    properties:
                      role: { type: string, enum: [user, assistant] }
                      content: { type: string, maxLength: 4000 }
      responses:
        "200":
          description: Réponse de l'assistant
          content:
            application/json:
              schema:
                type: object
                properties:
                  text: { type: string }
                  model: { type: string }
                  provider: { type: string }
                  remaining: { type: integer }
  /api/v1/ai/extract:
    post:
      tags: [AI]
      summary: Extraction structurée depuis PDF/image (BYOK vision)
      description: |
        Extraction vision (OCR intelligent) depuis un document (PDF ou
        image PNG/JPEG) vers un JSON structuré. 4 schémas disponibles :
        bilan_promoteur, plus_values, dpe, facture_immo.
        **Nécessite une clé BYOK Anthropic (PDF) ou OpenAI (images)** —
        les providers gratuits Cerebras/Groq ne supportent pas la vision.
        Limite 8 MB par fichier.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [schema, fileBase64, mediaType]
              properties:
                schema:
                  type: string
                  enum: [bilan_promoteur, plus_values, dpe, facture_immo]
                fileBase64: { type: string, description: "Fichier encodé base64 (sans préfixe data:)" }
                mediaType: { type: string, enum: ["application/pdf", "image/png", "image/jpeg"] }
      responses:
        "200":
          description: Données extraites
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: object, description: "Objet JSON structuré selon le schéma demandé" }
                  schema: { type: string }
                  provider: { type: string, enum: [openai, anthropic] }
                  model: { type: string }
        "400": { description: Schéma inconnu ou mismatch provider/media }
        "402": { description: "Clé BYOK vision requise" }
        "413": { description: "Fichier > 8 MB" }
  /api/v1/mlv:
    post:
      summary: Valeur de prêt hypothécaire (MLV / CRR)
      description: "Calcul Mortgage Lending Value selon EN 17503 + CSSF. Abattement prudentiel sur la Market Value."
      tags: [Evaluation]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [marketValue, assetType]
              properties:
                marketValue: { type: number, example: 650000 }
                assetType: { type: string, enum: [residential, office, retail, industrial, hotel] }
                abattement: { type: number, description: "Abattement prudentiel ex 0.10 = 10%", example: 0.1 }
      responses:
        "200": { description: MLV calculée }
  /api/v1/dcf:
    post:
      summary: DCF multi-locataires (10 ans)
      description: Discounted cash-flow avec reversion, vacance, index ICL, OpEx. Retourne VAN, TRI, valeur terminale.
      tags: [Evaluation]
      responses:
        "200": { description: "Flux + VAN + TRI" }
  /api/v1/capitalisation:
    post:
      summary: Capitalisation directe
      description: "VM = NOI / taux de cap. Taux par asset-type LU."
      tags: [Evaluation]
      responses:
        "200": { description: Valeur capitalisée }
  /api/v1/capital-investi:
    post:
      summary: Capital investi total
      tags: [PropCalc]
      responses:
        "200": { description: Total capital (apport + frais + travaux) }
  /api/v1/frais-acquisition:
    post:
      summary: Frais d'acquisition LU
      description: "Droits d'enregistrement (6/7/10%), frais notaire, TVA logement, Bëllegen Akt."
      tags: [Costs]
      responses:
        "200": { description: Frais détaillés par poste }
  /api/v1/plus-values:
    post:
      summary: Plus-value immobilière LU
      description: "Art. 99ter LIR, demi-taux, abattement Bëllegen Akt, exonération résidence principale."
      tags: [Fiscal]
      responses:
        "200": { description: Plus-value imposable + impôt }
  /api/v1/market-data:
    get:
      summary: Données marché (STATEC + Euribor + OAT + ICV)
      description: "Source STATEC, BCL, INSEE. Rafraîchi quotidiennement."
      tags: [Data]
      responses:
        "200": { description: Indicateurs macro LU }
  /api/v1/propcalc/countries:
    get:
      summary: Liste pays supportés PropCalc
      description: "LU/FR/DE/BE/NL/IT/ES/PT/UK/US avec macro HICP + property growth."
      tags: [PropCalc]
      responses:
        "200": { description: Liste pays + méta }
  /api/v1/propcalc/cashflow:
    post:
      summary: Cashflow locatif multi-pays
      tags: [PropCalc]
      responses:
        "200": { description: Cashflow annuel + cumul 10 ans }
  /api/v1/propcalc/mortgage:
    post:
      summary: Amortissement hypothèque (multi-pays)
      tags: [PropCalc]
      responses:
        "200": { description: Échéancier mensuel/annuel }
  /api/v1/propcalc/yield:
    post:
      summary: Rendement brut + net
      tags: [PropCalc]
      responses:
        "200": { description: Yield calculé }
  /api/v1/propcalc/fees:
    post:
      summary: Frais d'acquisition multi-pays
      tags: [PropCalc]
      responses:
        "200": { description: Frais détaillés par pays }
components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
    BearerAuth:
      type: http
      scheme: bearer
  schemas:
    EstimationInput:
      type: object
      required: [commune, surface]
      properties:
        commune:
          type: string
          description: Nom de la commune luxembourgeoise (ex. "Luxembourg")
        quartier:
          type: string
          description: Quartier optionnel pour affiner (ex. "Belair")
        surface:
          type: number
          minimum: 1
          maximum: 10000
          description: Surface habitable en m²
        nbChambres:
          type: integer
          minimum: 0
          maximum: 20
        etage:
          type: string
          enum: [rdc, entre, dernier, maison]
        etat:
          type: string
          enum: [neuf, renove, bon, rafraichir, renover]
        exterieur:
          type: string
          enum: [aucun, balcon, terrasse, jardin]
        parking:
          type: boolean
        classeEnergie:
          type: string
          enum: [A, B, C, D, E, F, G, NC]
        typeBien:
          type: string
          enum: [appartement, maison]
        estNeuf:
          type: boolean
    EstimationResponse:
      type: object
      properties:
        success: { type: boolean }
        data:
          type: object
          properties:
            estimationBasse: { type: number }
            estimationCentrale: { type: number }
            estimationHaute: { type: number }
            prixM2Ajuste: { type: number }
            confiance:
              type: string
              enum: [forte, moyenne, faible]
            ajustements:
              type: array
              items:
                type: object
                properties:
                  labelKey: { type: string }
                  pct: { type: number }
        meta:
          type: object
          properties:
            api_key_name: { type: string }
            tier:
              type: string
              enum: [free, pro, enterprise, sandbox]
            method: { type: string }
    ErrorResponse:
      type: object
      properties:
        success: { type: boolean, example: false }
        error: { type: string }
