package de.votian.services.service;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.FontFactory;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import de.votian.services.dto.InvoiceJobRowDto;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Service
public class InvoiceExportService {

    private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");

    public byte[] createCsv(String title, List<InvoiceJobRowDto> rows) {
        boolean includeServiceDetails = includeServiceDetails(rows);
        StringBuilder csv = new StringBuilder();
        csv.append(title).append("\n");
        csv.append("Auftrag;Kunde;Kostenstelle;Auftragszeit;Abschluss;Kommission;Abholung;Zustellung;Kurier;Umsatz;Kurierkosten;Marge;Exportiert;");
        if (includeServiceDetails) {
            csv.append("Leistungen;");
        }
        csv.append("Rechnungstext\n");
        for (InvoiceJobRowDto row : rows) {
            csv.append(value(row.getJobId())).append(';')
                    .append(csvValue(row.getCustomerName())).append(';')
                    .append(csvValue(row.getCostCenterName())).append(';')
                    .append(csvValue(format(row.getOrderTime()))).append(';')
                    .append(csvValue(format(row.getFinishTime()))).append(';')
                    .append(csvValue(row.getCommissionNo())).append(';')
                    .append(csvValue(row.getPickupName())).append(';')
                    .append(csvValue(row.getDeliveryName())).append(';')
                    .append(csvValue(row.getCourierSid())).append(';')
                    .append(csvValue(amount(row.getTotalPrice()))).append(';')
                    .append(csvValue(amount(row.getCourierPrice()))).append(';')
                    .append(csvValue(amount(row.getMargin()))).append(';')
                    .append(row.isExported() ? "Ja" : "Nein").append(';');
            if (includeServiceDetails) {
                csv.append(csvValue(row.getServiceDetails())).append(';');
            }
            csv
                    .append(csvValue(row.getInvoiceText()))
                    .append('\n');
        }
        return csv.toString().getBytes(StandardCharsets.UTF_8);
    }

    public byte[] createPdf(String title, List<InvoiceJobRowDto> rows) {
        try {
            boolean includeServiceDetails = includeServiceDetails(rows);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            Document document = new Document(PageSize.A4.rotate(), 24, 24, 24, 24);
            PdfWriter.getInstance(document, outputStream);
            document.open();
            document.add(new Paragraph(title, FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14)));
            document.add(new Paragraph("Erstellt: " + format(LocalDateTime.now()), FontFactory.getFont(FontFactory.HELVETICA, 9)));
            document.add(new Paragraph(" "));

            PdfPTable table = new PdfPTable(new float[]{1.1f, 1.8f, 1.5f, 1.4f, 1.8f, 1.8f, 1.2f, 1.1f, 1.1f, 2f});
            table.setWidthPercentage(100);
            addHeader(table, "Auftrag");
            addHeader(table, "Kunde");
            addHeader(table, "Kostenstelle");
            addHeader(table, "Kommission");
            addHeader(table, "Abholung");
            addHeader(table, "Zustellung");
            addHeader(table, "Kurier");
            addHeader(table, "Umsatz");
            addHeader(table, "Kurier");
            addHeader(table, "Zeiten");

            for (InvoiceJobRowDto row : rows) {
                addValue(table, value(row.getJobId()));
                addValue(table, row.getCustomerName());
                addValue(table, row.getCostCenterName());
                addValue(table, row.getCommissionNo());
                addValue(table, row.getPickupName());
                addValue(table, row.getDeliveryName());
                addValue(table, row.getCourierSid());
                addValue(table, amount(row.getTotalPrice()));
                addValue(table, amount(row.getCourierPrice()));
                addValue(table, format(row.getOrderTime()) + " / " + format(row.getFinishTime()));
                if (includeServiceDetails && hasServiceDetails(row)) {
                    PdfPCell detailCell = new PdfPCell(new Phrase(
                            "Leistungen: " + row.getServiceDetails().replace("\r", "").replace("\n", " | "),
                            FontFactory.getFont(FontFactory.HELVETICA, 8)
                    ));
                    detailCell.setColspan(10);
                    table.addCell(detailCell);
                }
            }

            document.add(table);
            if (!rows.isEmpty()) {
                document.add(new Paragraph(" "));
                BigDecimal revenue = rows.stream().map(InvoiceJobRowDto::getTotalPrice)
                        .filter(value -> value != null)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal courierCosts = rows.stream().map(InvoiceJobRowDto::getCourierPrice)
                        .filter(value -> value != null)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                document.add(new Paragraph("Gesamt Umsatz: " + amount(revenue), FontFactory.getFont(FontFactory.HELVETICA_BOLD, 10)));
                document.add(new Paragraph("Gesamt Kurierkosten: " + amount(courierCosts), FontFactory.getFont(FontFactory.HELVETICA_BOLD, 10)));
            }
            document.close();
            return outputStream.toByteArray();
        } catch (DocumentException exception) {
            throw new IllegalStateException("PDF konnte nicht erzeugt werden", exception);
        }
    }

    private void addHeader(PdfPTable table, String value) {
        PdfPCell cell = new PdfPCell(new Phrase(value, FontFactory.getFont(FontFactory.HELVETICA_BOLD, 9)));
        table.addCell(cell);
    }

    private void addValue(PdfPTable table, String value) {
        table.addCell(new PdfPCell(new Phrase(value != null ? value : "", FontFactory.getFont(FontFactory.HELVETICA, 8))));
    }

    private String csvValue(String value) {
        if (value == null) {
            return "";
        }
        return "\"" + value.replace("\"", "\"\"").replace("\n", " ").replace("\r", " ") + "\"";
    }

    private String value(Object value) {
        return value != null ? value.toString() : "";
    }

    private String amount(BigDecimal value) {
        return value != null ? value.toPlainString() : "";
    }

    private String format(LocalDateTime value) {
        return value != null ? value.format(DATE_TIME_FORMAT) : "";
    }

    private boolean includeServiceDetails(List<InvoiceJobRowDto> rows) {
        return rows.stream().anyMatch(this::hasServiceDetails);
    }

    private boolean hasServiceDetails(InvoiceJobRowDto row) {
        return row != null && row.getServiceDetails() != null && !row.getServiceDetails().isBlank();
    }
}
