package top.octopusyan.controller;

import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import top.octopusyan.base.BaseController;
import top.octopusyan.config.ProxyConfig;
import top.octopusyan.config.ProxyConfig.ProxyServer;
import top.octopusyan.config.ProxyConfig.ProxyType;
import top.octopusyan.config.TextValidate;
import top.octopusyan.http.request.ProxySetup;
import top.octopusyan.manager.FrpManager;
import top.octopusyan.manager.ProxyManager;
import top.octopusyan.manager.http.listener.OnHttpListener;
import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.model.ProxySetupModel;
import top.octopusyan.utils.AlertUtil;
import top.octopusyan.utils.DomainUtil;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.IOException;
import java.util.List;
import java.util.*;

import static top.octopusyan.model.ApplicatonStore.*;

/**
 * @author : octopus yan
 * @email : octopus_yan@foxmail.com
 * @description :
 * @create : 2022-4-4 22:32
 */
public class MainController extends BaseController<StackPane> implements Initializable {

    public static final String PROXY_LIST_ITEM_CLASS = "proxyListItem";
    public static final String PROXY_LIST_ITEM_STOP_CLASS = "proxyListItem-stop";
    public static final String PROXY_LIST_ITEM_RUN_CLASS = "proxyListItem-run";
    public static final String PROXY_LIST_ITEM_CLOSE_CLASS = "proxyListItem-close";
    public static final String PROXY_LIST_ITEM_SELECT_CLASS = "proxyListItem-select";
    public static final String INPUT_CLASS = "inputText";

    @FXML
    public StackPane root;

    @FXML
    public JFXButton closeBtn, minimizeBtn;

    @FXML
    public JFXButton startProxyBtn, addProxyBtn;
    @FXML
    public JFXComboBox<String> proxyProtocolComboBox;
    @FXML
    public JFXButton customizeDomainBtn;
    @FXML
    public JFXRadioButton openProxyRBtn, closeProxyRBtn;
    @FXML
    public JFXTextField domainTextField, domainSuffixTextField;
    @FXML
    public JFXTextField proxyNameTextField, localHostTextField, localPortTextField;
    @FXML
    public JFXComboBox<String> proxyServerComboBox;
    @FXML
    public JFXTabPane tabPane;
    @FXML
    public JFXListView<Label> proxyListView;
    @FXML
    public Label domainHtinTextField;
    @FXML
    public HBox domainPane;
    @FXML
    public JFXButton resetProxyBtn, clearLogBtn, copyDomainBtn;
    @FXML
    public Tab proxyLogPane;
    @FXML
    public Hyperlink domainLink;
    @FXML
    public HBox proxyStatusPane;
    @FXML
    public JFXButton logoutBtn;

    private ToggleGroup openProxyGroup = new ToggleGroup();
    private SimpleBooleanProperty customizeDomain = new SimpleBooleanProperty(false);
    private Map<String, FrpManager> frpUtilMap = new HashMap<>();
    private ProxySetupModel proxySetupModel;
    private ProxySetup proxySetup;
    private SimpleBooleanProperty setup = new SimpleBooleanProperty(false);

    @Override
    public boolean dragWindow() {
        return true;
    }

    @Override
    public StackPane getRootPanel() {
        return root;
    }

    @NotNull
    @Override
    public String getRootFxmlPath() {
        return "/fxml/main.fxml";
    }

    @Override
    public Button getClooseBtn() {
        return closeBtn;
    }

    @Override
    public Button getMinimizeBtn() {
        return minimizeBtn;
    }

    @Override
    public JFXButton getFirstBtn() {
        return minimizeBtn;
    }

