package com.ecep.contract.controller;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.RefreshableSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.QueryService;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.BaseViewModel;
import com.ecep.contract.vm.IdentityViewModel;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.stage.WindowEvent;
import lombok.Getter;
import lombok.Setter;

public abstract class AbstEntityController<T extends IdentityEntity, TV extends IdentityViewModel<T>>
        extends BaseController {

    /**
     * 保存按钮
     */
    public Button saveBtn;

    @Getter
    @Setter
    protected TV viewModel;

    @Getter
    @Setter
    protected CompletableFuture<T> loadedFuture;
    private final ObservableList<TabSkin> tabSkins = FXCollections.observableArrayList();

    @Override
    public void onShown(WindowEvent windowEvent) {
        super.onShown(windowEvent);
        ViewModelService<T, TV> service = getViewModelService();

        if (service instanceof QueryService<T, TV> queryService) {
            setStatus("读取...");
            loadedFuture = queryService.asyncFindById(viewModel.getId().get());
            loadedFuture.thenAccept(entity -> {
                // fixed, bind change if new view model create
                if (viewModel != null) {
                    viewModel.bindListener();
                }
                setStatus();
                // BaseViewModel.updateInFxApplicationThread(entity, viewModel);
            });
            loadedFuture.exceptionally(ex -> {
                handleException("载入失败,#" + viewModel.getId().get(), ex);
                return null;
            });
        } else {
            loadedFuture = CompletableFuture.supplyAsync(() -> {
                T entity = getViewModelService().findById(viewModel.getId().get());
                if (entity == null) {
                    return null;
                }
                Platform.runLater(() -> {
                    setStatus();
                    viewModel.update(entity);
                });
                viewModel.bindListener();
                return entity;
            });
        }

        registerTabSkins();
        if (saveBtn != null) {
            saveBtn.disableProperty().bind(createTabSkinChangedBindings().not());
            saveBtn.setOnAction(event -> saveTabSkins());
        }

        installTabSkins();
    }

    public T getEntity() {
        return getLoadedFuture().join();
    }

    protected void setEntity(T entity) {
        loadedFuture = CompletableFuture.completedFuture(entity);
        BaseViewModel.updateInFxApplicationThread(entity, viewModel);
    }

    protected T saveEntity(T entity) {
        return getViewModelService().save(entity);
    }

    public T save(T entity) {
        T saved = saveEntity(entity);
        setEntity(saved);
        return saved;
    }

    protected void registerTabSkins() {
    }

    protected <K extends AbstEntityBasedTabSkin<?, ?, ?>> K registerTabSkin(Tab tab, Function<Tab, K> func) {
        K f = func.apply(tab);
        if (f != null) {
            tabSkins.add(f);
        }
        return f;
    }

    protected void installTabSkins() {
        for (TabSkin tabSkin : tabSkins) {
            try {
                tabSkin.install();
            } catch (Exception e) {
                throw new RuntimeException(tabSkin + ".install", e);
            }
        }
    }

    public void saveTabSkins() {
        for (TabSkin tabSkin : tabSkins) {
            try {
                tabSkin.save();
            } catch (Exception e) {
                UITools.showExceptionAndWait("Tab" + tabSkin.getTab().getText() + " 保存失败,请检查", e);
            }
        }
    }

    public TabSkin getTabSkin(Tab tab) {
        for (TabSkin tabSkin : tabSkins) {
            if (tabSkin.getTab() == tab) {
                return tabSkin;
            }
        }
        return null;
    }

    public BooleanBinding createTabSkinChangedBindings() {
        return Bindings.createBooleanBinding(() -> {
            for (TabSkin tabSkin : tabSkins) {
                if (tabSkin.changeProperty().get()) {
                    return true;
                }
            }
            return false;
        }, tabSkins.stream().map(TabSkin::changeProperty).toArray(BooleanProperty[]::new));
    }

    @SuppressWarnings("unchecked")
    public <C> C getTabSkin(Class<C> skinClass) {
        for (TabSkin tabSkin : tabSkins) {
            if (tabSkin.getClass().isAssignableFrom(skinClass)) {
                return (C) tabSkin;
            }
        }
        return null;
    }

    public void onHidden(WindowEvent windowEvent) {
        if (viewModel != null) {
            try {
                viewModel.unBindListener();
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        uninstallTabSkins();
        super.onHidden(windowEvent);
    }

    protected void uninstallTabSkins() {
        tabSkins.forEach(TabSkin::dispose);
    }

    /**
     * 刷新
     *
     * @param button 按钮
     */
    public void refreshByButton(Button button) {
        CompletableFuture.runAsync(() -> {
            button.setDisable(true);
            refresh().whenComplete((v, ex) -> {
                button.setDisable(false);
                if (ex != null) {
                    setStatus(ex.getMessage());
                }
            });
        });
    }

    public CompletableFuture<Void> refresh() {
        CompletableFuture<Void> future = new CompletableFuture<>();
        ViewModelService<T, TV> service = getViewModelService();
        if (service instanceof QueryService<T, TV> queryService) {
            loadedFuture = queryService.asyncFindById(viewModel.getId().get());
            loadedFuture.whenComplete((entity, ex) -> {
                if (ex != null) {
                    future.completeExceptionally(ex);
                    return;
                }
                BaseViewModel.updateInFxApplicationThread(entity, viewModel);
            });
        } else {
            T entity = service.findById(viewModel.getId().get());
            setEntity(entity);
        }

        List<RefreshableSkin> list = tabSkins.stream()
                .filter(v -> v instanceof RefreshableSkin)
                .map(v -> ((RefreshableSkin) v)).toList();

        if (list.isEmpty()) {
            future.complete(null);
            return future;
        }

        return CompletableFuture.allOf(list
                .stream()
                .map(RefreshableSkin::refresh)
                .filter(Objects::nonNull)
                .toArray(CompletableFuture<?>[]::new))
                .whenComplete((v, ex) -> {
                    if (ex != null) {
                        future.completeExceptionally(ex);
                    } else {
                        future.complete(null);
                    }
                });
    }

    public abstract ViewModelService<T, TV> getViewModelService();
}
