package com.ecep.contract.controller.vendor.purchase.order;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.util.StringUtils;

import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.cell.InventoryTableCell;
import com.ecep.contract.controller.vendor.PurchaseOrderWindowController;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.PurchaseOrder;
import com.ecep.contract.model.PurchaseOrderItem;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.InventoryService;
import com.ecep.contract.service.PurchaseOrderItemService;
import com.ecep.contract.service.PurchaseOrdersService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ProxyUtils;
import com.ecep.contract.vm.ContractItemComposeViewModel;
import com.ecep.contract.vm.PurchaseOrderItemViewModel;
import com.ecep.contract.vm.PurchaseOrderViewModel;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.util.Callback;
import javafx.util.Duration;
import lombok.Setter;

@FxmlPath("/ui/contract/purchase-orders-tab-item.fxml")
public class PurchaseOrderTabSkinItems
        extends
        AbstEntityTableTabSkin<PurchaseOrderWindowController, PurchaseOrder, PurchaseOrderViewModel, PurchaseOrderItem, PurchaseOrderItemViewModel>
        implements TabSkin {
    @Setter
    PurchaseOrdersService purchaseOrdersService;
    @Setter
    PurchaseOrderItemService purchaseOrderItemService;
    @Setter
    private InventoryService inventoryService;

    /* 以下是合同内容 */
    public TableView<PurchaseOrderItemViewModel> itemTable;
    public TableColumn<PurchaseOrderItemViewModel, Number> idColumn;
    public TableColumn<PurchaseOrderItemViewModel, Number> refIdColumn;
    public TableColumn<PurchaseOrderItemViewModel, Inventory> nameColumn;
    public TableColumn<PurchaseOrderItemViewModel, String> unitColumn;

    public TableColumn<PurchaseOrderItemViewModel, Double> itemTable_quantityColumn;
    public TableColumn<PurchaseOrderItemViewModel, Double> itemTable_taxPriceColumn;
    public TableColumn<PurchaseOrderItemViewModel, Double> itemTable_taxRateColumn;
    public TableColumn<PurchaseOrderItemViewModel, Double> itemTable_exclusiveTaxPriceColumn;
    public TableColumn<PurchaseOrderItemViewModel, Double> itemTable_exclusiveTaxAmountColumn;
    public TableColumn<PurchaseOrderItemViewModel, Double> itemTable_taxAmountColumn;

    public TableColumn<PurchaseOrderItemViewModel, LocalDate> itemTable_arriveDateColumn;
    public TableColumn<PurchaseOrderItemViewModel, String> itemTable_descriptionColumn;

    public PurchaseOrderTabSkinItems(PurchaseOrderWindowController controller) {
        super(controller);
    }

    @Override
    public Tab getTab() {
        return controller.itemTab;
    }

    @Override
    public TableView<PurchaseOrderItemViewModel> getTableView() {
        return itemTable;
    }

    PurchaseOrderItemService getPurchaseOrderItemService() {
        if (purchaseOrderItemService == null) {
            purchaseOrderItemService = getBean(PurchaseOrderItemService.class);
        }
        return purchaseOrderItemService;
    }

    @Override
    protected PurchaseOrderItemService getViewModelService() {
        return getPurchaseOrderItemService();
    }

    static class UnitTableCell extends TableCell<PurchaseOrderItemViewModel, String> {
        {
            setAlignment(Pos.CENTER);
        }

        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (empty || item == null) {
                setText(null);
                return;
            }
            setText(item);
        }
    }

    PurchaseOrdersService getPurchaseOrdersService() {
        if (purchaseOrdersService == null) {
            purchaseOrdersService = getBean(PurchaseOrdersService.class);
        }
        return purchaseOrdersService;
    }

    InventoryService getInventoryService() {
        if (inventoryService == null) {
            inventoryService = getBean(InventoryService.class);
        }
        return inventoryService;
    }

    @Override
    public Map<String, Object> getSpecification(PurchaseOrder parent) {
        Map<String, Object> params = getSpecification();
        params.put("order", parent);
        return params;
    }

    ContractItemComposeViewModel createNew(String key, ContractItem item) {
        ContractItemComposeViewModel m = new ContractItemComposeViewModel();
        // set key
        m.getTitle().set(item.getTitle());
        m.getSpecification().set(item.getSpecification());
        return m;
    }

    private void addIn(HashMap<String, ContractItemComposeViewModel> map, List<ContractItem> list) {
        for (ContractItem item : list) {
            String key = makeKey(item);
            ContractItemComposeViewModel model = map.computeIfAbsent(key, k -> createNew(k, item));
            model.getUnit().set(item.getUnit());
            model.getIn().add(item);
        }
    }

    private void addOut(List<ContractItem> list, HashMap<String, ContractItemComposeViewModel> map) {
        for (ContractItem item : list) {
            String key = makeKey(item);
            ContractItemComposeViewModel model = map.computeIfAbsent(key, k -> createNew(k, item));
            model.getUnit().set(item.getUnit());
            model.getOut().add(item);
        }
    }

    @Override
    public void initializeTab() {
        super.initializeTab();

        idColumn.setCellValueFactory(param -> param.getValue().getId());
        refIdColumn.setCellValueFactory(param -> param.getValue().getRefId());
        nameColumn.setCellValueFactory(param -> param.getValue().getInventory());
        nameColumn.setCellFactory(col -> new InventoryTableCell<>());
        unitColumn.setCellValueFactory(param -> param.getValue().getInventory().map(v -> {
            if (v == null)
                return null;
            if (!ProxyUtils.isInitialized(v)) {
            v = getInventoryService().findById(v.getId());
        }
            return v.getUnit();
        }));
        unitColumn.setCellFactory(col -> new UnitTableCell());
        NumberCellFactory v1 = new NumberCellFactory("%,.1f", null);
        NumberCellFactory v2 = new NumberCellFactory("%,.2f", null);
        NumberCellFactory v3 = new NumberCellFactory("%,.2f", "custom-cell");

        // 进项

        itemTable_exclusiveTaxPriceColumn
                .setCellValueFactory(param -> param.getValue().getExclusiveTaxPrice().asObject());
        itemTable_exclusiveTaxPriceColumn.setCellFactory(v2);

        itemTable_taxRateColumn.setCellValueFactory(param -> param.getValue().getTaxRate().asObject());
        itemTable_taxRateColumn.setCellFactory(v1);

        itemTable_taxPriceColumn.setCellValueFactory(param -> param.getValue().getPrice().asObject());
        itemTable_taxPriceColumn.setCellFactory(v2);

        itemTable_quantityColumn.setCellValueFactory(param -> param.getValue().getQuantity().asObject());
        itemTable_quantityColumn.setCellFactory(v1);

        itemTable_exclusiveTaxAmountColumn.setCellValueFactory(
                param -> param.getValue().getExclusiveTaxPrice().multiply(param.getValue().getQuantity()).asObject());
        itemTable_exclusiveTaxAmountColumn.setCellFactory(v2);
        itemTable_taxAmountColumn.setCellValueFactory(
                param -> param.getValue().getPrice().multiply(param.getValue().getQuantity()).asObject());
        itemTable_taxAmountColumn.setCellFactory(v2);

        itemTable_arriveDateColumn.setCellValueFactory(param -> param.getValue().getArriveDate());
        // itemTable_startDateColumn.setCellFactory(v2);

        itemTable_descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
    }

    @Override
    protected void createContextMenu(ContextMenu contextMenu) {
        MenuItem item2 = new MenuItem("刷新");
        item2.setOnAction(this::onTableRefreshAction);

        MenuItem item3 = new MenuItem("删除");
        item3.setOnAction(this::onTableDeleteAction);

        contextMenu.getItems().addAll(item2, item3);
    }

    private void sum(ContractItemComposeViewModel model, Contract contract,
            HashMap<String, ContractItemComposeViewModel> map) {
        double inQuantity = map.values().stream().map(ContractItemComposeViewModel::getIn)
                .mapToDouble(v -> v.getQuantity().get()).sum();
        Platform.runLater(() -> model.getIn().getQuantity().set(inQuantity));

        double inTaxAmount = map.values().stream().map(ContractItemComposeViewModel::getIn)
                .mapToDouble(v -> v.getTaxAmount().get()).sum();
        Platform.runLater(() -> model.getIn().getTaxAmount().set(inTaxAmount));

        double inExclusiveTaxAmount = map.values().stream().map(ContractItemComposeViewModel::getIn)
                .mapToDouble(v -> v.getExclusiveTaxAmount().get()).sum();
        Platform.runLater(() -> model.getIn().getExclusiveTaxAmount().set(inExclusiveTaxAmount));

        double outQuantity = map.values().stream().map(ContractItemComposeViewModel::getOut)
                .mapToDouble(v -> v.getQuantity().get()).sum();
        Platform.runLater(() -> model.getOut().getQuantity().set(outQuantity));

        double outTaxAmount = map.values().stream().map(ContractItemComposeViewModel::getOut)
                .mapToDouble(v -> v.getTaxAmount().get()).sum();
        Platform.runLater(() -> model.getOut().getTaxAmount().set(outTaxAmount));

        double outExclusiveTaxAmount = map.values().stream().map(ContractItemComposeViewModel::getOut)
                .mapToDouble(v -> v.getExclusiveTaxAmount().get()).sum();
        Platform.runLater(() -> model.getOut().getExclusiveTaxAmount().set(outExclusiveTaxAmount));
    }

    String makeKey(ContractItem item) {
        return item.getTitle() + "-" + item.getSpecification();
    }

    static class NumberTableCell extends TableCell<PurchaseOrderItemViewModel, Double> {
        // "%.1f"
        private final String format;

        public NumberTableCell(String format, String styleClass) {
            this.format = format;
            if (StringUtils.hasText(styleClass)) {
                getStyleClass().add(styleClass);
            }
        }

        @Override
        protected void updateItem(Double item, boolean empty) {
            super.updateItem(item, empty);
            if (empty || item == null) {
                setText(null);
                return;
            }
            setText(String.format(format, item));
        }

        private void setTooltip(ObservableList<ContractItem> list, Function<ContractItem, Double> getPrice) {
            String text = list.stream().map(v -> {
                Contract contract = v.getContract();
                if (!ProxyUtils.isInitialized(contract)) {
                    contract = SpringApp.getBean(ContractService.class).findById(contract.getId());
                }
                Double price = getPrice.apply(v);
                double quantity = v.getQuantity();
                return contract.getCode() + " " + String.format(format, price) + " x " + quantity + " = "
                        + String.format(format, (price * quantity));
            }).collect(Collectors.joining("\n"));
            setTooltip(text);
        }

        private void setTooltip(String text) {
            Tooltip tooltip = getTooltip();
            if (StringUtils.hasText(text)) {
                if (tooltip == null) {
                    tooltip = new Tooltip();
                    setTooltip(tooltip);
                }
                tooltip.setShowDelay(Duration.ZERO);
                tooltip.setHideDelay(Duration.ZERO);
                tooltip.setText(text);
            } else {
                if (tooltip != null) {
                    tooltip.setText(null);
                }
            }
        }
    }

    static class NumberCellFactory implements
            Callback<TableColumn<PurchaseOrderItemViewModel, Double>, TableCell<PurchaseOrderItemViewModel, Double>> {
        private final String format;
        private final String styleClass;

        public NumberCellFactory(String format, String styleClass) {
            this.format = format;
            this.styleClass = styleClass;
        }

        @Override
        public TableCell<PurchaseOrderItemViewModel, Double> call(
                TableColumn<PurchaseOrderItemViewModel, Double> param) {
            NumberTableCell cell = new NumberTableCell(format, styleClass);
            cell.setAlignment(Pos.CENTER_RIGHT);
            return cell;
        }
    }

}
