package com.ecep.contract;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import com.ecep.contract.controller.BaseController;
import com.ecep.contract.controller.HomeWindowController;
import com.ecep.contract.controller.OkHttpLoginController;
import com.ecep.contract.task.TaskMonitorCenter;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.util.TextMessageHolder;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CurrentEmployee;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import lombok.Getter;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;

/**
 * JavaFx 应用程序
 *
 * @author ecep
 *         Created by ecep on 2017/05/08.
 */
public class Desktop extends Application {
    public static final Logger logger = LoggerFactory.getLogger(Desktop.class);
    public static Desktop instance;

    public static void shutdown() {
        if (logger.isDebugEnabled()) {
            logger.debug("shutdown");
        }
        if (instance != null) {
            try {
                instance.stop();
            } catch (Throwable e) {
                logger.error("shutdown error", e);
            }
        }
    }

    private ScheduledExecutorService scheduledExecutorService = null;
    private final TaskMonitorCenter taskMonitorCenter = new TaskMonitorCenter();

    private final SimpleStringProperty sessionId = new SimpleStringProperty("");
    @Getter
    private final CurrentEmployee activeEmployee = new CurrentEmployee();
    @Getter
    private OkHttpClient httpClient;

    public void setActiveEmployeeId(int activeEmployeeId) {
        activeEmployee.getId().set(activeEmployeeId);
    }

    public int getActiveEmployeeId() {
        return activeEmployee.getId().get();
    }

    public String getSessionId() {
        return sessionId.get();
    }

    public void setSessionId(String sessionId) {
        this.sessionId.set(sessionId);
    }

    public ScheduledExecutorService getExecutorService() {
        if (scheduledExecutorService == null) {
            scheduledExecutorService = Executors.newScheduledThreadPool(3);
        }
        return scheduledExecutorService;
    }

    public TaskMonitorCenter getTaskMonitorCenter() {
        return taskMonitorCenter;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("start");
        }
        if (instance != null) {
            logger.error("Desktop already started");
        }
        instance = this;

        URL resource = getClass().getResource("/ui/start_lamp.fxml");
        FXMLLoader loader = new FXMLLoader(resource);
        primaryStage.setTitle("CMS");
        primaryStage.initStyle(StageStyle.TRANSPARENT);

        Parent root = loader.load();
        Scene scene = new Scene(root);
        scene.getStylesheets().add("/ui/start_lamp.css");

        primaryStage.setScene(scene);
        primaryStage.setOnShown(e -> {
            System.out.println("primaryStage#OnShown");
        });
        primaryStage.show();
        System.out.println("Desktop.start -> primaryStage.show()");

