package com.ecep.contract.controller.tab;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.FxmlUtils;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.IdentityViewModel;

import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Tab;
import lombok.Getter;
import lombok.Setter;

public abstract class AbstEntityBasedTabSkin<C extends AbstEntityController<T, V>, T extends IdentityEntity, V extends IdentityViewModel<T>>
        extends AbstGenericTabSkin<C> {
    private static final Logger logger = LoggerFactory.getLogger(AbstEntityBasedTabSkin.class);

    @Setter
    @Getter
    protected V viewModel;

    public AbstEntityBasedTabSkin(C controller) {
        super(controller);
        viewModel = controller.getViewModel();
    }

    public abstract Tab getTab();

    protected CompletableFuture<T> getFuture() {
        return controller.getLoadedFuture();
    }

    protected void setFuture(CompletableFuture<T> future) {
        controller.setLoadedFuture(future);
    }

    protected T getEntity() {
        CompletableFuture<T> future = getFuture();
        if (future == null) {
            return null;
        }
        return future.join();
    }

    protected void setEntity(T entity) {
        setFuture(CompletableFuture.completedFuture(entity));
        viewModel.update(entity);
    }

    protected <ENTITY extends IdentityEntity, MODEL extends IdentityViewModel<ENTITY>, Controller extends AbstEntityController<ENTITY, MODEL>> void showInOwner(Class<Controller> clz, MODEL model) {
        BaseController.show(clz, model, getTab().getTabPane().getScene().getWindow());
    }

    /**
     * 覆盖 install 方法， 用于初始化表格组件
     */
    @Override
    public void install() {
        FxmlPath annotation = getClass().getAnnotation(FxmlPath.class);
        if (annotation == null) {
            // 如果没有 annotation 注解，直接调用
            initializeUIComponents();
            return;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("load fxml: {}", annotation.value());
        }
        FxmlUtils.newLoaderAsyncWithRunLater(
                annotation.value(),
                loader -> loader.setControllerFactory(c -> this),
                loader -> {
                    Tab tab = getTab();
                    Node root = loader.getRoot();
                    tab.setContent(root);
                    if (logger.isDebugEnabled()) {
                        logger.debug("set {} to {}'s content", root, tab);
                    }
                    try {
                        initializeUIComponents();
                    } catch (Exception e) {
                        logger.error("initialize UI Components", e);
                    }
                }).whenComplete((v, ex) -> {
            if (ex != null) {
                handleException(ex);
            }
        });
    }

    public void initializeUIComponents() {
        // 第一个页面，立即显示
        Platform.runLater(this::initializeTab);
    }

    /**
     * 监听 tab 选择, 当 tab 被选中时，调用 onTabShown 方法
     */
    protected void listenTabSelectionChanged() {
        Tab tab = getTab();
        tab.setOnSelectionChanged(event -> {
            if (tab.isSelected()) {
                onTabShown();
            }
        });
    }

    public void onSaveAction(ActionEvent event) {
        try {
            save();
        } catch (Exception e) {
            UITools.showExceptionAndWait("保存失败,请检查", e);
        }
    }

    @SuppressWarnings("unchecked")
    protected T createNewEntity() {
        Class<?> viewModelClz = viewModel.getClass();
        Method method = BeanUtils.findMethodWithMinimalParameters(viewModelClz, "copyTo");
        if (method == null) {
            throw new RuntimeException(viewModelClz.getName() + " unable find copyTo method!");
        }
        Class<?>[] types = method.getParameterTypes();
        if (types.length == 0) {
            throw new RuntimeException(viewModelClz.getName() + " copyTo method need a parameter!");
        }
        return (T) BeanUtils.instantiateClass(types[0]);
    }

    @Override
    public void save() {
        T entity = getFuture().join();
        if (entity == null) {
            entity = createNewEntity();
        }
        if (viewModel.copyTo(entity)) {
            save(entity);
            return;
        }
        if (isChanged()) {
            setStatus("没有修改");
            setChanged(false);
        }
    }

    protected T save(T entity) {
        return controller.save(entity);
    }

    public boolean isChanged() {
        return changeProperty().get();
    }

    public void setChanged(boolean changed) {
        changeProperty().set(changed);
    }

    public BooleanProperty changeProperty() {
        return viewModel.getChanged();
    }

    public void dispose() {
        if (viewModel != null) {
            try {
                viewModel.unBindListener();
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
