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.datetimepicker.DateTimePicker;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Paragraph;
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.textfield.NumberField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.votian.web.model.SessionData;
import de.votian.web.service.RestClientService;
import org.springframework.core.ParameterizedTypeReference;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Route(value = "jobs/batch", layout = MainLayout.class)
@PageTitle("Votian - Listenerfassung")
public class JobBatchView extends VerticalLayout {

    private final RestClientService restClient;
    private final SessionData sessionData;

    private final ComboBox<CustomerOption> customerField = new ComboBox<>("Kunde");
    private final ComboBox<CostCenterOption> costCenterField = new ComboBox<>("Kostenstelle");
    private final ComboBox<CourierOption> defaultCourierField = new ComboBox<>("Standard-Kurier");
    private final VerticalLayout rowsLayout = new VerticalLayout();
    private final List<BatchRowEditor> rowEditors = new ArrayList<>();

    public JobBatchView(RestClientService restClient, SessionData sessionData) {
        this.restClient = restClient;
        this.sessionData = sessionData;

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

        customerField.setItemLabelGenerator(CustomerOption::label);
        customerField.setWidth("360px");
        customerField.addValueChangeListener(event -> loadCostCenters(event.getValue() != null ? event.getValue().id() : null));

        costCenterField.setItemLabelGenerator(CostCenterOption::label);
        costCenterField.setWidth("360px");

        defaultCourierField.setItemLabelGenerator(CourierOption::label);
        defaultCourierField.setWidth("260px");

        rowsLayout.setPadding(false);
        rowsLayout.setSpacing(true);
        rowsLayout.setWidthFull();

        Button addRowButton = new Button("Zeile hinzufuegen", event -> addRow(null));
        Button saveButton = new Button("Liste buchen", event -> saveBatch());
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        Button cancelButton = new Button("Zurueck", event -> getUI().ifPresent(ui -> ui.navigate(JobListView.class)));

        HorizontalLayout actions = new HorizontalLayout(addRowButton, saveButton, cancelButton);

        add(
                new H3("Listenerfassung"),
                new Paragraph("Erzeugt mehrere abgeschlossene Listenbuchungen fuer einen Kunden und eine Kostenstelle."),
                new HorizontalLayout(customerField, costCenterField, defaultCourierField),
                rowsLayout,
                actions
        );

        loadCustomers();
        loadCouriers();
        addInitialRows();
    }

