package com.ecep.contract.controller.contract;

import static com.ecep.contract.util.TableViewUtils.bindDoubleClicked;
import static com.ecep.contract.util.TableViewUtils.bindEnterPressed;

import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.ecep.contract.Message;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.Employee;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.service.ProjectSaleTypeService;
import com.ecep.contract.service.VendorGroupService;
import com.ecep.contract.task.ContractVerifyComm;
import com.ecep.contract.task.ContractVerifyResultExportAsExcelFileTasker;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.util.UITools;

import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/contract/contract-verify.fxml")
public class ContractVerifyWindowController extends BaseController {
    private static final Logger logger = LoggerFactory.getLogger(ContractVerifyWindowController.class);

    @Override
    public Stage show(FXMLLoader loader, Window owner, Modality modality) {
        return super.show(loader, owner, modality);
    }

    @Setter
    @Getter
    public static class MessageExt extends Message {
        private String prefix = null;

        public MessageExt(Level level, String message) {
            super(level, message);
        }
    }

    @Data
    public static class Model implements MessageHolder {
        private SimpleStringProperty code = new SimpleStringProperty();
        private SimpleStringProperty name = new SimpleStringProperty();
        private SimpleObjectProperty<Employee> employee = new SimpleObjectProperty<>();
        private SimpleObjectProperty<LocalDate> setupDate = new SimpleObjectProperty<>();
        private SimpleListProperty<MessageExt> messages = new SimpleListProperty<>(FXCollections.observableArrayList());

        @Override
        public void addMessage(Level level, String message) {
            MessageExt msg = new MessageExt(level, message);
            messages.add(msg);
        }

        @Override
        public MessageHolder sub(String prefix) {
            return (level, message) -> {
                MessageExt msg = new MessageExt(level, message);
                msg.setPrefix(prefix);
                msg.setMessage(message);
                messages.add(msg);
            };
        }
    }

    static class StateTableCell extends TableCell<Model, ObservableList<MessageExt>> {
        Label message2Label(MessageExt message) {
            Label label = new Label(message.getMessage());
            label.getStyleClass().add("msg-text");
            label.getStyleClass().add(message.getLevel().getName().toLowerCase());
            return label;
        }

