package de.votian.web.view;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Pre;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import de.votian.web.service.RestClientService;
import org.springframework.core.ParameterizedTypeReference;

import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Route(value = "courier-invoices", layout = MainLayout.class)
@PageTitle("Votian - Kurierauftraege")
public class CourierInvoiceView extends VerticalLayout {

    private final RestClientService restClient;

    private final DatePicker fromDate = new DatePicker("Von");
    private final DatePicker toDate = new DatePicker("Bis");
    private final ComboBox<HistoryMode> modeField = new ComboBox<>("Ansicht");

    private final Span totalJobs = new Span("-");
    private final Span totalRevenue = new Span("-");
    private final Span totalCourierPrice = new Span("-");

    private final Grid<CourierRow> grid = new Grid<>(CourierRow.class, false);
    private final Anchor csvExportLink = new Anchor();
    private final Anchor pdfExportLink = new Anchor();
    private final Button csvExportButton = new Button("CSV exportieren");
    private final Button pdfExportButton = new Button("PDF exportieren");

    private final Span detailJob = new Span("-");
    private final Span detailCustomer = new Span("-");
    private final Span detailRoute = new Span("-");
    private final Span detailTime = new Span("-");
    private final Span detailStatus = new Span("-");
    private final Pre invoiceText = new Pre();

    private CourierRow selectedRow;

    public CourierInvoiceView(RestClientService restClient) {
        this.restClient = restClient;

        setSizeFull();
        setPadding(true);
        setSpacing(true);

        configureFilters();
        configureGrid();
        configureExportLinks();
        configureDetails();

        HorizontalLayout filterBar = buildFilterBar();
        HorizontalLayout cards = buildCards();
        VerticalLayout gridPanel = buildGridPanel();
        VerticalLayout detailPanel = buildDetailPanel();
        HorizontalLayout workspace = new HorizontalLayout(gridPanel, detailPanel);
        workspace.setSizeFull();
        workspace.expand(gridPanel);

        add(new H3("Kurierauftraege"), filterBar, cards, workspace);
        expand(workspace);

        loadRows();
    }

    private void configureFilters() {
        fromDate.setValue(LocalDate.now().withDayOfMonth(1));
        toDate.setValue(LocalDate.now());
        modeField.setItems(HistoryMode.values());
        modeField.setItemLabelGenerator(HistoryMode::label);
        modeField.setValue(HistoryMode.FINISHED);
        fromDate.addValueChangeListener(event -> updateExportLinks());
        toDate.addValueChangeListener(event -> updateExportLinks());
        modeField.addValueChangeListener(event -> updateExportLinks());
    }

    private void configureGrid() {
        grid.addColumn(CourierRow::jobId).setHeader("Auftrag").setAutoWidth(true);
        grid.addColumn(CourierRow::customerName).setHeader("Kunde").setFlexGrow(1);
        grid.addColumn(CourierRow::pickupName).setHeader("Abholung").setFlexGrow(1);
        grid.addColumn(CourierRow::deliveryName).setHeader("Zustellung").setFlexGrow(1);
        grid.addColumn(CourierRow::displayTime).setHeader("Zeit").setAutoWidth(true);
        grid.addColumn(CourierRow::statusLabel).setHeader("Status").setAutoWidth(true);
        grid.addColumn(row -> formatAmount(row.totalPrice())).setHeader("Umsatz").setAutoWidth(true);
        grid.addColumn(row -> formatAmount(row.courierPrice())).setHeader("Kurierpreis").setAutoWidth(true);
        grid.addColumn(row -> row.exported() ? "Ja" : "Nein").setHeader("Export").setAutoWidth(true);
        grid.setSizeFull();
        grid.asSingleSelect().addValueChangeListener(event -> {
            selectedRow = event.getValue();
            updateDetail();
        });
    }

    private void configureExportLinks() {
        csvExportButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        pdfExportButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        csvExportLink.add(csvExportButton);
        pdfExportLink.add(pdfExportButton);
        csvExportLink.getElement().setAttribute("download", true);
        pdfExportLink.getElement().setAttribute("download", true);
        updateExportLinks();
    }