        try {
            startSpringApp(primaryStage, root, loader);
        } catch (Exception e) {
            UITools.showExceptionAndWait("启动失败", e);
        }
    }

    public CompletableFuture<Void> runAsync(Runnable runnable) {
        return CompletableFuture.runAsync(runnable, getExecutorService());
    }

    private void startSpringApp(Stage primaryStage, Parent root, FXMLLoader loader) {
        logger.debug("startSpringApp");
        // 更新窗口标题
        Node titleNode = root.lookup("#title");
        if (titleNode != null) {
            primaryStage.setTitle(((Text) titleNode).getText());
        }

        Node lookup = root.lookup("#logBox");
        if (!(lookup instanceof VBox logBox)) {
            throw new RuntimeException("启动界面加载失败, #logger 类型错误");
        }

        ScrollPane logPane = (ScrollPane) root.lookup("#logPane");

        logBox.getChildren().clear();
        TextMessageHolder holder = new TextMessageHolder() {
            @Override
            public void addTextMessage(Text text) {
                Platform.runLater(() -> {
                    logBox.getChildren().add(text);
                    logPane.layout();
                    logPane.setVvalue(1.0);
                });
            }
        };
        holder.info("启动中，请稍后...");

        runAsync(() -> {
            try {
                //
                holder.info("读取配置文件...");
                Properties properties = new Properties();
                File configFile = new File("config.properties");
                if (configFile.exists()) {
                    holder.info("读取配置文件 " + configFile.getAbsolutePath() + "...");
                    try (FileInputStream input = new FileInputStream(configFile)) {
                        properties.load(input);
                        holder.info("配置文件读取成功.");
                    } catch (IOException e) {
                        holder.error("读取失败:" + e.getMessage());
                        logger.error(e.getMessage(), e);
                        return;
                    }
                } else {
                    logger.warn("配置文件{}不存在", configFile.getAbsolutePath());
                }
                HibernateProxyUtils.useProxy(false);

                runAsync(() -> {
                    SpringApp.launch(properties, holder);
                    ConfigurableListableBeanFactory beanFactory = SpringApp.context.getBeanFactory();

                    beanFactory.registerSingleton("scheduledExecutorService", getExecutorService());
                    beanFactory.registerSingleton("taskMonitorCenter", taskMonitorCenter);
                });

                try {
                    initHttpClient();

                    OkHttpLoginController controller = new OkHttpLoginController();
                    controller.setHttpClient(this.httpClient);
                    controller.setHolder(holder);
                    controller.setPrimaryStage(primaryStage);
                    controller.setProperties(properties);
                    while (true) {
                        try {
                            controller.tryLogin().get();
                            holder.info("登录成功");

                            try {
                                while (!SpringApp.isRunning()) {
                                    System.out.println("等待启动");
                                    Thread.sleep(1000);
                                }
                            } catch (InterruptedException ex) {
                                throw new RuntimeException(ex);
                            }

                            // 必须要等待启动成功后才能关闭主场景，否则进程结束程序退出
                            HomeWindowController.show().thenRun(() -> Platform.runLater(primaryStage::close));

                            break;
                        } catch (Exception ex) {
                            holder.error(ex.getMessage());
                            Thread.sleep(3000);
                        }
                    }
                } catch (Exception e) {
                    holder.error("登录失败:" + e.getMessage());
                    logger.error(e.getMessage(), e);
                }
            } catch (Exception e) {
                holder.error(e.getMessage());
                logger.error(e.getMessage(), e);
            }
        });
        System.out.println("Desktop.startSpringApp.");

    }

    private void initHttpClient() {
        this.httpClient = new OkHttpClient().newBuilder().cookieJar(new CookieJar() {
            private final List<Cookie> cookies = new java.util.ArrayList<>();

            @Override
            public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                // 保存服务器返回的Cookie（如JSESSIONID）
                this.cookies.addAll(cookies);
                System.out.println("保存Cookie: " + cookies);
            }

            @Override
            public List<Cookie> loadForRequest(HttpUrl url) {
                // 请求时自动携带Cookie
                return cookies;
            }

        }).build();
    }

    @Override
    public void stop() throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("stop");
        }
        CompletableFuture<Void> future = BaseController.shutdown();
        future.orTimeout(5, TimeUnit.SECONDS);
        future.exceptionally(e -> {
            logger.error(e.getMessage(), e);
            return null;
        });
        future.thenRun(() -> {
            SpringApp.shutdown();
            try {
                shutdownExecutorService();
                super.stop();
                if (logger.isDebugEnabled()) {
                    logger.debug("stopped");
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        });
    }

    private void shutdownExecutorService() {
        List<Runnable> runnableList = scheduledExecutorService.shutdownNow();
        for (Runnable runnable : runnableList) {
            if (logger.isDebugEnabled()) {
                logger.debug("shutdown runnable = {}", runnable);
            }
            if (runnable instanceof FutureTask<?> future) {
                if (logger.isDebugEnabled()) {
                    logger.debug("runnable as future, isCancelled() = {}, isDone() = {}", future.isCancelled(),
                            future.isDone());
                }
                if (future.cancel(true)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("runnable as future canceled");
                    }
                }
            }
        }
        scheduledExecutorService.close();
    }
}