        @Override
        protected void updateItem(ObservableList<MessageExt> item, boolean empty) {
            super.updateItem(item, empty);
            if (empty || item == null) {
                setText(null);
                setGraphic(null);
                return;
            }
            if (item.isEmpty()) {
                setText("");
                setGraphic(null);
                return;
            }
            try {
                HBox box = new HBox();
                box.getStyleClass().add("message-column-ctx");

                // 使用绑定方式监听列表变化，避免重复创建节点
                List<Label> labels = item.stream().map(this::message2Label).toList();
                Platform.runLater(() -> box.getChildren().setAll(labels));

                // 监听消息列表变化，动态更新 UI
                item.addListener((ListChangeListener<? super MessageExt>) change -> {
                    List<MessageExt> messages = new ArrayList<>(change.getList());
                    List<Label> list = messages.stream().map(this::message2Label).toList();
                    Platform.runLater(() -> box.getChildren().setAll(list));
                });
                setGraphic(box);
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("{} @ {}", e.getMessage(), getTableRow().getItem().getCode(), e);
                }
            }
        }
    }

    ContractVerifyComm comm = new ContractVerifyComm();

    @Autowired
    private ProjectSaleTypeService saleTypeService;
    @Autowired
    private VendorGroupService vendorGroupService;
    @Autowired
    private ContractService contractService;
    @Autowired
    private EmployeeService employeeService;

    @FXML
    public DatePicker setupDateBeginSelector;
    @FXML
    public DatePicker setupDateEndSelector;
    // 企业验证开关
    @FXML
    public CheckMenuItem verifyCompanyStatusChecker;
    @FXML
    public CheckMenuItem verifyCompanyPathChecker;
    @FXML
    public CheckMenuItem verifyCompanyCreditChecker;
    // 供应商验证开关
    @FXML
    public CheckMenuItem verifyVendorChecker;
    @FXML
    public CheckMenuItem verifyVendorFileChecker;
    // 客户验证开关
    @FXML
    public CheckMenuItem verifyCustomerChecker;
    @FXML
    public CheckMenuItem verifyCustomerFileChecker;
    @FXML
    public CheckMenuItem verifyCustomerSubContractDateChecker;

    @FXML
    public CheckMenuItem onlyShowVerifiedChecker;

    @FXML
    public TableView<Model> viewTable;
    private final ObservableList<Model> viewTableDataSet = FXCollections.observableArrayList();
    @FXML
    public TableColumn<Model, String> viewTable_codeColumn;
    @FXML
    public TableColumn<Model, String> viewTable_nameColumn;
    @FXML
    public TableColumn<Model, Employee> viewTable_employeeColumn;
    @FXML
    public TableColumn<Model, LocalDate> viewTable_setupDateColumn;
    @FXML
    public TableColumn<Model, ObservableList<MessageExt>> viewTable_stateColumn;

    @Override
    public void show(Stage stage) {
        super.show(stage);
        getTitle().set("合同合规验证");
    }

    public void onShown(WindowEvent windowEvent) {
        super.onShown(windowEvent);
        comm.setContractService(contractService);
        viewTable.getScene().getStylesheets().add("/ui/contract/contract-verify.css");

        LocalDate now = LocalDate.now();
        setupDateBeginSelector.setValue(LocalDate.of(2024, 1, 1));
        setupDateEndSelector.setValue(now);

        viewTable_codeColumn.setCellValueFactory(param -> param.getValue().getCode());
        viewTable_nameColumn.setCellValueFactory(param -> param.getValue().getName());
        viewTable_employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
        viewTable_employeeColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
        viewTable_setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
        // viewTable_stateColumn.setCellValueFactory(param ->
        // param.getValue().getMessages().map(messages -> {
        // return
        // messages.stream().map(Message::getMessage).collect(Collectors.joining(", "));
        // }));
        viewTable_stateColumn.setCellValueFactory(param -> param.getValue().getMessages());
        viewTable_stateColumn.setCellFactory(param -> new StateTableCell());

        viewTable.setItems(viewTableDataSet);

        comm.getVerifyCompanyPath().bind(verifyCompanyPathChecker.selectedProperty());
        comm.getVerifyCompanyStatus().bind(verifyCompanyStatusChecker.selectedProperty());
        comm.getVerifyCompanyCredit().bind(verifyCompanyCreditChecker.selectedProperty());
        comm.getVerifyVendor().bind(verifyVendorChecker.selectedProperty());
        comm.getVerifyVendorFiles().bind(verifyVendorFileChecker.selectedProperty());
        comm.getVerifyCustomer().bind(verifyCustomerChecker.selectedProperty());
        comm.getVerifyCustomerFiles().bind(verifyCustomerFileChecker.selectedProperty());
        comm.getVerifyCustomerSubContractDate().bind(verifyCustomerSubContractDateChecker.selectedProperty());

        bindDoubleClicked(viewTable, this::showContract);
        bindEnterPressed(viewTable, this::reVerifyContract);

    }

    /**
     * 合同验证
     */
    public void onVerifyAction(ActionEvent event) {
        Button btn = (Button) event.getSource();
        CompletableFuture<Void> future = runAsync(() -> {
            viewTableDataSet.clear();
            Pageable pageRequest = PageRequest.ofSize(200);
            AtomicInteger counter = new AtomicInteger(0);
            Map<String, Object> params = new HashMap<>();
            params.put("setupDate",
                    ParamUtils.between(setupDateBeginSelector.getValue(), setupDateEndSelector.getValue()));

            long total = contractService.count(params);
            setStatus("合同：" + total + " 条");

            while (true) {
                if (isCloseRequested()) {
                    break;
                }

                Page<Contract> page = contractService.findAll(params, pageRequest);
                for (Contract contract : page) {
                    if (isCloseRequested()) {
                        break;
                    }
                    counter.incrementAndGet();
                    Model model = new Model();
                    viewTableDataSet.add(model);
                    Employee handler = contract.getHandler();
                    if (handler == null) {
                        model.getEmployee().set(contract.getEmployee());
                    } else {
                        model.getEmployee().set(handler);
                    }

                    model.getCode().set(contract.getCode());
                    model.getName().set(contract.getName());
                    model.getSetupDate().set(contract.getSetupDate());
                    comm.verify(contract, model);
                    setStatus("合同验证进度：" + counter.get() + " / " + total);
                    // 移除中间消息
                    if (!model.getMessages().isEmpty()) {
                        model.getMessages().removeIf(msg -> msg.getLevel().intValue() <= Level.INFO.intValue());
                    }
                    if (model.getMessages().isEmpty()) {
                        if (onlyShowVerifiedChecker.isSelected()) {
                            viewTableDataSet.remove(model);
                        }
                    }
                }
                if (!page.hasNext()) {
                    break;
                }
                pageRequest = page.nextPageable();
            }
            setStatus("合同验证完成");
        }).exceptionally(e -> {
            setStatus(e.getMessage());
            logger.error(e.getMessage(), e);
            return null;
        });
    }

    public void onContractReVerifyAction(ActionEvent event) {
        Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
        if (selectedItem == null) {
            return;
        }
        reVerifyContract(selectedItem);
    }

    private void reVerifyContract(Model model) {
        String contractCode = model.getCode().get();
        if (!StringUtils.hasText(contractCode)) {
            return;
        }
        runAsync(() -> {
            Contract contract = contractService.findByCode(contractCode);
            if (contract == null) {
                return;
            }
            model.getMessages().clear();
            try {
                comm.verify(contract, model);
                // 移除中间消息
                if (!model.getMessages().isEmpty()) {
                    model.getMessages().removeIf(msg -> msg.getLevel().intValue() <= Level.INFO.intValue());
                }
            } catch (Exception e) {
                logger.error(model.getCode().get(), e);
                model.error(e.getMessage());
            }

            if (model.getMessages().isEmpty()) {
                Platform.runLater(() -> {
                    viewTableDataSet.remove(model);
                });
            }
        });
    }

    public void onShowContractDetailAction(ActionEvent event) {
        Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
        if (selectedItem == null) {
            return;
        }
        showContract(selectedItem);
    }

    private void showContract(Model model) {
        String contractCode = model.getCode().get();
        if (!StringUtils.hasText(contractCode)) {
            return;
        }
        Contract contract = contractService.findByCode(contractCode);
        if (contract == null) {
            return;
        }
        ContractWindowController.show(contract, viewTable.getScene().getWindow());
    }

    public void onExportVerifyResultAsFileAction(ActionEvent e) {
        FileChooser chooser = new FileChooser();
        chooser.setTitle("导出核验结果");
        chooser.setInitialFileName("核验结果.xlsx");
        File selected = chooser.showSaveDialog(viewTable.getScene().getWindow());
        if (selected != null) {
            ContractVerifyResultExportAsExcelFileTasker task = new ContractVerifyResultExportAsExcelFileTasker();
            task.setDestFile(selected);
            task.setModels(new ArrayList<>(viewTableDataSet));
            UITools.showTaskDialogAndWait("导出中...", task, null);
        }
    }

}
