package com.ecep.contract.controller.project;

import java.util.Objects;
import java.util.stream.Stream;

import org.springframework.util.StringUtils;

import com.ecep.contract.controller.ComboBoxUtils;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.model.DeliverySignMethod;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.ProductType;
import com.ecep.contract.model.ProductUsage;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectCost;
import com.ecep.contract.model.ProjectIndustry;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.model.ProjectType;
import com.ecep.contract.service.DeliverySignMethodService;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.service.ProductTypeService;
import com.ecep.contract.service.ProductUsageService;
import com.ecep.contract.service.ProjectCostService;
import com.ecep.contract.service.ProjectIndustryService;
import com.ecep.contract.service.ProjectTypeService;
import com.ecep.contract.service.SaleTypeService;
import com.ecep.contract.util.UITools;

import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;

/**
 * 基础信息
 */
public class ProjectTabSkinBase extends AbstProjectBasedTabSkin implements TabSkin {
    @Setter
    EmployeeStringConverter employeeStringConverter;
    @Setter
    LocalDateStringConverter localDateStringConverter;
    @Setter
    EmployeeService employeeService;

    @Setter
    private ProjectTypeService projectTypeService;
    @Setter
    private ProductTypeService productTypeService;
    @Setter
    private SaleTypeService saleTypeService;
    @Setter
    private ProductUsageService productUsageService;
    @Setter
    private ProjectIndustryService projectIndustryService;
    @Setter
    private DeliverySignMethodService deliverySignMethodService;


    public ProjectTabSkinBase(ProjectWindowController controller) {
        super(controller);
    }

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

    private EmployeeService getEmployeeService() {
        if (employeeService == null) {
            employeeService = getBean(EmployeeService.class);
        }
        return employeeService;
    }

    private ProjectTypeService getProjectTypeService() {
        if (projectTypeService == null) {
            projectTypeService = getBean(ProjectTypeService.class);
        }
        return projectTypeService;
    }

    private ProductTypeService getProductTypeService() {
        if (productTypeService == null) {
            productTypeService = getBean(ProductTypeService.class);
        }
        return productTypeService;
    }

    private SaleTypeService getSaleTypeService() {
        if (saleTypeService == null) {
            saleTypeService = getBean(SaleTypeService.class);
        }
        return saleTypeService;
    }

    private ProductUsageService getProductUsageService() {
        if (productUsageService == null) {
            productUsageService = getBean(ProductUsageService.class);
        }
        return productUsageService;
    }

    private ProjectIndustryService getProjectIndustryService() {
        if (projectIndustryService == null) {
            projectIndustryService = getBean(ProjectIndustryService.class);
        }
        return projectIndustryService;
    }

    private DeliverySignMethodService getDeliverySignMethodService() {
        if (deliverySignMethodService == null) {
            deliverySignMethodService = getBean(DeliverySignMethodService.class);
        }
        return deliverySignMethodService;
    }

    private EmployeeStringConverter getEmployeeStringConverter() {
        if (employeeStringConverter == null) {
            employeeStringConverter = getBean(EmployeeStringConverter.class);
        }
        return employeeStringConverter;
    }

    @Override
    public void initializeTab() {
        controller.deleteBtn.setOnAction(this::onDeleteAction);
        controller.nameField.textProperty().bindBidirectional(viewModel.getName());
        controller.codeField.textProperty().bind(viewModel.getCode());
        NumberStringConverter stringConverter = new NumberStringConverter();
        Bindings.bindBidirectional(controller.codeYearField.textProperty(), viewModel.getCodeYear(), stringConverter);
        Bindings.bindBidirectional(controller.codeSequenceNumberField.textProperty(), viewModel.getCodeSequenceNumber(), stringConverter);

        controller.addressField.textProperty().bindBidirectional(viewModel.getAddress());
        controller.useBidField.selectedProperty().bindBidirectional(viewModel.getUseBid());
        controller.useOfferField.selectedProperty().bindBidirectional(viewModel.getUseOffer());
        controller.standardPayWayField.selectedProperty().bindBidirectional(viewModel.getStandardPayWay());
        Bindings.bindBidirectional(controller.amountField.textProperty(), viewModel.getAmount(), new NumberStringConverter());

        initializeSaleTypeField(controller.saleTypeField);
        initializeSaleIndustryField(controller.industryField);
        initializeProjectTypeField(controller.projectTypeField);
        initializeProductTypeField(controller.productTypeField);
        initializeDeliverySignMethodField(controller.saleTypeField, controller.deliverySignMethodField);
        initializeProductUsageField(controller.productUsageField);

        controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());

