package com.ecep.contract.controller.tab;

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 com.ecep.contract.util.ProxyUtils;
import org.springframework.util.StringUtils;

import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.customer.SalesOrderWindowController;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.SalesOrder;
import com.ecep.contract.model.SalesOrderItem;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.SaleOrdersService;
import com.ecep.contract.service.SalesOrderItemService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.ContractItemComposeViewModel;
import com.ecep.contract.vm.SalesOrderItemViewModel;
import com.ecep.contract.vm.SalesOrderViewModel;

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.Tooltip;
import javafx.util.Callback;
import javafx.util.Duration;

@FxmlPath("/ui/contract/sale-orders-tab-item.fxml")
public class SalesOrderTabSkinItems
        extends
        AbstEntityTableTabSkin<SalesOrderWindowController, SalesOrder, SalesOrderViewModel, SalesOrderItem, SalesOrderItemViewModel>
        implements TabSkin {

    private SalesOrderItemService itemService;

    /* 以下是合同内容 */
    public TableColumn<SalesOrderItemViewModel, Number> itemTable_idColumn;
    public TableColumn<SalesOrderItemViewModel, String> itemTable_codeColumn;
    public TableColumn<SalesOrderItemViewModel, String> itemTable_nameColumn;
    public TableColumn<SalesOrderItemViewModel, String> itemTable_unitColumn;

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

    public TableColumn<SalesOrderItemViewModel, LocalDate> itemTable_startDateColumn;
    public TableColumn<SalesOrderItemViewModel, LocalDate> itemTable_endDateColumn;
    public TableColumn<SalesOrderItemViewModel, String> itemTable_descriptionColumn;

    public SalesOrderTabSkinItems(SalesOrderWindowController controller) {
        super(controller);
    }

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

    SaleOrdersService getSaleOrdersService() {
        return controller.getViewModelService();
    }

    SalesOrderItemService getItemService() {
        if (itemService == null) {
            itemService = SpringApp.getBean(SalesOrderItemService.class);
        }
        return itemService;
    }

    @Override
    protected SalesOrderItemService getViewModelService() {
        return getItemService();
    }

    static class UnitTableCell extends TableCell<SalesOrderItemViewModel, 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);
        }
    }

    @Override
    public Map<String, Object> getSpecification(SalesOrder 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();

        itemTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
        itemTable_codeColumn.setCellValueFactory(param -> param.getValue().getCode());
        itemTable_nameColumn.setCellValueFactory(param -> param.getValue().getName());
        // itemTable_unitColumn.setCellValueFactory(param ->
        // param.getValue().getUnit());
        itemTable_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_taxAmountColumn.setCellValueFactory(param ->
        // param.getValue().getTaxAmount().asObject());
        // itemTable_taxAmountColumn.setCellFactory(v3);

        // itemTable_exclusiveTaxAmountColumn.setCellValueFactory(param ->
        // param.getValue().getExclusiveTaxPrice().asObject());
        // itemTable_exclusiveTaxAmountColumn.setCellFactory(v3);

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

        itemTable_endDateColumn.setCellValueFactory(param -> param.getValue().getEndDate());
        // itemTable_endDateColumn.setCellFactory(v3);

        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<SalesOrderItemViewModel, 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<SalesOrderItemViewModel, Double>, TableCell<SalesOrderItemViewModel, Double>> {
        private final String format;
        private final String styleClass;

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

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