    private void configureDetails() {
        invoiceText.setWidthFull();
        invoiceText.getStyle()
                .set("background", "var(--lumo-contrast-5pct)")
                .set("padding", "var(--lumo-space-m)")
                .set("border-radius", "var(--lumo-border-radius-m)")
                .set("white-space", "pre-wrap")
                .set("margin", "0");
        clearDetail();
    }

    private HorizontalLayout buildFilterBar() {
        Button reloadButton = new Button("Neu laden", event -> loadRows());
        reloadButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        HorizontalLayout filterBar = new HorizontalLayout(fromDate, toDate, modeField, reloadButton, csvExportLink, pdfExportLink);
        filterBar.setAlignItems(Alignment.BASELINE);
        return filterBar;
    }

    private HorizontalLayout buildCards() {
        HorizontalLayout cards = new HorizontalLayout(
                createCard("Auftraege", totalJobs),
                createCard("Umsatz", totalRevenue),
                createCard("Kurierpreis", totalCourierPrice)
        );
        cards.setWidthFull();
        return cards;
    }

    private VerticalLayout buildGridPanel() {
        VerticalLayout panel = new VerticalLayout(new H4("Historie"), grid);
        panel.setPadding(false);
        panel.setSpacing(true);
        panel.setSizeFull();
        panel.expand(grid);
        return panel;
    }

    private VerticalLayout buildDetailPanel() {
        VerticalLayout panel = new VerticalLayout();
        panel.setPadding(true);
        panel.setSpacing(true);
        panel.setWidth("420px");
        panel.getStyle()
                .set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-l)");