    @Override
    public void initData() {
        // 初始化视图模型
        proxySetup = ProxyManager.initProxy(null);
        proxySetupModel = new ProxySetupModel(proxySetup);
        // 初始化用户隧道列表
        setProxyList(Collections.singletonList(proxySetup));
        // 重置隧道列表视图
        initProxyListView();

        // 隧道类型
        Arrays.asList(ProxyType.values()).forEach((type) -> proxyProtocolComboBox.getItems().add(type.name()));

        // 服务器
        Arrays.asList(ProxyServer.values()).forEach((server) -> proxyServerComboBox.getItems().add(server.getServerName()));

        // 获取用户隧道列表
        ProxyManager.getList(new OnHttpListener<List<ProxySetup>>() {
            @Override
            public void onSucceed(List<ProxySetup> result) {

                Platform.runLater(() -> {

                    // 如果用户隧道列表不为空
                    if (result != null && result.size() > 0) {
                        proxySetup = result.get(selectProxy());
                        proxySetupModel.set(result.get(selectProxy()));
                        // 初始化用户隧道列表
                        setProxyList(result);
                        // 重置隧道列表视图
                        initProxyListView();
                    }
//                    else {
//                    }
//
//                    bindDataView();
                });
            }

            @Override
            public void onFail(Exception e) {

            }
        });


        /* 数据绑定 */

        // 服务器
        proxyServerComboBox.valueProperty().bindBidirectional(proxySetupModel.serverProperty());
        // 隧道名称
        proxyNameTextField.textProperty().bindBidirectional(proxySetupModel.proxyNameProperty());
        // 隧道类型
        proxyProtocolComboBox.valueProperty().bindBidirectional(proxySetupModel.proxyTypeProperty());
        // 内网地址
        localHostTextField.textProperty().bindBidirectional(proxySetupModel.localIpProperty());
        // 内网端口
        localPortTextField.textProperty().bindBidirectional(proxySetupModel.localPortProperty());
        // 外网访问子域名
        domainTextField.textProperty().bindBidirectional(proxySetupModel.domainProperty());
        // 外网访问主域名
        domainSuffixTextField.textProperty().bindBidirectional(proxySetupModel.domainSuffixProperty());
        // 是否自定义访问域名
        customizeDomain.bindBidirectional(proxySetupModel.isCustomizeProperty());
        // 启动成功提示
        proxyStatusPane.visibleProperty().bindBidirectional(proxySetupModel.runningProperty());
    }

