// InstantDevis — moteur fiscal / juridique V2 (local, déterministe, sans réseau)
// Règles explicites par code ; `rulesetVersion` 2. Aucun texte de loi, aucune prétention de conformité absolue.
// S’appuie sur AF_FISCAL.getContext(), AF_PROFILE_STORE, AF_DEVIS.documentOutput.
// Les codes d’issues / warnings sont la référence pour la couche documentaire Légifrance (Worker) : mapping dans `cloudflare/legifrance-piste.mjs`, doc `context.md` §7.4.2 — le moteur local reste seul décisionnel.

const AF_FISCAL_ENGINE = (() => {
  const ENGINE_VERSION = 2;
  const RULESET_VERSION = 2;
  const TOL_TTC = 0.02;
  const TOL_ACOMPTE = 0.05;
  const TOL_LINE_HT_VS_TOTAL = 0.1;

  const DISCLAIMER =
    "InstantDevis n'est pas un cabinet d'expertise comptable ni juridique. Les contrôles ci-dessous sont des repères " +
    "issus des règles de cohérence intégrées (version " + RULESET_VERSION + "). Aucun article de loi n'est cité. " +
    "Vérification humaine, administratif ou comptable nécessaire en cas d'incertitude.";

  /**
   * @param {string} category  Ex. : profile, document, lignes, totals, vat, mention, artisan_identity, date, integrity, engine
   * @param {boolean} [blocking]  Facultatif ; par défaut `true` si severity === "error", sinon `false`
   */
  function _item(code, severity, message, source, category, blocking) {
    const cat = category || "general";
    const blk = blocking === undefined ? severity === "error" : !!blocking;
    return { code, severity, message, source, category: cat, blocking: blk, deterministic: true };
  }

  function _rawProfile() {
    if (typeof AF_PROFILE_STORE === "undefined" || !AF_PROFILE_STORE.get) {
      return {};
    }
    return AF_PROFILE_STORE.get() || {};
  }

  function _statusNorm(d) {
    const s = d && (d.status != null ? d.status : d.statut);
    return String(s == null ? "" : s)
      .toLowerCase()
      .replace(/\s/g, "");
  }

  function isVatModeNoInvoicedVat(vatMode) {
    return String(vatMode || "").toLowerCase() === "franchise";
  }

  function isVatModeExpectsVatCharged(vatMode) {
    const m = String(vatMode || "").toLowerCase();
    return m === "assujetti" || m === "reel_simplifie" || m === "reel_normal";
  }

  function profileRequiresFiscalMention(vatMode, companyPreset) {
    const vm = String(vatMode || "").toLowerCase();
    const pr = String(companyPreset || "").toLowerCase();
    return vm === "franchise" || pr === "auto_entrepreneur";
  }

  function lineQtyPuTva(l) {
    const qty = Number(l.qty != null ? l.qty : l.quantite);
    const pu = Number(l.pu != null ? l.pu : l.prixUnitaire);
    const tvaN =
      l.tva != null || l.tauxTVA != null ? Number(l.tva != null ? l.tva : l.tauxTVA) : NaN;
    return { qty, pu, tva: tvaN };
  }

  function _hasUsableDevisDate(d) {
    const a = d || {};
    const c = (x) => (x != null && String(x).replace(/\s/g, "") !== "");
    if (c(a.dueDate)) return true;
    if (c(a.createdAt)) return true;
    if (c(a.createdAtFull)) return true;
    if (a.signedSnapshot && c(a.signedSnapshot.signedAt)) return true;
    if (c(a.signedAt)) return true;
    return false;
  }

  function buildDocumentSnapshot(d) {
    if (typeof AF_DEVIS === "undefined" || !AF_DEVIS.documentOutput) {
      return { kind: "devis", documentType: "devis", error: "no_AF_DEVIS_documentOutput" };
    }
    const x = d || {};
    const dout = AF_DEVIS.documentOutput(x);
    const hasLignes = Array.isArray(x.lignes) && x.lignes.length > 0;
    const hasItems = !hasLignes && Array.isArray(x.items) && x.items.length > 0 && typeof x.items[0] === "object";
    const addr = String(dout.clientAddress != null ? dout.clientAddress : x.clientAddress || "").trim();
    return {
      kind: "devis",
      documentType: "devis",
      sealed: !!dout.sealed,
      status: _statusNorm(x) || "brouillon",
      title: String(dout.title || x.title || "").trim(),
      clientName: String(dout.clientName || x.clientName || "").trim(),
      clientAddress: addr,
      id: x.id,
      hasDateHint: _hasUsableDevisDate(x),
      lineCount: Array.isArray(dout.lignes) ? dout.lignes.length : 0,
      hasUserEnteredLines: !!(hasLignes || hasItems),
    };
  }

  function _defaultContext() {
    return {
      companyPreset: "custom",
      legalForm: "",
      vatMode: "",
      trade: "multi",
      customerMode: "",
      acomptePercent: 30,
      defaultTva: 20,
      fiscalMentionShort: "",
      isCustom: true,
      fiscalProfileComplete: false,
    };
  }

  function _emptyMeta(over) {
    return Object.assign(
      {
        engineVersion: ENGINE_VERSION,
        rulesetVersion: RULESET_VERSION,
        disclaimer: DISCLAIMER,
      },
      over || {}
    );
  }

  function analyzeDevis(devis, options) {
    const opt = options || {};
    if (typeof AF_FISCAL === "undefined" || !AF_FISCAL.getContext) {
      return {
        ok: false,
        level: "error",
        issues: [
          _item("fiscal_unavailable", "error", "Contexte fiscal indisponible (module AF_FISCAL).", "profile", "engine", true),
        ],
        warnings: [],
        info: [],
        missing: ["AF_FISCAL"],
        checks: [],
        summary: "Vérification nécessaire. — Contexte fiscal indisponible, analyse partielle interrompue.",
        confidence: "low",
        contextUsed: _defaultContext(),
        meta: _emptyMeta(),
      };
    }
    if (typeof AF_DEVIS === "undefined" || !AF_DEVIS.documentOutput) {
      return {
        ok: false,
        level: "error",
        issues: [
          _item("devis_unavailable", "error", "Module devis indisponible (AF_DEVIS).", "devis", "engine", true),
        ],
        warnings: [],
        info: [],
        missing: ["AF_DEVIS"],
        checks: [],
        summary: "Vérification nécessaire. — Analyse partielle, module devis absent.",
        confidence: "low",
        contextUsed: AF_FISCAL.getContext(),
        meta: _emptyMeta(),
      };
    }

    const ctx = opt.profileContext && typeof opt.profileContext === "object" ? opt.profileContext : AF_FISCAL.getContext();
    const rawP = _rawProfile();
    const d = devis || {};
    const dout = AF_DEVIS.documentOutput(d);
    const snap = buildDocumentSnapshot(d);
    const st = _statusNorm(d) || (snap && snap.status) || "";

    const issues = [];
    const warnings = [];
    const info = [];
    const missing = [];
    const checks = [];

    const nomA = String(rawP.nom || "").trim();
    const entA = String(rawP.entreprise || "").trim();
    const adA = String(rawP.adresse || "").trim();
    const cpA = String(rawP.codePostal || "").trim();
    const vA = String(rawP.ville || "").trim();
    if (!nomA && !entA) {
      warnings.push(
        _item(
          "artisan_identity_too_spare",
          "warning",
          "Identité d'artisan incomplète (nom d'artisan et raison sociale vides) : le document commercial manque d'un repère, à vérifier dans l'onglet identité / profil.",
          "profile",
          "artisan_identity",
          false
        )
      );
    } else if (!adA && !cpA && !vA) {
      warnings.push(
        _item(
          "artisan_address_too_spare",
          "warning",
          "Adresse d'artisan incomplète (adresse, code postal, ville vides) : reprise d'impression / PDF, à vérifier dans le profil.",
          "profile",
          "artisan_identity",
          false
        )
      );
    }
    if (!String(dout.clientAddress != null ? dout.clientAddress : d.clientAddress || "").trim()) {
      warnings.push(
        _item("document_missing_client_address", "warning", "Adresse client absente : utile sur le devis / envoi, à vérifier.", "devis", "document", false)
      );
      if (missing.indexOf("clientAddress") === -1) {
        missing.push("clientAddress");
      }
    }
    if (!snap.hasDateHint) {
      warnings.push(
        _item(
          "document_date_missing",
          "warning",
          "Aucune date exploitable (création, échéance, signature) : calendrier et relances, à vérifier.",
          "devis",
          "date",
          false
        )
      );
    }

    const legalFormS = String(ctx.legalForm || "").trim();
    const vatModeS = String(ctx.vatMode || "").trim();
    const customerS = String(ctx.customerMode || "").trim();
    const tradeS = String(ctx.trade || "").trim();
    const primaryRaw = rawP.primaryTrade;
    const tradeEmptyLike = !String(primaryRaw != null ? primaryRaw : tradeS).trim();
    if (!legalFormS) {
      issues.push(
        _item("profile_missing_legal_form", "error", "Forme juridique absente : à renseigner dans le profil (onglet Fiscal).", "profile", "profile", true)
      );
      missing.push("legalForm");
    }
    if (!vatModeS) {
      issues.push(
        _item("profile_missing_vat_mode", "error", "Régime TVA absent : à renseigner dans le profil (onglet Fiscal).", "profile", "profile", true)
      );
      missing.push("vatMode");
    }
    if (!customerS) {
      warnings.push(
        _item("profile_missing_customer_mode", "warning", "Type de clientèle (particuliers / pros) non indiqué dans le profil : à vérifier.", "profile", "profile", false)
      );
      missing.push("customerMode");
    }
    if (tradeEmptyLike) {
      warnings.push(
        _item("profile_missing_primary_trade", "warning", "Métier principal non précisé (équivalent absent) : contexte BTP, à vérifier.", "profile", "profile", false)
      );
      missing.push("primaryTrade");
    }
    if (ctx.fiscalProfileComplete !== true) {
      warnings.push(
        _item("profile_incomplete", "warning", "Profil fiscal incomplet (plusieurs champs requis vides) : vérification nécessaire côté profil.", "profile", "profile", false)
      );
    }
    const hasSignMark = !!(
      d.signatureDataUrl ||
      d.signedAt ||
      d.signedSnapshot ||
      d.signedDocumentHash
    );
    if (hasSignMark && (!legalFormS || !vatModeS)) {
      warnings.push(
        _item(
          "signature_marks_fiscal_context_incomplete",
          "warning",
          "Sceau ou marqueur de signature présent alors que le profil TVA / forme n'est pas complet : reprise côté profil, à vérifier.",
          "profile+devis",
          "integrity",
          false
        )
      );
    }
    if (d.id == null || d.id === "") {
      warnings.push(
        _item("document_missing_id", "warning", "Identifiant interne de devis manquant : à vérifier (saisie / synchronisation).", "devis", "document", false)
      );
      missing.push("id");
    }
    const titleDoc = String(dout.title != null ? dout.title : d.title != null ? d.title : "").trim();
    if (!titleDoc) {
      warnings.push(
        _item("document_missing_title", "warning", "Intitulé du devis absent : le document est incomplet, à vérifier.", "devis", "document", false)
      );
      missing.push("title");
    }
    if (!String(dout.clientName != null ? dout.clientName : d.clientName || "").trim()) {
      warnings.push(
        _item("document_missing_client_name", "warning", "Nom client absent : à compléter sur le devis (ou fiche client).", "devis", "document", false)
      );
      missing.push("clientName");
    }

    const lignes = Array.isArray(dout.lignes) ? dout.lignes : [];
    const hasExploitableLines = lignes.length > 0;
    if (!hasExploitableLines) {
      issues.push(
        _item(
          "document_missing_lines",
          "error",
          "Aucune ligne exploitable : impossible de vérifier la cohérence (HT, TVA, TTC) sur une base sûre, à vérifier.",
          "devis",
          "document",
          true
        )
      );
      missing.push("lignes");
    }
    const htN = Number(dout.totalHT);
    const tvaN = Number(dout.totalTVA);
    const ttcN = Number(dout.totalTTC);
    const totalsValid =
      Number.isFinite(htN) && Number.isFinite(tvaN) && Number.isFinite(ttcN) && ttcN >= 0;
    if (!totalsValid) {
      issues.push(
        _item(
          "document_missing_totals",
          "error",
          "Totaux HT, TVA ou TTC manquants ou non numériques après normalisation : document non analysable, à vérifier.",
          "devis",
          "totals",
          true
        )
      );
      missing.push("totals");
    } else {
      if (ttcN < 0.01) {
        issues.push(
          _item(
            "document_missing_totals",
            "error",
            "Montant TTC nul ou inférieur à 0,01 € après normalisation : totaux non exploitables, à vérifier.",
            "devis",
            "totals",
            true
          )
        );
        if (missing.indexOf("totals") === -1) {
          missing.push("totals");
        }
      } else if (Math.abs(htN + tvaN - ttcN) > TOL_TTC) {
        issues.push(
          _item(
            "totals_incoherent",
            "error",
            "Total HT + TVA ne correspond pas au TTC affiché (écart > " + TOL_TTC + " €) : à vérifier sur les montants saisis.",
            "totals",
            "totals",
            true
          )
        );
      }
    }
    let sumLineHt = 0;
    if (lignes.length) {
      for (let i = 0; i < lignes.length; i++) {
        const l = lignes[i];
        const { qty, pu, tva } = lineQtyPuTva(l);
        sumLineHt += qty * pu;
        if (qty <= 0 || pu < 0) {
          issues.push(
            _item(
              "line_negative_or_zero_amount",
              "error",
              "Ligne " + (i + 1) + " : quantité ou prix unitaire incohérent (quantité ≤ 0 ou PU < 0) : à vérifier.",
              "lignes",
              "lignes",
              true
            )
          );
        }
        if (l.tva == null && l.tauxTVA == null) {
          warnings.push(
            _item(
              "vat_rate_missing_on_line",
              "warning",
              "Ligne " + (i + 1) + " : taux de TVA non indiqué (repli 20 % possible selon l'appli) : à vérifier.",
              "lignes",
              "lignes",
              false
            )
          );
        } else if (!Number.isFinite(tva) || tva < 0) {
          warnings.push(
            _item(
              "vat_rate_missing_on_line",
              "warning",
              "Ligne " + (i + 1) + " : taux de TVA non exploitable : à vérifier.",
              "lignes",
              "lignes",
              false
            )
          );
        }
      }
      if (totalsValid && Math.abs(sumLineHt - htN) > TOL_LINE_HT_VS_TOTAL) {
        warnings.push(
          _item(
            "line_ht_sum_mismatch",
            "warning",
            "Somme des lignes HT (qty × PU) " +
              Math.round(sumLineHt * 100) / 100 +
              " €, différente du total HT " +
              htN +
              " € (tol. " +
              TOL_LINE_HT_VS_TOTAL +
              " €) : reprise, arrondi ou calcul, à vérifier.",
            "totals",
            "totals",
            false
          )
        );
      }
    }
    if (lignes.length >= 1) {
      const labels = lignes.map((l) => String(l.label != null ? l.label : l.description != null ? l.description : "").trim());
      const allVeryShort = labels.length && labels.every((s) => s.length < 2);
      const allGenericLigne = labels.length && labels.every((s) => s === "Ligne" || s.length === 0);
      if (allVeryShort || allGenericLigne) {
        warnings.push(
          _item(
            "lines_too_poor_for_analysis",
            "warning",
            "Libellés de lignes pauvres (très courts ou génériques) : détail de la prestation insuffisant pour une relecture utile, à vérifier.",
            "lignes",
            "lignes",
            false
          )
        );
      }
    }
    const ttcUsable = totalsValid && ttcN > 0.01;
    if (ttcUsable && (d.acompte == null || d.acompte === undefined)) {
      warnings.push(
        _item(
          "acompte_missing",
          "warning",
          "Acompte non renseigné sur le devis (calcul côté app) : à vérifier si mention contractuelle requise.",
          "devis",
          "document",
          false
        )
      );
    }
    if (d.acompte != null && Number(d.acompte) !== 0 && ttcUsable) {
      const pct = Number.isFinite(Number(ctx.acomptePercent)) ? Number(ctx.acomptePercent) : 30;
      const att = Math.round(ttcN * (pct / 100) * 100) / 100;
      if (Math.abs(Number(d.acompte) - att) > TOL_ACOMPTE) {
        warnings.push(
          _item(
            "acompte_incoherent_with_profile",
            "warning",
            "Acompte saisi différent de l'application du " + pct + " % TTC (tol. " + TOL_ACOMPTE + " €) : à vérifier.",
            "profile+devis",
            "totals",
            false
          )
        );
      }
    }
    if (dout.acompte === 0 && ttcN > 1) {
      warnings.push(
        _item(
          "acompte_zero_with_amount",
          "warning",
          "Acompte à 0 € avec TTC significatif : cas exceptionnel, à vérifier.",
          "devis",
          "totals",
          false
        )
      );
    }
    if (isVatModeNoInvoicedVat(vatModeS) && tvaN > 0.02) {
      issues.push(
        _item(
          "vat_mode_conflict_no_vat_but_document_has_vat",
          "error",
          "Régime sans TVA facturée incohérent avec une TVA non nulle : à vérifier.",
          "profile+devis",
          "vat",
          true
        )
      );
    } else if (lignes.length && isVatModeNoInvoicedVat(vatModeS)) {
      if (lignes.some((l) => (Number(l.tva != null ? l.tva : l.tauxTVA != null ? l.tauxTVA : 0) || 0) > 0.001)) {
        issues.push(
          _item(
            "vat_mode_conflict_no_vat_but_document_has_vat",
            "error",
            "Lignes avec TVA > 0 % alors que le profil indique une forme « sans TVA facturée » : à vérifier.",
            "profile+devis",
            "vat",
            true
          )
        );
      }
    }
    if (isVatModeExpectsVatCharged(vatModeS) && ttcN > 0.1) {
      const allLinesZeroTva = lignes.length
        ? lignes.every(
            (l) => (Number(l.tva != null ? l.tva : l.tauxTVA != null ? l.tauxTVA : 0) || 0) < 0.001
          )
        : true;
      if (tvaN < 0.02 && allLinesZeroTva) {
        warnings.push(
          _item(
            "vat_mode_conflict_vat_expected_but_zero",
            "warning",
            "Régime avec TVA attendue, TVA nulle (lignes 0 %) : taux, exonération, erreur de saisie, à vérifier.",
            "profile+devis",
            "vat",
            false
          )
        );
      }
    }
    if (lignes.length > 0) {
      const defT = Number(ctx.defaultTva);
      if (Number.isFinite(defT)) {
        const rates = lignes
          .map((l) => Number(l.tva != null ? l.tva : l.tauxTVA != null ? l.tauxTVA : defT))
          .filter((n) => Number.isFinite(n));
        const allDiffer = rates.length > 0 && rates.every((r) => Math.abs(r - defT) > 0.001);
        if (allDiffer) {
          info.push(
            _item(
              "default_vat_conflict",
              "info",
              "Tous les taux de ligne diffèrent du taux par défaut du profil (" + defT + " %), à vérifier si c'est voulu.",
              "profile+lignes",
              "vat",
              false
            )
          );
        }
      }
    }
    if (profileRequiresFiscalMention(vatModeS, ctx.companyPreset) && !String(ctx.fiscalMentionShort || "").trim()) {
      warnings.push(
        _item(
          "fiscal_mention_missing_when_profile_requires_it",
          "warning",
          "Mention fiscale courte (profil) recommandée manquante : à vérifier selon l'usage du pied de document.",
          "profile",
          "mention",
          false
        )
      );
      if (missing.indexOf("fiscalMentionShort") === -1) {
        missing.push("fiscalMentionShort");
      }
    }
    if (String(ctx.fiscalMentionShort || "").trim() && ctx.fiscalProfileComplete !== true) {
      info.push(
        _item(
          "fiscal_mention_present_but_profile_incomplete",
          "info",
          "Mention fiscale saisie alors qu'autres champs du profil manquent, à vérifier.",
          "profile",
          "mention",
          false
        )
      );
    }
    if (st === "signe" && !d.signedSnapshot && !d.signedDocumentHash) {
      issues.push(
        _item(
          "signed_document_without_signature_metadata",
          "error",
          "Statut « signé » sans métadonnées de scellage : à vérifier.",
          "devis",
          "integrity",
          true
        )
      );
    }
    if (st === "paye") {
      const docFragile =
        !ttcUsable || !hasExploitableLines || !titleDoc || !String(d.clientName || dout.clientName || "").trim() || ctx.fiscalProfileComplete !== true;
      if (docFragile) {
        warnings.push(
          _item(
            "paid_document_without_signed_or_clear_basis",
            "warning",
            "Devis en « payé » alors que la complétude (profil, lignes, totaux, intitulé) est faible, à vérifier.",
            "devis",
            "document",
            false
          )
        );
      }
    }
    if (st === "signe" || st === "paye") {
      info.push(
        _item(
          "read_only_document_detected",
          "info",
          "Document verrouillé (" + (st === "paye" ? "payé" : "signé") + ") : l'analyse porte sur l'enregistrement actuel, à vérifier.",
          "devis",
          "integrity",
          false
        )
      );
    }
    if (dout && dout.sealed) {
      checks.push({ code: "document_sealed", passed: true, message: "Document scellé : totaux / lignes issus du snapshot.", deterministic: true });
    }
    checks.push({ code: "local_deterministic_engine_v2", passed: true, message: "Moteur V2 local, ruleset " + RULESET_VERSION + ", sans appel réseau.", deterministic: true });
    const hasIssueError = issues.length > 0;
    const ok = !hasIssueError;
    let level = "ok";
    if (hasIssueError) {
      level = "error";
    } else if (warnings.length > 0 || missing.length > 0) {
      level = "warning";
    }
    const uniqueMissing = Array.from(new Set(missing));
    const profileIncomplete = ctx.fiscalProfileComplete !== true;
    const docPartial = !!(
      (d.id == null || d.id === "")
      || !titleDoc
      || !String(d.clientName || dout.clientName || "").trim()
      || warnings.some((w) => w.code === "vat_rate_missing_on_line")
      || !String(dout.clientAddress != null ? dout.clientAddress : d.clientAddress || "").trim()
      || !snap.hasDateHint
    );
    const totalsCoherent = totalsValid && ttcN >= 0.01 && Math.abs(htN + tvaN - ttcN) <= TOL_TTC;
    let confidence = "medium";
    if (hasIssueError) {
      confidence = "low";
    } else if (profileIncomplete) {
      confidence = "low";
    } else if (docPartial || warnings.length > 0) {
      confidence = "medium";
    } else if (ctx.fiscalProfileComplete === true && hasExploitableLines && totalsCoherent) {
      confidence = "high";
    }
    const tooIncomplete = profileIncomplete || !titleDoc || !hasExploitableLines || !totalsValid;
    const summary = (() => {
      const prefix = tooIncomplete
        ? "Analyse partielle. Vérification nécessaire. — "
        : hasIssueError
        ? "Vérification nécessaire. — "
        : level === "warning"
        ? "Analyse partielle. — "
        : "";
      if (hasIssueError) {
        return (
          prefix +
          "Un ou plusieurs points bloquants (ruleset " +
          RULESET_VERSION +
          ") : aucun diagnostic de conformité total ; reprise manuelle, à vérifier."
        );
      }
      if (level === "warning") {
        return (
          prefix +
          "Des manques ou écarts subsistent. Aucun « conforme de bout en bout » ici, à vérifier avec l'artisan, le client ou l'administratif le cas échéant."
        );
      }
      if (info.length > 0 && !tooIncomplete) {
        return "Analyse partielle. — Aucun blocage : rappels informatifs ci-dessous, à vérifier si besoin.";
      }
      if (tooIncomplete) {
        return "Analyse partielle. Vérification nécessaire. — D'autres vérifications restent possibles hors application.";
      }
      return "Aucun point bloquant par les règles de cohérence V" + RULESET_VERSION + " (analyse partielle, non exhaustive) ; n'équivaut pas à un avis d'autorité fiscale ou légale.";
    })();
    const contextUsed = {
      companyPreset: ctx.companyPreset,
      legalForm: ctx.legalForm,
      vatMode: ctx.vatMode,
      trade: ctx.trade,
      customerMode: ctx.customerMode,
      acomptePercent: ctx.acomptePercent,
      defaultTva: ctx.defaultTva,
      fiscalProfileComplete: ctx.fiscalProfileComplete,
      documentSnapshot: {
        sealed: !!dout.sealed,
        lineCount: lignes.length,
        totalTTC: ttcN,
        hasUserEnteredLines: snap.hasUserEnteredLines,
        hasDateHint: snap.hasDateHint,
        status: st,
      },
    };
    return {
      ok,
      level,
      issues,
      warnings,
      info,
      missing: uniqueMissing,
      checks,
      summary,
      confidence,
      contextUsed,
      meta: _emptyMeta(),
    };
  }

  function buildFiscalAgentPayload(devis, profileContext) {
    const ctx =
      profileContext && typeof profileContext === "object"
        ? profileContext
        : typeof AF_FISCAL !== "undefined" && AF_FISCAL.getContext
        ? AF_FISCAL.getContext()
        : _defaultContext();
    const snap = buildDocumentSnapshot(devis);
    const dout = typeof AF_DEVIS !== "undefined" && AF_DEVIS.documentOutput ? AF_DEVIS.documentOutput(devis) : null;
    const local = analyzeDevis(devis, { profileContext: ctx });
    return {
      schemaVersion: 2,
      regionDefault: "FR",
      instantDevis: {
        engine: "AF_FISCAL_ENGINE",
        engineVersion: ENGINE_VERSION,
        rulesetVersion: RULESET_VERSION,
        purpose: "cloudflare_fiscal_agent_futures",
      },
      profile: { ...ctx, primaryTrade: ctx.trade || "multi" },
      document: snap,
      documentTotals: dout
        ? { totalHT: dout.totalHT, totalTVA: dout.totalTVA, totalTTC: dout.totalTTC, acompte: dout.acompte, sealed: dout.sealed }
        : null,
      localEngineSummary: {
        ok: local.ok,
        level: local.level,
        rulesetVersion: RULESET_VERSION,
        summary: local.summary,
        issues: local.issues,
        warnings: local.warnings,
        info: local.info,
        missing: local.missing,
        confidence: local.confidence,
      },
      disclaimer:
        "Sérialisation pour usage serveur optionnel. Pas de règles de droit non implémentées côté outil, à vérifier en humain.",
    };
  }

  return {
    analyzeDevis,
    buildDocumentSnapshot,
    buildFiscalAgentPayload,
    ENGINE_VERSION,
    RULESET_VERSION,
    DISCLAIMER,
  };
})();

Object.assign(window, { AF_FISCAL_ENGINE });
