<?php

namespace ESI_PEPPOL\controllers;

use ESI_PEPPOL\models\PEPPOL_Main_model;
use ESI_PEPPOL\helpers\PEPPOL_Woo_Helper;

class PEPPOL_peppol_controller {

    /**
     * URL de base de l'API ESIPeppol.
     */
    private const API_BASE_URL = 'https://demo.esi-peppol.be/api';

    /**
     * Retourne l'URL de base de l'API (filtrable).
     *
     * @return string
     */
    protected static function get_base_url(): string {
        /**
         * Filtre pour surcharger l'URL de base de l'API ESIPeppol.
         *
         * @param string $base_url URL de base actuelle.
         */
        return (string) \apply_filters('esi_peppol_api_base_url', self::API_BASE_URL);
    }

    /**
     * Récupère les identifiants API stockés dans les options WordPress.
     *
     * @return array{api_key:string,password:string}
     */
    protected static function get_credentials(): array {
        $api_key  = (string) \get_option('esi_peppol_api_key', '');
        $password = (string) \get_option('esi_peppol_password', '');

        return [
            'api_key'  => $api_key,
            'password' => $password,
        ];
    }

    /**
     * Construit les en-têtes HTTP pour les appels authentifiés.
     *
     * @return array<string,string>
     */
    protected static function get_auth_headers(): array {
        $creds = self::get_credentials();

        return [
            'Content-Type' => 'application/json',
            'Accept'       => 'application/json',
            'X-API-KEY'    => $creds['api_key'],
            'X-PASSWORD'   => $creds['password'],
        ];
    }

    /**
     * Appel générique à l'API ESIPeppol.
     *
     * @param string $method    Méthode HTTP (GET, POST, ...).
     * @param string $path      Chemin de l'endpoint (ex. '/health').
     * @param array  $args      Arguments supplémentaires (body, headers...).
     * @param bool   $with_auth Indique si l'appel doit être authentifié.
     *
     * @return array{
     *   success:bool,
     *   http_code:int,
     *   message:string,
     *   data:mixed
     * }
     */
    protected static function request(string $method, string $path, array $args = [], bool $with_auth = true): array {
        $base_url = rtrim(self::get_base_url(), '/');
        $path     = '/' . ltrim($path, '/');
        $url      = $base_url . $path;

        $headers = [
            'Accept' => 'application/json',
        ];

        if ($with_auth) {
            $headers = array_merge($headers, self::get_auth_headers());
        }

        if (isset($args['headers']) && is_array($args['headers'])) {
            $headers = array_merge($headers, $args['headers']);
        }

        $wp_args = [
            'method'  => $method,
            'timeout' => 20,
            'headers' => $headers,
        ];

        if (isset($args['body'])) {
            $body = $args['body'];

            if (!is_string($body)) {
                $body = \wp_json_encode($body);
            }

            $wp_args['body'] = $body;
        }

        $response = \wp_remote_request($url, $wp_args);

        if (\is_wp_error($response)) {
            return [
                'success'   => false,
                'http_code' => 0,
                'message'   => $response->get_error_message(),
                'data'      => null,
            ];
        }

        $code = (int) \wp_remote_retrieve_response_code($response);
        $body = \wp_remote_retrieve_body($response);

        $data = null;
        if ($body !== '') {
            $decoded = \json_decode($body, true);
            $data    = \json_last_error() === \JSON_ERROR_NONE ? $decoded : $body;
        }

        $success = $code >= 200 && $code < 300;

        $message = '';
        if (!$success) {
            if (is_array($data) && isset($data['message'])) {
                $message = (string) $data['message'];
            }
        }

        return [
            'success'   => $success,
            'http_code' => $code,
            'message'   => $message,
            'data'      => $data,
        ];
    }

    /**
     * Appelle l'endpoint public /health (sans authentification).
     *
     * @return array
     */
    public static function health_check(): array {
        return self::request('GET', '/health', [], false);
    }