        employeeAutoCompletion(controller.applicantField, viewModel.getApplicant());
        employeeAutoCompletion(controller.authorizerField, viewModel.getAuthorizer());

        controller.createdField.setConverter(localDateStringConverter);
        controller.createdField.valueProperty().bindBidirectional(viewModel.getCreated());
        controller.plannedStartTimeField.setConverter(localDateStringConverter);
        controller.plannedStartTimeField.valueProperty().bindBidirectional(viewModel.getPlannedStartTime());
        controller.plannedCompletionTimeField.setConverter(localDateStringConverter);
        controller.plannedCompletionTimeField.valueProperty().bindBidirectional(viewModel.getPlannedCompletionTime());

        controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
    }

    private void onDeleteAction(ActionEvent event) {
        ProjectCostService projectCostService = getBean(ProjectCostService.class);
        Project project = getEntity();
        for (ProjectCost cost : projectCostService.findAllByProject(project)) {
            projectCostService.delete(cost);
        }
        getProjectService().delete(project);
        controller.root.getScene().getWindow().hide();
    }

    private void initializeSaleIndustryField(ComboBox<ProjectIndustry> field) {
        ComboBoxUtils.initialComboBox(field, viewModel.getIndustry(), getProjectIndustryService().findAll(), true);
    }

    private void initializeProjectTypeField(ComboBox<ProjectType> field) {
        ComboBoxUtils.initialComboBox(field, viewModel.getProjectType(), getProjectTypeService().findAll(), true);
    }

    private void initializeProductTypeField(ComboBox<ProductType> field) {
        ObservableList<ProductType> list = FXCollections.observableArrayList();
        list.add(null);
        list.addAll(getProductTypeService().findAll());
        field.setItems(list);
        field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            viewModel.getProductType().set(newValue);
        });
        EntityStringConverter<ProductType> converter = new EntityStringConverter<>();
        converter.setInitialized(type -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getId(), type.getId())).findFirst().orElse(null);
        });
        converter.setFromString(name -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
        });
        field.setConverter(converter);
        field.valueProperty().bindBidirectional(viewModel.getProductType());
    }

    private void initializeDeliverySignMethodField(
            ComboBox<ProjectSaleType> saleTypeField,
            ComboBox<DeliverySignMethod> field
    ) {
        ObservableList<DeliverySignMethod> list = FXCollections.observableArrayList();
        list.add(null);
        list.addAll(getDeliverySignMethodService().findAll());
        field.setItems(list);

        ProjectSaleType saleType = saleTypeField.getValue();
        if (saleType != null) {
            field.setItems(list.filtered(p -> {
                return p == null || Objects.equals(p.getSaleType(), saleType);
            }));
        }

        saleTypeField.valueProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) {
                field.setItems(list);
            } else {
                field.setItems(list.filtered(p -> {
                    return p == null || Objects.equals(p.getSaleType(), newValue);
                }));
            }
        });

        field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            viewModel.getDeliverySignMethod().set(newValue);
        });
        EntityStringConverter<DeliverySignMethod> converter = new EntityStringConverter<>();
        converter.setInitialized(signMethod -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getId(), signMethod.getId())).findFirst().orElse(null);
        });
        converter.setFromString(name -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
        });
        field.setConverter(converter);
        field.valueProperty().bindBidirectional(viewModel.getDeliverySignMethod());
    }

    private void initializeProductUsageField(ComboBox<ProductUsage> field) {
        ObservableList<ProductUsage> list = FXCollections.observableArrayList();
        list.add(null);
        list.addAll(getProductUsageService().findAll());
        field.setItems(list);
        field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            viewModel.getProductUsage().set(newValue);
        });
        EntityStringConverter<ProductUsage> converter = new EntityStringConverter<>();
        converter.setInitialized(usage -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getId(), usage.getId())).findFirst().orElse(null);
        });
        converter.setFromString(name -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
        });
        field.setConverter(converter);
        field.valueProperty().bindBidirectional(viewModel.getProductUsage());
    }

    private void initializeSaleTypeField(ComboBox<ProjectSaleType> field) {
        ObservableList<ProjectSaleType> list = FXCollections.observableArrayList();
        list.add(null);
        list.addAll(getSaleTypeService().findAll());
        field.setItems(list);
        field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            viewModel.getSaleType().set(newValue);
            // 当 SequenceNumber 为 0 时， 自动检索最新的 SequenceNumber
            if (viewModel.getCodeSequenceNumber().get() == 0 && viewModel.getCodeYear().get() != 0) {
                int nextCodeSequenceNumber = getProjectService().getNextCodeSequenceNumber(newValue, viewModel.getCodeYear().get());
                viewModel.getCodeSequenceNumber().set(nextCodeSequenceNumber);
            }
        });
        field.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(ProjectSaleType item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText("-");
                    setStyle("");
                } else {
                    setText(item.getName());
                    if (item.isActive()) {
                        this.setStyle("");
                    } else {
                        this.setStyle("-fx-text-fill: grey;");
                    }
                }
            }
        });
        EntityStringConverter<ProjectSaleType> converter = new EntityStringConverter<>();
        converter.setInitialized(saleType -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getId(), saleType.getId())).findFirst().orElse(null);
        });
        converter.setFromString(name -> {
            return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
        });
        field.setConverter(converter);
        field.valueProperty().bindBidirectional(viewModel.getSaleType());

    }

    private void employeeAutoCompletion(TextField textField, SimpleObjectProperty<Employee> property) {
        UITools.autoCompletion(textField, property, getEmployeeStringConverter());
    }

    public void save() {
        makeProjectCode();
        super.save();
    }

    private void makeProjectCode() {
        StringBuilder sb = new StringBuilder();

        ProjectSaleType saleType = controller.saleTypeField.getValue();
        if (saleType == null) {
            sb.append("_");
        } else {
            controller.saleTypeField.getItems().stream()
                    .filter(item -> item != null && item.getId().equals(saleType.getId()))
                    .findFirst()
                    .ifPresent(item -> sb.append(item.getCode()));
        }


        int year = 0;
        {
            String text = controller.codeYearField.getText();
            if (StringUtils.hasText(text)) {
                year = Integer.parseInt(text);
            }
        }
        if (year < 10) {
            sb.append("0");
        }
        sb.append(year);

        int sn = 0;
        {
            String text = controller.codeSequenceNumberField.getText();
            if (StringUtils.hasText(text)) {
                sn = Integer.parseInt(text);
            }
        }
        if (sn < 10) {
            sb.append("00");
        } else if (sn < 100) {
            sb.append("0");
        }
        sb.append(sn);

        DeliverySignMethod signMethod = controller.deliverySignMethodField.getValue();
        ProductType productType = controller.productTypeField.getValue();
        ProjectType projectType = controller.projectTypeField.getValue();
        ProjectIndustry industry = controller.industryField.getValue();
        ProductUsage productUsage = controller.productUsageField.getValue();


        if (!Stream.of(signMethod, productType, projectType, industry, productUsage)
                .allMatch(Objects::isNull)) {
            if (signMethod == null) {
                sb.append("_");
            } else {
                controller.deliverySignMethodField.getItems().stream()
                        .filter(item -> item != null && item.getId().equals(signMethod.getId()))
                        .findFirst()
                        .ifPresent(item -> sb.append(item.getCode()));
            }

            if (productType == null) {
                sb.append("_");
            } else {
                controller.productTypeField.getItems().stream()
                        .filter(item -> item != null && item.getId().equals(productType.getId()))
                        .findFirst()
                        .ifPresent(item -> sb.append(item.getCode()));
            }


            if (projectType == null) {
                sb.append("_");
            } else {
                controller.projectTypeField.getItems().stream()
                        .filter(item -> item != null && item.getId().equals(projectType.getId()))
                        .findFirst()
                        .ifPresent(item -> sb.append(item.getCode()));
            }


            if (industry != null) {
                controller.industryField.getItems().stream()
                        .filter(item -> item != null && item.getId().equals(industry.getId()))
                        .findFirst()
                        .ifPresent(item -> sb.append(item.getCode()));
            } else {
                sb.append("_");
            }


            if (productUsage != null) {
                controller.productUsageField.getItems().stream()
                        .filter(item -> item != null && item.getId().equals(productUsage.getId()))
                        .findFirst()
                        .ifPresent(item -> sb.append(item.getCode()));
            }
        }

        viewModel.getCode().set(sb.toString());
    }

}