    private void loadCustomers() {
        try {
            List<CustomerOption> customers = new ArrayList<>();
            if (sessionData.isCustomerUser() && sessionData.getCustomerId() != null) {
                @SuppressWarnings("unchecked")
                Map<String, Object> customer = restClient.get("/customers/" + sessionData.getCustomerId(), LinkedHashMap.class);
                customers.add(new CustomerOption(
                        sessionData.getCustomerId(),
                        str(customer.get("companyName")).isBlank() ? str(customer.get("eid")) : str(customer.get("companyName"))
                ));
            } else {
                List<LinkedHashMap<String, Object>> data = restClient.getList(
                        "/customers/hq/" + sessionData.getHeadquartersId(),
                        new ParameterizedTypeReference<>() {}
                );
                for (Map<String, Object> row : data) {
                    customers.add(new CustomerOption(
                            toLong(row.get("id")),
                            str(row.get("companyName")).isBlank() ? str(row.get("eid")) : str(row.get("companyName"))
                    ));
                }
            }
            customerField.setItems(customers);
            if (!customers.isEmpty()) {
                customerField.setValue(customers.get(0));
            }
            customerField.setEnabled(!sessionData.isCustomerUser());
        } catch (Exception e) {
            Notification.show("Kunden konnten nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadCostCenters(Long customerId) {
        if (customerId == null) {
            costCenterField.clear();
            costCenterField.setItems(List.of());
            return;
        }

        try {
            List<LinkedHashMap<String, Object>> data = restClient.getList(
                    "/customers/" + customerId + "/costcenters/visible",
                    new ParameterizedTypeReference<>() {}
            );
            List<CostCenterOption> options = new ArrayList<>();
            for (Map<String, Object> row : data) {
                options.add(new CostCenterOption(
                        toLong(row.get("id")),
                        str(row.get("path")).isBlank()
                                ? str(row.get("name"))
                                : str(row.get("path")) + " / " + str(row.get("name"))
                ));
            }
            costCenterField.setItems(options);
            if (!options.isEmpty()) {
                costCenterField.setValue(options.get(0));
            }
        } catch (Exception e) {
            Notification.show("Kostenstellen konnten nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadCouriers() {
        try {
            List<LinkedHashMap<String, Object>> data = restClient.getList(
                    "/couriers/hq/" + sessionData.getHeadquartersId(),
                    new ParameterizedTypeReference<>() {}
            );
            List<CourierOption> options = new ArrayList<>();
            options.add(new CourierOption(null, "Keine Zuordnung"));
            for (Map<String, Object> row : data) {
                options.add(new CourierOption(
                        toLong(row.get("id")),
                        str(row.get("sid")).isBlank() ? str(row.get("eid")) : str(row.get("sid"))
                ));
            }
            defaultCourierField.setItems(options);
            defaultCourierField.setValue(options.get(0));
        } catch (Exception e) {
            Notification.show("Kuriere konnten nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void addInitialRows() {
        if (rowEditors.isEmpty()) {
            addRow(null);
            addRow(null);
            addRow(null);
        }
    }

    private void addRow(BatchRowState initialState) {
        BatchRowEditor editor = new BatchRowEditor(initialState);
        rowEditors.add(editor);
        rowsLayout.add(editor);
    }

    private void removeRow(BatchRowEditor editor) {
        if (rowEditors.size() == 1) {
            editor.clear();
            return;
        }
        rowEditors.remove(editor);
        rowsLayout.remove(editor);
    }

    private void saveBatch() {
        if (customerField.getValue() == null || costCenterField.getValue() == null) {
            Notification.show("Bitte Kunde und Kostenstelle waehlen.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
            return;
        }

        List<Map<String, Object>> rows = new ArrayList<>();
        for (BatchRowEditor editor : rowEditors) {
            if (!editor.hasContent()) {
                continue;
            }
            if (!editor.isValid()) {
                Notification.show("Bitte alle ausgefuellten Zeilen vollstaendig erfassen.",
                                4000, Notification.Position.MIDDLE)
                        .addThemeVariants(NotificationVariant.LUMO_ERROR);
                return;
            }
            rows.add(editor.toRequest(defaultCourierField.getValue()));
        }

        if (rows.isEmpty()) {
            Notification.show("Bitte mindestens eine Zeile erfassen.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
            return;
        }

        try {
            Map<String, Object> request = new LinkedHashMap<>();
            request.put("hqId", sessionData.getHeadquartersId());
            request.put("customerId", customerField.getValue().id());
            request.put("costCenterId", costCenterField.getValue().id());
            request.put("rows", rows);
            restClient.post("/jobs/batch", request, Object[].class);
            Notification.show("Listenerfassung gespeichert.", 3000, Notification.Position.BOTTOM_START);
            getUI().ifPresent(ui -> ui.navigate(JobListView.class));
        } catch (Exception e) {
            Notification.show("Listenerfassung konnte nicht gespeichert werden: " + e.getMessage(),
                            5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    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 String str(Object value) {
        return value != null ? value.toString() : "";
    }

    private record CustomerOption(Long id, String label) {
    }

    private record CostCenterOption(Long id, String label) {
    }

    private record CourierOption(Long id, String label) {
    }

    private record BatchRowState(LocalDateTime orderTime,
                                 String description,
                                 String commissionNo,
                                 Double totalPrice,
                                 Double courierPrice,
                                 Long courierId) {
    }

    private class BatchRowEditor extends VerticalLayout {

        private final DateTimePicker orderTimeField = new DateTimePicker("Zeit");
        private final TextField descriptionField = new TextField("Auftrag / Tourenverlauf");
        private final TextField commissionField = new TextField("Kst./Referenz");
        private final NumberField totalPriceField = new NumberField("Fuhrbetrag");
        private final NumberField courierPriceField = new NumberField("Kurierpreis");
        private final ComboBox<CourierOption> courierField = new ComboBox<>("Kurier");

        private BatchRowEditor(BatchRowState initialState) {
            setPadding(true);
            setSpacing(true);
            getStyle()
                    .set("border", "1px solid var(--lumo-contrast-20pct)")
                    .set("border-radius", "var(--lumo-border-radius-l)");

            orderTimeField.setWidth("260px");
            orderTimeField.setValue(initialState != null && initialState.orderTime() != null
                    ? initialState.orderTime()
                    : LocalDateTime.now().withSecond(0).withNano(0));

            descriptionField.setWidthFull();
            commissionField.setWidth("220px");
            totalPriceField.setWidth("180px");
            courierPriceField.setWidth("180px");

            courierField.setItems(defaultCourierField.getListDataView().getItems().toList());
            courierField.setItemLabelGenerator(CourierOption::label);
            courierField.setWidth("220px");
            courierField.setValue(resolveCourier(initialState != null ? initialState.courierId() : null));

            if (initialState != null) {
                descriptionField.setValue(initialState.description() != null ? initialState.description() : "");
                commissionField.setValue(initialState.commissionNo() != null ? initialState.commissionNo() : "");
                totalPriceField.setValue(initialState.totalPrice());
                courierPriceField.setValue(initialState.courierPrice());
            }

            Button removeButton = new Button("Zeile entfernen", event -> removeRow(this));
            removeButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);

            FormLayout form = new FormLayout();
            form.setWidthFull();
            form.setResponsiveSteps(
                    new FormLayout.ResponsiveStep("0", 1),
                    new FormLayout.ResponsiveStep("720px", 2),
                    new FormLayout.ResponsiveStep("1080px", 3)
            );
            form.add(orderTimeField, descriptionField, commissionField, totalPriceField, courierPriceField, courierField);

            add(form, removeButton);
        }

        private void clear() {
            descriptionField.clear();
            commissionField.clear();
            totalPriceField.clear();
            courierPriceField.clear();
            courierField.setValue(resolveCourier(null));
        }

        private boolean hasContent() {
            return !descriptionField.getValue().isBlank()
                    || !commissionField.getValue().isBlank()
                    || totalPriceField.getValue() != null
                    || courierPriceField.getValue() != null
                    || (courierField.getValue() != null && courierField.getValue().id() != null);
        }

        private boolean isValid() {
            return orderTimeField.getValue() != null
                    && !descriptionField.getValue().isBlank()
                    && totalPriceField.getValue() != null;
        }

        private Map<String, Object> toRequest(CourierOption defaultCourier) {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("orderTime", orderTimeField.getValue().toString());
            row.put("description", descriptionField.getValue());
            row.put("commissionNo", commissionField.getValue());
            row.put("totalPrice", decimal(totalPriceField.getValue()));
            row.put("courierPrice", decimal(courierPriceField.getValue()));
            CourierOption selectedCourier = courierField.getValue() != null ? courierField.getValue() : defaultCourier;
            row.put("courierId", selectedCourier != null ? selectedCourier.id() : null);
            return row;
        }

        private CourierOption resolveCourier(Long courierId) {
            return defaultCourierField.getListDataView().getItems()
                    .filter(option -> option.id() == null ? courierId == null : option.id().equals(courierId))
                    .findFirst()
                    .orElse(defaultCourierField.getListDataView().getItems().findFirst().orElse(null));
        }

        private BigDecimal decimal(Double value) {
            return value != null ? BigDecimal.valueOf(value) : null;
        }
    }
}