    /**
     * Appelle l'endpoint /status (avec authentification) pour vérifier
     * la validité des credentials et l'état du quota.
     *
     * @return array
     */
    public static function status(): array {
        return self::request('GET', '/status', [], true);
    }

    /**
     * Variante de /status qui utilise explicitement les identifiants fournis,
     * sans passer par les options WordPress (utile pour les tests de connexion).
     *
     * @param string $api_key
     * @param string $password
     *
     * @return array
     */
    public static function status_with_credentials(string $api_key, string $password): array {
        $headers = [
            'X-API-KEY'  => $api_key,
            'X-PASSWORD' => $password,
        ];

        return self::request('GET', '/status', ['headers' => $headers], false);
    }

    /**
     * Envoie un JSON déjà construit vers /upload-json.
     *
     * Si un $order_id est fourni, le résultat est enregistré dans la table custom.
     *
     * @param array    $payload  Données JSON structurées prêtes pour l'API.
     * @param int|null $order_id ID de commande WooCommerce lié (optionnel).
     *
     * @return array Réponse normalisée (success, http_code, message, data).
     */
    public static function upload_json(array $payload, ?int $order_id = null): array {
        $result = self::request('POST', '/upload-json', ['body' => $payload], true);

        if ($order_id !== null) {
            $data = is_array($result['data'] ?? null) ? $result['data'] : [];

            // Extraire document_id et peppol_document_id depuis la réponse API
            $document_id        = isset($data['document_id']) ? (string) $data['document_id'] : '';
            $peppol_document_id = isset($data['peppol_document_id']) ? (string) $data['peppol_document_id'] : '';
            $status             = isset($data['status']) ? (string) $data['status'] : ($result['success'] ? 'created' : 'error');

            $message = $result['message'];
            if ($message === '' && isset($data['message'])) {
                $message = (string) $data['message'];
            }

            // Log technique optionnel pour vérifier le payload réellement envoyé
            if (class_exists(PEPPOL_Plugin::class)) {
                PEPPOL_Plugin::write_debug_file(
                    [
                        'event'              => 'upload_json',
                        'order_id'           => $order_id,
                        'payload_sent'       => $payload,
                        'api_result'         => $result,
                        'document_id'        => $document_id,
                        'peppol_document_id' => $peppol_document_id,
                    ],
                    'INFO'
                );
            }

            // Enregistrer ou mettre à jour dans la base de données
            PEPPOL_Main_model::save_for_order(
                $order_id,
                $data,
                $payload,
                $document_id,
                $peppol_document_id,
                $status,
                (bool) $result['success'],
                $message,
                (int) $result['http_code']
            );

            // Envoyer un email en cas d'erreur API
            if (!$result['success'] && class_exists(PEPPOL_Plugin::class)) {
                PEPPOL_Plugin::send_api_error_email($order_id, $result, $payload);
            }
        }

        return $result;
    }