    @Override
    public void initViewStyle() {
        // 设置列表
        proxyListView.getSelectionModel().select(0);
        // 启用链接
        openProxyRBtn.setToggleGroup(openProxyGroup);
        closeProxyRBtn.setToggleGroup(openProxyGroup);
        openProxyRBtn.setUserData(true);
        closeProxyRBtn.setUserData(false);
        openProxyRBtn.setSelected(true);

        // 是否启用
        proxySetupModel.statusProperty().addListener((observable, oldValue, newValue) -> {
            openProxyRBtn.setSelected(newValue);
            closeProxyRBtn.setSelected(!newValue);
            closeProxy(!newValue);
        });
        proxySetupModel.proxyTypeProperty().addListener((observable, oldValue, newValue) -> {
            String newType = StringUtils.lowerCase(newValue);
            String port, host;
            if (!newType.equals(proxySetup.getProxy_type())) {
                host = "127.0.0.1";
                port = ProxyConfig.getTypePort(newType).toString();
            } else {
                host = proxySetupModel.getLocalIp();
                port = proxySetupModel.getLocalPort();
            }
            // 域名提示
            domainPane.setVisible(DomainUtil.isHttp(proxySetupModel));

            // 设置
            localHostTextField.setText(host);
            localPortTextField.setText(port);
        });

        // 隧道名称
        proxySetupModel.proxyNameProperty().addListener((observable, oldValue, newValue) -> {
            if (proxyListView.getItems().size() > 0)
                proxyListView.getItems().get(selectProxy()).setText(newValue);
        });

        // 运行状态监听
        proxySetupModel.runningProperty().addListener((observable, oldValue, newValue) -> {
            String startClass = "startProxyBtn";
            String stopClass = "stopProxyBtn";
            // 启动按钮
            startProxyBtn.getStyleClass().remove(startClass);
            startProxyBtn.getStyleClass().remove(stopClass);
            startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass);
            startProxyBtn.setText(newValue ? "停止" : "启动");
            // 列表显示
            ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
            styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS);
            styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
            styleClass.add(newValue ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);

            setDomainLink();
        });

        // 自定义外网访问地址按钮
        customizeDomain.addListener((observable, oldValue, newValue) -> {
            AnchorPane parent = (AnchorPane) domainTextField.getParent();
            // 是否切换为自定义域名
            if (newValue) {
                // 隐藏系统域名
                if (parent.getChildren().contains(domainSuffixTextField))
                    parent.getChildren().remove(domainSuffixTextField);
                // 用户域名是否自定义
                if (!DomainUtil.isCustomize(proxySetupModel.get())) {
                    proxySetupModel.setDomain("");
                } else {
                    proxySetupModel.setDomain(proxySetupModel.getDomain());
                }
                proxySetupModel.setDomainSuffix("");
                domainTextField.promptTextProperty().set("自定义域名");
                customizeDomainBtn.setText("系统分配");
                domainHtinTextField.setText("请输入您的域名，并解析至: " + ProxyConfig.getServerIP(proxySetupModel.getServer()));
            } else {
                // 显示系统域名
                if (!parent.getChildren().contains(domainSuffixTextField))
                    parent.getChildren().add(domainSuffixTextField);
                // 用户域名是否自定义
                if (DomainUtil.isCustomize(proxySetupModel.get())) {
                    proxySetupModel.setDomain("");
                    proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(proxySetupModel.getServer()));
                } else {
                    proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetupModel.get()));
                    proxySetupModel.setDomainSuffix(DomainUtil.getSuffix(proxySetup));
                }
                domainTextField.promptTextProperty().set("自定义子域名 大于3位");
                customizeDomainBtn.setText("自定义");
                domainHtinTextField.setText("请输入子域名，长度不小于3个字符");
            }
        });

        // 默认展示 "常见问题" 面板
        tabPane.getSelectionModel().select(1);
    }

    /** 设置访问链接 */
    private void setDomainLink() {
        // 外网访问连接
        if (DomainUtil.isHttp(proxySetupModel)) {
            // http / https
            String prefix = proxySetupModel.getProxyType() + "://";
            domainLink.textProperty().set(prefix + proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix());
        } else {
            // ssh / tcp
            domainLink.textProperty().set(ProxyConfig.getServerIP(ProxyConfig.getServerNode(proxySetupModel.getServer())) + ":" + proxySetupModel.getRemotePort());
        }
    }

    @Override
    public void initViewAction() {
        // 重置
        resetProxyBtn.setOnMouseClicked(event -> {
            proxySetup.setRuning(false);
            proxySetupModel.set(proxySetup);
            domainTextField.resetValidation();
        });

        // 日志清理
        clearLogBtn.setOnMouseClicked(event -> {
            if (frpUtilMap.get(proxySetupModel.getProxyName()) != null)
                frpUtilMap.get(proxySetupModel.getProxyName()).clearLog();
        });

        // 更换服务器地址
        proxyServerComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
            // 重新设置是否自定义
            proxySetupModel.set(proxySetupModel.get());
            if (!customizeDomain.get()) {
                proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(newValue));
            } else {
                proxySetupModel.setDomainSuffix("");
                // 域名检查
                domainTextField.validate();
            }
        });

        // 是否启用
        openProxyGroup.selectedToggleProperty().addListener(
                (ObservableValue<? extends Toggle> ov, Toggle old_toggle, Toggle new_toggle) -> {
                    Toggle toggle = openProxyGroup.getSelectedToggle();
                    if (toggle != null) {
                        // 设置状态
                        proxySetupModel.setStatus((Boolean) toggle.getUserData());
                    }
                }
        );

        // 自定义访问域名
        customizeDomainBtn.setOnMouseClicked((event) -> customizeDomain.set(!customizeDomain.get()));

        // 本地IP检查
        localHostTextField.getValidators().add(TextValidate.IpRequired);
        localHostTextField.getValidators().add(TextValidate.IpFormat);
        localHostTextField.textProperty().addListener((observable, oldValue, newValue) -> {
            localHostTextField.validate();
        });

        // 端口检查
        localPortTextField.getValidators().add(TextValidate.PortRequired);
        localPortTextField.getValidators().add(TextValidate.PortFormat);
        localPortTextField.textProperty().addListener((observable, oldValue, newValue) -> {
            localPortTextField.validate();
        });

        // 域名检查
        setup.addListener((observable, oldValue, newValue) -> {
            if (newValue) domainTextField.validate();
        });
        domainTextField.textProperty().addListener((observable, oldValue, newValue) -> {
            if (!domainTextField.getValidators().contains(TextValidate.DomainRequired)) {
                domainTextField.getValidators().add(TextValidate.DomainRequired);// 域名为空检查
                domainTextField.getValidators().add(TextValidate.domainFormatValidator(proxySetupModel));// 域名格式检查
                domainTextField.getValidators().add(TextValidate.domainLengthValidator(proxySetupModel));// 域名长度检查
                domainTextField.getValidators().add(TextValidate.domainAddressValidator(proxySetupModel));// 自定义域名解析检查
            } else
                domainTextField.validate();
        });

        // 点击隧道列表
        proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            ObservableList<Label> items = proxyListView.getItems();

            if (items.size() == 0) return;

            for (Label item : items) {
                item.getStyleClass().remove(PROXY_LIST_ITEM_SELECT_CLASS);
            }
            if (newValue != null) {
                newValue.getStyleClass().add(PROXY_LIST_ITEM_SELECT_CLASS);
                int oldIndex = items.indexOf(oldValue);
                int newIndex = items.indexOf(newValue);
                // 保存隧道状态
                ObservableList<ProxySetup> proxyList = proxyList();
                if (oldIndex != -1 && StringUtils.isNotEmpty(proxyList.get(oldIndex).getProxy_name()))
                    proxyList.set(oldIndex, proxySetupModel.get());
                proxySetup = proxyList.get(newIndex);
                selectProxy(newIndex);
                Platform.runLater(() -> {
                    setup.set(false);
                    proxySetupModel.set(proxySetup);
                    FrpManager frpManager = frpUtilMap.get(proxySetup.getProxy_name());
                    proxyLogPane.contentProperty().set(frpManager == null ? null : frpManager.getConsole());
                    setup.set(true);
                    setDomainLink();
                });
            }
        });

        // 添加隧道
        addProxyBtn.setOnMouseClicked(event -> {
            // 获取默认隧道设置加入列表
            ObservableList<ProxySetup> proxyList = proxyList();
            addProxyList(ProxyManager.initProxy(proxyList.get(proxyList.size() - 1).getSort()));
            // 重置隧道列表视图
            initProxyListView();
            proxyListView.getSelectionModel().select(proxyListView.getItems().size() - 1);
        });

        // 启动
        startProxyBtn.setOnMouseClicked(event -> {
            if (proxySetupModel.isRunning())
                stopProxy();
            else
                startProxy();
        });

        // 链接跳转
        domainLink.setOnMouseClicked(event -> {
            getApplication().getHostServices().showDocument(domainLink.getText());
        });

        // 复制外网访问地址
        copyDomainBtn.setOnMouseClicked(event -> {
            //获取系统剪切板
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            //构建String数据类型
            StringSelection selection = new StringSelection(domainLink.getText());
            //添加文本到系统剪切板
            clipboard.setContents(selection, null);
            // 复制成功提示
            AlertUtil.info("复制成功，快去分享给小伙伴吧！").show();
        });

        // 退出登录
        logoutBtn.setOnMouseClicked(event -> {
            for (String key : frpUtilMap.keySet()) {
                frpUtilMap.get(key).stop();
            }
            ApplicatonStore.logout();
            try {
                jumpTo(new LoginController());
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
//
//    private void initEmptyProxyListView() {
//        // 初始化视图模型
//        proxySetup = ProxyManager.initProxy(null);
//        proxySetupModel = new ProxySetupModel(proxySetup);
//        setProxyList(Collections.singletonList(proxySetup));
//        initProxyListView();
//    }

    /**
     * 是否启用当前隧道
     */
    private void closeProxy(boolean close) {
        if (close && proxySetupModel.isRunning()) stopProxy();

        proxyNameTextField.setDisable(close);// 隧道名称
        proxyProtocolComboBox.setDisable(close);// 隧道类型
        localHostTextField.setDisable(close);// 本地地址
        localPortTextField.setDisable(close);// 本地端口
        domainTextField.setDisable(close);   // 外网子域名
        startProxyBtn.setDisable(close);     // 启动按钮

        ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
        styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
        styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS);
        styleClass.remove(PROXY_LIST_ITEM_CLOSE_CLASS);
        if (close) {
            styleClass.add(PROXY_LIST_ITEM_CLOSE_CLASS);
        } else {
            styleClass.add(proxySetupModel.isRunning() ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
        }
    }

    private void initProxyListView() {
        ObservableList<ProxySetup> proxyList = proxyList();
        for (ProxySetup proxy : proxyList.subList(proxyListView.getItems().size(), proxyList.size())) {
            setProxyListView(proxyList.indexOf(proxy), proxy);
        }
        // 设置选中
        proxyListView.getSelectionModel().select(selectProxy());
    }

    private void setProxyListView(int index, ProxySetup setup) {
        ObservableList<Label> items = proxyListView.getItems();
        try {
            Label label = FXMLLoader.load(getClass().getResource("/fxml/proxyItem.fxml"));
            label.textProperty().set(setup.getProxy_name());
            ObservableList<String> styleClass = label.getStyleClass();
            styleClass.addAll(PROXY_LIST_ITEM_CLASS, setup.isRuning() ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
            if (index < items.size()) {
                items.set(index, label);
            } else {
                items.add(label);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        proxyListView.getSelectionModel().select(items.size() - 1);
    }

    /**
     * 启动代理
     */
    private void startProxy() {
        // 验证
        if(!domainTextField.validate() || !localHostTextField.validate() || !localPortTextField.validate())
            return;

        // 非 http 隧道系统随机分配端口
        if (!DomainUtil.isHttp(proxySetupModel)) {
            proxySetupModel.setRemotePort(String.valueOf(ProxyManager.randomPort()));
        }

        // 是用户隧道
        if (proxySetupModel.getId() != null && !proxySetupModel.get().equals(proxySetup)) {
            // 删除旧隧道
            ProxyManager.delete(Integer.parseInt(proxySetupModel.getId()));

            // 添加隧道
            ProxyManager.add(new OnHttpListener<ProxySetup>() {
                @Override
                public void onSucceed(ProxySetup result) {
                    result.setRuning(frpUtilMap.containsKey(result.getProxy_name()));
                    proxySetupModel.set(result);
                }

                @Override
                public void onFail(Exception e) {

                }
            }, proxySetupModel.get());
        } else if(proxySetupModel.getId() == null) {

            // 添加隧道
            ProxyManager.add(new OnHttpListener<ProxySetup>() {
                @Override
                public void onSucceed(ProxySetup result) {
                    result.setRuning(frpUtilMap.containsKey(result.getProxy_name()));
                    proxySetupModel.set(result);
                }

                @Override
                public void onFail(Exception e) {

                }
            }, proxySetupModel.get());
        }

        // 初始化frputil
        FrpManager frpManager;
        if ((frpManager = frpUtilMap.get(proxySetupModel.getProxyName())) == null)
            frpUtilMap.put(proxySetupModel.getProxyName(), frpManager = FrpManager.init(proxySetupModel));

        // 设置文本域对象
        if (proxyLogPane.getContent() == null) proxyLogPane.contentProperty().set(frpManager.getConsole());

        // 开始
        frpManager.start();

        // 显示日志
        tabPane.getSelectionModel().select(0);
    }

    /**
     * 暂停代理
     */
    private void stopProxy() {
        // 关闭CMD
        FrpManager frpManager = frpUtilMap.get(proxySetupModel.getProxyName());
        if (frpManager != null) frpManager.stop();
    }

    @Override
    public void onDestroy() {


        super.onDestroy();
    }
}
