package de.assecutor.pdftool.template;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.assecutor.pdftool.invoice.InvoiceMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Baut die Positions-Variablen für das Rendern von Rechnungstemplates auf:
 * {@code services.json} (eine Zeile je Position mit Name, Nettobetrag und
 * USt-Satz) sowie die Summen {@code invoice.net_total/vat_total/gross_total}.
 * Wird von der E-Rechnungs-Maske und dem Rechnungsgenerator gemeinsam genutzt.
 */
public final class TemplateVariables {

    private static final Logger log = LoggerFactory.getLogger(TemplateVariables.class);

    private static final BigDecimal HUNDRED = new BigDecimal("100");

    private TemplateVariables() {
    }

    /** Ergänzt services.json, Summen und USt-Satz-Label aus den Positionen. */
    public static void addPositions(Map<String, String> variables, List<InvoiceMetadata.LineItem> items) {
        List<Map<String, String>> positions = new ArrayList<>();
        BigDecimal netTotal = BigDecimal.ZERO;
        BigDecimal vatTotal = BigDecimal.ZERO;

        for (InvoiceMetadata.LineItem item : items) {
            BigDecimal net = lineNet(item);
            Map<String, String> position = new HashMap<>();
            position.put("name", item.description());
            position.put("netAmount", formatAmount(net));
            position.put("vat", vatLabel(item.vatPercent()));
            positions.add(position);
            netTotal = netTotal.add(net);
            vatTotal = vatTotal.add(lineVat(item));
        }
        BigDecimal grossTotal = netTotal.add(vatTotal);

        try {
            variables.put("services.json", new ObjectMapper().writeValueAsString(positions));
        } catch (Exception ex) {
            log.error("Positionen konnten nicht serialisiert werden: {}", ex.getMessage(), ex);
            variables.put("services.json", "[]");
        }
        variables.put("services.count", String.valueOf(positions.size()));
        variables.put("invoice.net_total", formatAmount(netTotal) + " €");
        variables.put("invoice.vat_total", formatAmount(vatTotal) + " €");
        variables.put("invoice.gross_total", formatAmount(grossTotal) + " €");
        variables.put("invoice.vat_rate", uniformVatLabel(items));
        variables.put("services.net_total", formatAmount(netTotal) + " €");
        variables.put("services.gross_total", formatAmount(grossTotal) + " €");
    }

    /** JSON für die Canvas-Darstellung des services.list-Bausteins. */
    public static String canvasPositionsJson(List<InvoiceMetadata.LineItem> items) {
        List<Map<String, String>> rows = new ArrayList<>();
        BigDecimal netTotal = BigDecimal.ZERO;
        BigDecimal vatTotal = BigDecimal.ZERO;

        for (InvoiceMetadata.LineItem item : items) {
            BigDecimal net = lineNet(item);
            Map<String, String> row = new HashMap<>();
            row.put("name", item.description());
            row.put("vat", vatLabel(item.vatPercent()));
            row.put("net", formatAmount(net) + " €");
            rows.add(row);
            netTotal = netTotal.add(net);
            vatTotal = vatTotal.add(lineVat(item));
        }

        Map<String, Object> data = new LinkedHashMap<>();
        data.put("rows", rows);
        data.put("netTotal", formatAmount(netTotal) + " €");
        data.put("vatTotal", formatAmount(vatTotal) + " €");
        data.put("grossTotal", formatAmount(netTotal.add(vatTotal)) + " €");

        try {
            return new ObjectMapper().writeValueAsString(data);
        } catch (Exception ex) {
            log.error("Canvas-Positionsdaten konnten nicht serialisiert werden: {}", ex.getMessage(), ex);
            return "null";
        }
    }

    /** Betrag mit zwei Nachkommastellen und Komma, z. B. "1200,00". */
    public static String formatAmount(BigDecimal amount) {
        return amount.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ",");
    }

    /** USt-Satz als Anzeige-Label, z. B. "19%" oder "7,5%". */
    public static String vatLabel(BigDecimal percent) {
        BigDecimal normalized = percent.stripTrailingZeros();
        if (normalized.scale() < 0) {
            normalized = normalized.setScale(0);
        }
        return normalized.toPlainString().replace('.', ',') + "%";
    }

    private static BigDecimal lineNet(InvoiceMetadata.LineItem item) {
        return item.quantity().multiply(item.unitPriceNet()).setScale(2, RoundingMode.HALF_UP);
    }

    private static BigDecimal lineVat(InvoiceMetadata.LineItem item) {
        return lineNet(item).multiply(item.vatPercent()).divide(HUNDRED, 2, RoundingMode.HALF_UP);
    }

    /** Gemeinsamer USt-Satz aller Positionen oder leer bei gemischten Sätzen. */
    private static String uniformVatLabel(List<InvoiceMetadata.LineItem> items) {
        List<String> labels = items.stream().map(item -> vatLabel(item.vatPercent())).distinct().toList();
        return labels.size() == 1 ? labels.get(0) : "";
    }
}