    /**
     * Ajoute au tableau des totaux de TVA les montants d'une ligne,
     * en gérant les cas avec plusieurs taux sur la même ligne.
     *
     * @param array<string,array<string,mixed>> $vat_totals_by_rate_code
     * @param float                             $taxable_amount   Montant HT de la ligne (tous taux confondus).
     * @param float                             $vat_amount       Montant TVA total de la ligne.
     * @param array<int,array<string,mixed>>    $tax_details      Détails TVA par taux (helper my_get_item_tax_details).
     * @param float                             $fallback_vat_rate Taux utilisé si aucun détail exploitable.
     *
     * @return void
     */
    private static function accumulate_vat_totals_for_line(
        array &$vat_totals_by_rate_code,
        float $taxable_amount,
        float $vat_amount,
        array $tax_details,
        float $fallback_vat_rate
    ): void {
        // Si pas de détails ou TVA totale nulle, on retombe sur l'ancien comportement (un seul taux).
        $positive_taxes = array_filter(
            $tax_details,
            static function ($detail): bool {
                return isset($detail['amount'], $detail['percent'])
                    && (float) $detail['amount'] > 0.0
                    && (float) $detail['percent'] > 0.0;
            }
        );

        if (empty($positive_taxes) || $vat_amount <= 0.0 || $taxable_amount <= 0.0) {
            $key = (string) $fallback_vat_rate;

            if (!isset($vat_totals_by_rate_code[$key])) {
                $vat_totals_by_rate_code[$key] = [
                    'vat_rate'          => $fallback_vat_rate,
                    // La catégorie sera recalculée après accumulation des montants
                    'vat_category_code' => 'S',
                    'taxable_amount'    => 0.0,
                    'vat_amount'        => 0.0,
                    'tax_scheme_id'     => 'VAT',
                    'tax_scheme_name'   => 'Value Added Tax',
                ];
            }

            $vat_totals_by_rate_code[$key]['taxable_amount'] += $taxable_amount;
            $vat_totals_by_rate_code[$key]['vat_amount']     += $vat_amount;

            return;
        }

        // Plusieurs taux positifs sur la même ligne : on répartit le HT au prorata des montants de TVA.
        $total_positive_vat = 0.0;
        foreach ($positive_taxes as $detail) {
            $total_positive_vat += (float) $detail['amount'];
        }

        if ($total_positive_vat <= 0.0) {
            // Sécurité : repli sur le comportement ancien.
            $key = (string) $fallback_vat_rate;

            if (!isset($vat_totals_by_rate_code[$key])) {
                $vat_totals_by_rate_code[$key] = [
                    'vat_rate'          => $fallback_vat_rate,
                    'vat_category_code' => 'S',
                    'taxable_amount'    => 0.0,
                    'vat_amount'        => 0.0,
                    'tax_scheme_id'     => 'VAT',
                    'tax_scheme_name'   => 'Value Added Tax',
                ];
            }

            $vat_totals_by_rate_code[$key]['taxable_amount'] += $taxable_amount;
            $vat_totals_by_rate_code[$key]['vat_amount']     += $vat_amount;

            return;
        }

        $allocated_taxable = 0.0;
        $positive_count    = count($positive_taxes);
        $index             = 0;

        foreach ($positive_taxes as $detail) {
            ++$index;

            $rate      = (float) $detail['percent'];
            $tax_part  = (float) $detail['amount'];
            $rate_key  = (string) $rate;

            // Répartition du HT au prorata de la TVA de ce taux.
            if ($index === $positive_count) {
                // Dernier taux : on prend le reste pour éviter les erreurs d'arrondi.
                $taxable_share = $taxable_amount - $allocated_taxable;
            } else {
                $taxable_share   = $taxable_amount * ($tax_part / $total_positive_vat);
                $allocated_taxable += $taxable_share;
            }

            if (!isset($vat_totals_by_rate_code[$rate_key])) {
                $vat_totals_by_rate_code[$rate_key] = [
                    'vat_rate'          => $rate,
                    'vat_category_code' => 'S',
                    'taxable_amount'    => 0.0,
                    'vat_amount'        => 0.0,
                    'tax_scheme_id'     => 'VAT',
                    'tax_scheme_name'   => 'Value Added Tax',
                ];
            }

            $vat_totals_by_rate_code[$rate_key]['taxable_amount'] += $taxable_share;
            $vat_totals_by_rate_code[$rate_key]['vat_amount']     += $tax_part;
        }

        // S'il reste une partie du HT non allouée (par ex. combiné à un taux 0%),
        // on l'ajoute dans un bucket 0% (exempt).
        $remaining_taxable = $taxable_amount - $allocated_taxable;
        if ($remaining_taxable > 0.0001) {
            $key_zero = '0';

            if (!isset($vat_totals_by_rate_code[$key_zero])) {
                $vat_totals_by_rate_code[$key_zero] = [
                    'vat_rate'          => 0.0,
                    'vat_category_code' => 'E',
                    'taxable_amount'    => 0.0,
                    'vat_amount'        => 0.0,
                    'tax_scheme_id'     => 'VAT',
                    'tax_scheme_name'   => 'Value Added Tax',
                ];
            }

            $vat_totals_by_rate_code[$key_zero]['taxable_amount'] += $remaining_taxable;
        }
    }