        panel.add(
                new H4("Details"),
                detailLine("Auftrag", detailJob),
                detailLine("Kunde", detailCustomer),
                detailLine("Route", detailRoute),
                detailLine("Zeit", detailTime),
                detailLine("Status", detailStatus),
                new H4("Rechnungstext"),
                invoiceText
        );
        return panel;
    }

    private HorizontalLayout detailLine(String label, Span value) {
        Span caption = new Span(label + ":");
        caption.getStyle().set("font-weight", "600");
        return new HorizontalLayout(caption, value);
    }

    private VerticalLayout createCard(String title, Span value) {
        VerticalLayout card = new VerticalLayout();
        card.setPadding(true);
        card.setSpacing(false);
        card.getStyle()
                .set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-l)")
                .set("min-width", "180px");
        H3 headline = new H3(title);
        headline.getStyle().set("margin", "0");
        value.getStyle().set("font-size", "var(--lumo-font-size-xl)").set("font-weight", "700");
        card.add(headline, value);
        return card;
    }

    private void loadRows() {
        try {
            List<LinkedHashMap<String, Object>> data = restClient.getList(
                    "/invoices/courier-items?from=" + fromDate.getValue()
                            + "&to=" + toDate.getValue()
                            + "&mode=" + modeField.getValue().apiValue(),
                    new ParameterizedTypeReference<>() {}
            );
            List<CourierRow> rows = new ArrayList<>();
            BigDecimal revenueSum = BigDecimal.ZERO;
            BigDecimal courierSum = BigDecimal.ZERO;
            for (Map<String, Object> row : data) {
                BigDecimal totalPrice = toBigDecimal(row.get("totalPrice"));
                BigDecimal courierPrice = toBigDecimal(row.get("courierPrice"));
                rows.add(new CourierRow(
                        toLong(row.get("jobId")),
                        str(row.get("customerName")),
                        str(row.get("pickupName")),
                        str(row.get("deliveryName")),
                        str(row.get("orderTime")),
                        str(row.get("finishTime")),
                        toInteger(row.get("status")),
                        toInteger(row.get("incomplete")),
                        Boolean.TRUE.equals(row.get("exported")) || "true".equalsIgnoreCase(str(row.get("exported"))),
                        totalPrice,
                        courierPrice,
                        str(row.get("invoiceText"))
                ));
                if (totalPrice != null) {
                    revenueSum = revenueSum.add(totalPrice);
                }
                if (courierPrice != null) {
                    courierSum = courierSum.add(courierPrice);
                }
            }
            grid.setItems(rows);
            totalJobs.setText(String.valueOf(rows.size()));
            totalRevenue.setText(formatAmount(revenueSum));
            totalCourierPrice.setText(formatAmount(courierSum));
            grid.asSingleSelect().clear();
            if (!rows.isEmpty()) {
                grid.select(rows.get(0));
            } else {
                clearDetail();
            }
            updateExportLinks();
        } catch (Exception e) {
            Notification.show("Kurierhistorie konnte nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void updateExportLinks() {
        StreamResource csv = new StreamResource(
                "courier_jobs_" + modeField.getValue().apiValue() + "_" + fromDate.getValue() + "_" + toDate.getValue() + ".csv",
                () -> new ByteArrayInputStream(restClient.getBytes(
                        "/invoices/courier-export/csv?from=" + fromDate.getValue()
                                + "&to=" + toDate.getValue()
                                + "&mode=" + modeField.getValue().apiValue()
                ))
        );
        StreamResource pdf = new StreamResource(
                "courier_jobs_" + modeField.getValue().apiValue() + "_" + fromDate.getValue() + "_" + toDate.getValue() + ".pdf",
                () -> new ByteArrayInputStream(restClient.getBytes(
                        "/invoices/courier-export/pdf?from=" + fromDate.getValue()
                                + "&to=" + toDate.getValue()
                                + "&mode=" + modeField.getValue().apiValue()
                ))
        );
        csvExportLink.setHref(csv);
        pdfExportLink.setHref(pdf);
    }

    private void updateDetail() {
        if (selectedRow == null) {
            clearDetail();
            return;
        }
        detailJob.setText(String.valueOf(selectedRow.jobId()));
        detailCustomer.setText(selectedRow.customerName());
        detailRoute.setText(selectedRow.pickupName() + " -> " + selectedRow.deliveryName());
        detailTime.setText(selectedRow.displayTime());
        detailStatus.setText(selectedRow.statusLabel());
        invoiceText.setText(selectedRow.invoiceText().isBlank() ? "(kein Rechnungstext)" : selectedRow.invoiceText());
    }

    private void clearDetail() {
        detailJob.setText("-");
        detailCustomer.setText("-");
        detailRoute.setText("-");
        detailTime.setText("-");
        detailStatus.setText("-");
        invoiceText.setText("(kein Auftrag ausgewaehlt)");
    }

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

    private BigDecimal toBigDecimal(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof BigDecimal decimal) {
            return decimal;
        }
        try {
            return new BigDecimal(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Long toLong(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number number) {
            return number.longValue();
        }
        try {
            return Long.parseLong(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Integer toInteger(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number number) {
            return number.intValue();
        }
        try {
            return Integer.parseInt(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

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

    private record CourierRow(Long jobId, String customerName, String pickupName, String deliveryName,
                              String orderTime, String finishTime, Integer status, Integer incomplete,
                              boolean exported, BigDecimal totalPrice, BigDecimal courierPrice,
                              String invoiceText) {

        private String displayTime() {
            String value = finishTime != null && !finishTime.isBlank() ? finishTime : orderTime;
            return value != null ? value.replace("T", " ") : "-";
        }

        private String statusLabel() {
            String base = switch (status != null ? status : -1) {
                case 0 -> "Blockiert";
                case 1 -> "Zugewiesen";
                case 2 -> "In Zustellung";
                case 8 -> "Zugestellt";
                case 9 -> "Abgeschlossen";
                case 10 -> "Storniert";
                default -> status != null ? String.valueOf(status) : "-";
            };
            if (exported) {
                return base + ", exportiert";
            }
            if (incomplete != null && incomplete > 0) {
                return base + ", unvollstaendig";
            }
            return base;
        }
    }

    private enum HistoryMode {
        FINISHED("Erledigt", "finished"),
        RUNNING("Laufend", "running");

        private final String label;
        private final String apiValue;

        HistoryMode(String label, String apiValue) {
            this.label = label;
            this.apiValue = apiValue;
        }

        private String label() {
            return label;
        }

        private String apiValue() {
            return apiValue;
        }
    }
}