    /**
     * Construit le payload JSON attendu par /upload-json à partir
     * d'une commande WooCommerce.
     *
     * @param \WC_Order $order
     *
     * @return array
     */
    public static function build_payload_from_order(\WC_Order $order): array {
        $order_id      = $order->get_id();
        $order_number  = $order->get_order_number();
        $currency_code = $order->get_currency();

        $invoice_date = $order->get_date_created()
            ? $order->get_date_created()->date('Y-m-d')
            : \gmdate('Y-m-d');

        // Par défaut, échéance à +30 jours (filtrable)
        $due_date = \apply_filters(
            'esi_peppol_invoice_due_date',
            (new \DateTimeImmutable($invoice_date))
                ->modify('+30 days')
                ->format('Y-m-d'),
            $order
        );

        $external_reference = \apply_filters(
            'esi_peppol_external_reference',
            'WC-' . $order_number,
            $order
        );

        $invoice_notes = \apply_filters(
            'esi_peppol_invoice_notes',
            sprintf(
                /* translators: %s: order number */
                __('Facture générée depuis la commande WooCommerce %s', 'esi_peppol'),
                $order_number
            ),
            $order
        );

        $payment_terms = \apply_filters(
            'esi_peppol_payment_terms',
            __('Paiement à 30 jours nets', 'esi_peppol'),
            $order
        );

        // Données vendeur (store) - basées sur les options WooCommerce
        $store_name      = \get_bloginfo('name');
        $store_address   = \get_option('woocommerce_store_address', '');
        $store_address2  = \get_option('woocommerce_store_address_2', '');
        $store_city      = \get_option('woocommerce_store_city', '');
        $store_postcode  = \get_option('woocommerce_store_postcode', '');
        $store_country   = \get_option('woocommerce_default_country', '');
        $store_vat_number = \get_option('woocommerce_store_vat_number', '');

        // Le country peut être sous forme "BE:BE" selon les versions
        if (strpos($store_country, ':') !== false) {
            [$store_country] = explode(':', $store_country, 2);
        }

        $seller = [
            'name'       => $store_name,
            'legal_name' => $store_name,
            'company_id' => '',
            'vat_number' => $store_vat_number,
            'address'    => [
                'address_line_1' => $store_address,
                'address_line_2' => $store_address2,
                'city'           => $store_city,
                'postal_code'    => $store_postcode,
                'country_code'   => $store_country,
            ],
            'contact'    => [
                'contact_name'  => $store_name,
                'contact_email' => \get_option('admin_email', ''),
            ],
        ];

        $seller = \apply_filters('esi_peppol_seller_party', $seller, $order);

        // Données client (buyer) à partir de la commande
        $buyer_country = $order->get_billing_country();

        $buyer = [
            'name'       => $order->get_billing_company() ?: $order->get_formatted_billing_full_name(),
            'legal_name' => $order->get_billing_company() ?: $order->get_formatted_billing_full_name(),
            'company_id' => '',
            'vat_number' => $order->get_meta('_billing_vat_number') ?: '',
            'address'    => [
                'address_line_1' => $order->get_billing_address_1(),
                'address_line_2' => $order->get_billing_address_2(),
                'city'           => $order->get_billing_city(),
                'postal_code'    => $order->get_billing_postcode(),
                'country_code'   => $buyer_country,
            ],
            'contact'    => [
                'contact_name'  => $order->get_formatted_billing_full_name(),
                'contact_email' => $order->get_billing_email(),
                'contact_phone' => $order->get_billing_phone(),
            ],
        ];

        $buyer = \apply_filters('esi_peppol_buyer_party', $buyer, $order);

        // Lignes de facture
        $invoice_lines           = [];
        $total_excl_vat          = 0.0;
        $total_vat               = 0.0;
        $total_incl_vat          = 0.0;
        $vat_totals_by_rate_code = [];

        $line_number = 1;

        // 1) Lignes produits
        foreach ($order->get_items('line_item') as $item) {
            /** @var \WC_Order_Item_Product $item */
            $product = $item->get_product();

            $quantity = (float) $item->get_quantity();
            $line_subtotal      = (float) $item->get_subtotal();
            $line_subtotal_tax  = (float) $item->get_subtotal_tax();
            $line_total         = (float) $item->get_total();
            $line_total_tax     = (float) $item->get_total_tax();

            // On travaille sur les montants "total" (après remises)
            $taxable_amount      = $line_total;
            $vat_amount          = $line_total_tax;
            $line_total_incl_vat = $taxable_amount + $vat_amount;

            $unit_price = $quantity > 0 ? $taxable_amount / $quantity : 0.0;

            // Détermination du taux de TVA via le helper centralisé
            $vat_rate    = 0.0;
            $tax_details = PEPPOL_Woo_Helper::my_get_item_tax_details($item);

            if (!empty($tax_details)) {
                // On prend le premier taux non nul (cas standard WooCommerce)
                foreach ($tax_details as $tax_detail) {
                    if (!empty($tax_detail['percent'])) {
                        $vat_rate = round((float) $tax_detail['percent'], 2);
                        break;
                    }
                }
            }

            // Fallback: approximation du taux de TVA basée sur les montants
            if ($vat_rate === 0.0) {
                if ($line_subtotal > 0.0 && $line_subtotal_tax > 0.0) {
                    $vat_rate = ($line_subtotal_tax / $line_subtotal) * 100;
                } elseif ($taxable_amount > 0.0 && $vat_amount > 0.0) {
                    $vat_rate = ($vat_amount / $taxable_amount) * 100;
                }

                $vat_rate = round($vat_rate, 2);
            }

            // Log technique pour debug TVA par ligne
            if (class_exists(PEPPOL_Plugin::class)) {
                PEPPOL_Plugin::write_debug_file(
                    [
                        'event'              => 'vat_debug_line',
                        'order_id'           => $order->get_id(),
                        'line_number'        => $line_number,
                        'product_id'         => $product ? $product->get_id() : null,
                        'product_name'       => $item->get_name(),
                        'quantity'           => $quantity,
                        'line_subtotal'      => $line_subtotal,
                        'line_subtotal_tax'  => $line_subtotal_tax,
                        'line_total'         => $line_total,
                        'line_total_tax'     => $line_total_tax,
                        'taxable_amount'     => $taxable_amount,
                        'vat_amount'         => $vat_amount,
                        'computed_vat_rate'  => $vat_rate,
                        'line_taxes_raw'     => $item->get_taxes(),
                        'tax_details_helper' => $tax_details,
                    ],
                    'INFO'
                );
            }

            // Déterminer la catégorie de TVA en fonction du montant de TVA
            // - Montant TVA 0  => catégorie "E" (exempt / taux zéro appliqué)
            // - Montant TVA >0 => catégorie "S" (standard)
            $is_zero_vat_amount = ($vat_amount == 0.0);
            $vat_category_code  = $is_zero_vat_amount ? 'E' : 'S';

            $unit_of_measure = 'C62'; // "Unit" code par défaut

            if ($product && $product->get_meta('unit_of_measure')) {
                $unit_of_measure = (string) $product->get_meta('unit_of_measure');
            }

            $seller_item_id = '';
            if ($product) {
                // Pour les produits : P_ + SKU si existant, sinon P_ + ID produit
                $base_id        = $product->get_sku() ?: (string) $product->get_id();
                $seller_item_id = 'P_' . $base_id;
            }

            // Description courte sans balises HTML pour compatibilité PEPPOL
            $item_description = '';
            if ($product) {
                $raw_short_description = (string) $product->get_short_description();
                // Supprime toutes les balises HTML et compresse les espaces
                $item_description = \wp_strip_all_tags($raw_short_description, true);
            }

            $line = [
                'line_number'                     => $line_number,
                'item_name'                       => $item->get_name(),
                'item_description'                => $item_description,
                'seller_item_id'                  => $seller_item_id,
                'quantity'                        => $quantity,
                'unit_of_measure'                 => $unit_of_measure,
                'unit_price'                      => round($unit_price, 2),
                'vat_rate'                        => $vat_rate,
                'vat_category_code'               => $vat_category_code,
                'line_total_amount'               => round($taxable_amount, 2),
                'line_vat_amount'                 => round($vat_amount, 2),
                'line_total_amount_including_vat' => round($line_total_incl_vat, 2),
            ];

            $invoice_lines[] = \apply_filters('esi_peppol_invoice_line', $line, $item, $order);

            $total_excl_vat += $taxable_amount;
            $total_vat      += $vat_amount;
            $total_incl_vat += $line_total_incl_vat;

            self::accumulate_vat_totals_for_line(
                $vat_totals_by_rate_code,
                $taxable_amount,
                $vat_amount,
                $tax_details,
                $vat_rate
            );

            ++$line_number;
        }

        // 2) Lignes de livraison
        foreach ($order->get_items('shipping') as $item) {
            /** @var \WC_Order_Item_Shipping $item */
            $quantity = 1.0;

            // IMPORTANT : pour la livraison, on recalcule la base HT à partir du montant TTC
            // et du taux de TVA réel, car les valeurs de WooCommerce (get_total_tax()) peuvent 
            // être incohérentes avec le taux de TVA appliqué.
            //
            // Exemple : si total TTC = 50 € et taux TVA = 21% :
            //   - Base HT correcte = 50 / 1.21 = 41.32 €
            //   - TVA correcte = 50 - 41.32 = 8.68 €
            // 
            // (et NON pas : 50 - 10.5 = 39.5 € comme le donnerait une simple soustraction
            // de la TVA retournée par WooCommerce, qui peut être erronée)
            
            $gross_amount        = (float) $item->get_total();      // Montant TVAC
            $line_total_incl_vat = $gross_amount;

            // Détermination du taux de TVA pour la livraison via le helper
            $vat_rate    = 0.0;
            $tax_details = PEPPOL_Woo_Helper::my_get_item_tax_details($item);

            if (!empty($tax_details)) {
                foreach ($tax_details as $tax_detail) {
                    if (!empty($tax_detail['percent'])) {
                        $vat_rate = round((float) $tax_detail['percent'], 2);
                        break;
                    }
                }
            }

            // Calcul de la base HT et de la TVA à partir du montant TVAC et du taux.
            // Pour un taux de 21%, on fait : TVAC / 1.21 pour obtenir la base HT,
            // puis TVA = TVAC - HT.
            if ($vat_rate > 0.0) {
                $divider        = 1 + ($vat_rate / 100);
                $taxable_amount = $divider > 0 ? $gross_amount / $divider : $gross_amount;
                $vat_amount     = $gross_amount - $taxable_amount;
            } else {
                // Si pas de taux de TVA détecté, on considère tout en HT.
                $taxable_amount = $gross_amount;
                $vat_amount     = 0.0;
            }

            // Sécurité : éviter les valeurs négatives en cas de configuration exotique.
            if ($taxable_amount < 0) {
                $taxable_amount = 0.0;
            }

            // Arrondis pour cohérence avec les autres lignes
            $taxable_amount = round($taxable_amount, 2);
            $vat_amount     = round($vat_amount, 2);

            $unit_price = $quantity > 0 ? $taxable_amount / $quantity : 0.0;

            $is_zero_vat_amount = ($vat_amount == 0.0);
            $vat_category_code  = $is_zero_vat_amount ? 'E' : 'S';

            // Identifiant vendeur pour les frais de livraison : préfixe SHIPCQT_ + n° de ligne
            $seller_item_id = 'SHIPCQT_' . $line_number;

            $line = [
                'line_number'                     => $line_number,
                'item_name'                       => $item->get_name() ?: \__('Frais de livraison', 'esi_peppol'),
                'item_description'                => '',
                'seller_item_id'                  => $seller_item_id,
                'quantity'                        => $quantity,
                'unit_of_measure'                 => 'C62',
                'unit_price'                      => round($unit_price, 2),
                'vat_rate'                        => $vat_rate,
                'vat_category_code'               => $vat_category_code,
                'line_total_amount'               => $taxable_amount,
                'line_vat_amount'                 => $vat_amount,
                'line_total_amount_including_vat' => round($line_total_incl_vat, 2),
            ];

            $invoice_lines[] = \apply_filters('esi_peppol_invoice_line_shipping', $line, $item, $order);

            $total_excl_vat += $taxable_amount;
            $total_vat      += $vat_amount;
            $total_incl_vat += $line_total_incl_vat;

            self::accumulate_vat_totals_for_line(
                $vat_totals_by_rate_code,
                $taxable_amount,
                $vat_amount,
                $tax_details,
                $vat_rate
            );

            ++$line_number;
        }

        // 3) Lignes de frais supplémentaires
        foreach ($order->get_items('fee') as $item) {
            /** @var \WC_Order_Item_Fee $item */
            $quantity = 1.0;

            // Montant du fee dans WooCommerce.
            // Pour les frais, on considère que get_total() retourne un montant TVAC
            // et on reconstitue la base HT & la TVA à partir du taux de TVA.
            $gross_amount = (float) $item->get_total(); // Montant TVAC

            // Détermination du taux de TVA pour les frais via le helper
            $vat_rate    = 0.0;
            $tax_details = PEPPOL_Woo_Helper::my_get_item_tax_details($item);

            if (!empty($tax_details)) {
                foreach ($tax_details as $tax_detail) {
                    if (!empty($tax_detail['percent'])) {
                        $vat_rate = round((float) $tax_detail['percent'], 2);
                        break;
                    }
                }
            }

            // Calcul de la base HT et de la TVA à partir du montant TVAC.
            // Exemple : pour 21%, on fait TVAC / 1.21 pour obtenir la base,
            // puis TVA = TVAC - HT.
            if ($vat_rate > 0.0) {
                $divider        = 1 + ($vat_rate / 100);
                $taxable_amount = $divider > 0 ? $gross_amount / $divider : $gross_amount;
                $vat_amount     = $gross_amount - $taxable_amount;
            } else {
                // Si pas de taux de TVA détecté, on considère tout en HT.
                $taxable_amount = $gross_amount;
                $vat_amount     = 0.0;
            }

            $line_total_incl_vat = $gross_amount;

            if ($taxable_amount < 0) {
                $taxable_amount = 0.0;
            }

            // Arrondis pour cohérence
            $taxable_amount = round($taxable_amount, 2);
            $vat_amount     = round($vat_amount, 2);

            $unit_price = $quantity > 0 ? $taxable_amount / $quantity : 0.0;

            $is_zero_vat_amount = ($vat_amount == 0.0);
            $vat_category_code  = $is_zero_vat_amount ? 'E' : 'S';

            // Identifiant vendeur pour les frais supplémentaires : préfixe FEE_ + n° de ligne
            $seller_item_id = 'FEE_' . $line_number;

            $line = [
                'line_number'                     => $line_number,
                'item_name'                       => $item->get_name() ?: \__('Frais supplémentaires', 'esi_peppol'),
                'item_description'                => '',
                'seller_item_id'                  => $seller_item_id,
                'quantity'                        => $quantity,
                'unit_of_measure'                 => 'C62',
                'unit_price'                      => round($unit_price, 2),
                'vat_rate'                        => $vat_rate,
                'vat_category_code'               => $vat_category_code,
                'line_total_amount'               => $taxable_amount,
                'line_vat_amount'                 => $vat_amount,
                'line_total_amount_including_vat' => round($line_total_incl_vat, 2),
            ];

            $invoice_lines[] = \apply_filters('esi_peppol_invoice_line_fee', $line, $item, $order);

            $total_excl_vat += $taxable_amount;
            $total_vat      += $vat_amount;
            $total_incl_vat += $line_total_incl_vat;

            self::accumulate_vat_totals_for_line(
                $vat_totals_by_rate_code,
                $taxable_amount,
                $vat_amount,
                $tax_details,
                $vat_rate
            );

            ++$line_number;
        }

        $vat_totals = array_map(
            static function (array $row) use ($order): array {
                // Arrondir la base imposable agrégée
                $row['taxable_amount'] = round($row['taxable_amount'], 2);

                // IMPORTANT : recalculer la TVA à partir de la base totale et du taux,
                // au lieu de sommer les montants de TVA ligne par ligne (produits, frais, etc.).
                // Cela garantit que la TVA par taux est cohérente avec la base globale.
                $rate = isset($row['vat_rate']) ? (float) $row['vat_rate'] : 0.0;
                if ($rate > 0.0) {
                    $row['vat_amount'] = round($row['taxable_amount'] * $rate / 100, 2);
                } else {
                    $row['vat_amount'] = 0.0;
                }

                // Catégorie "E" si aucun montant de TVA pour ce taux, sinon "S"
                $row['vat_category_code'] = ($row['vat_amount'] == 0.0) ? 'E' : 'S';

                // Log technique pour debug TVA agrégée par taux
                if (class_exists(PEPPOL_Plugin::class)) {
                    \ESI_PEPPOL\controllers\PEPPOL_Plugin::write_debug_file(
                        [
                            'event'             => 'vat_debug_total',
                            'order_id'          => $order->get_id(),
                            'vat_rate'          => $row['vat_rate'],
                            'taxable_amount'    => $row['taxable_amount'],
                            'vat_amount'        => $row['vat_amount'],
                            'vat_category_code' => $row['vat_category_code'],
                        ],
                        'INFO'
                    );
                }

                return $row;
            },
            $vat_totals_by_rate_code
        );

        // Nettoyage : on supprime les lignes de TVA sans montant (taux 0% "artificiel")
        // afin d'éviter d'avoir un bucket 0% dans vat_totals quand toute la facture
        // est réellement soumise à un taux positif (ex. 21% partout).
        $vat_totals = array_filter(
            $vat_totals,
            static function (array $row): bool {
                return isset($row['vat_amount']) && (float) $row['vat_amount'] > 0.0;
            }
        );
        // Réindexer les clés après le filtre
        $vat_totals = array_values($vat_totals);

        $invoice_totals = [
            'total_amount_excluding_vat' => round($total_excl_vat, 2),
            'total_vat_amount'           => round($total_vat, 2),
            'total_amount_including_vat' => round($total_incl_vat, 2),
            'total_paid_amount'          => 0.0,
            'total_payable_amount'       => round($order->get_total(), 2),
            'amount_due'                 => round($order->get_total(), 2),
        ];

        $invoice_totals = \apply_filters('esi_peppol_invoice_totals', $invoice_totals, $order);

        // Informations de paiement (laissées filtrables pour configuration future)
        $payment_info = [
            'payee_iban' => '',
            'payee_bic'  => '',
            'bank_name'  => '',
        ];

        $payment_info = \apply_filters('esi_peppol_payment_info', $payment_info, $order);

        $payload = [
            'document_type'      => 'invoice',
            'external_reference' => $external_reference,
            'invoice'            => [
                'invoice_number' => $order_number,
                'invoice_date'   => $invoice_date,
                'due_date'       => $due_date,
                'currency_code'  => $currency_code,
                'invoice_notes'  => $invoice_notes,
                'payment_terms'  => $payment_terms,
            ],
            'parties'           => [
                'seller' => $seller,
                'buyer'  => $buyer,
            ],
            'invoice_lines'     => $invoice_lines,
            'vat_totals'        => array_values($vat_totals),
            'invoice_totals'    => $invoice_totals,
            'payment_info'      => $payment_info,
        ];

        return \apply_filters('esi_peppol_payload_from_order', $payload, $order);
    }
}