package com.ecep.contract.controller;

import java.io.FileOutputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.ecep.contract.Desktop;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;

import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import lombok.Setter;

public class LoginWidowController implements MessageHolder {
    private static final Logger logger = LoggerFactory.getLogger(LoginWidowController.class);
    @Setter
    MessageHolder holder;
    @Setter
    Stage primaryStage;
    @Setter
    Properties properties;

    @Override
    public void addMessage(Level level, String message) {
        holder.addMessage(level, message);
    }

    private void storeProperties() {
        try (FileOutputStream fos = new FileOutputStream("config.properties")) {
            // 保存到文件
            properties.store(fos, "Updated config.properties");
            info("配置文件已更新！");
        } catch (java.io.IOException e) {
            error("保存配置文件失败:" + e.getMessage());
        }
    }

    String getHost() {
        return properties.getProperty("server.host");
    }

    public void tryLogin() {
        // CompletableFuture<ButtonType> future = new CompletableFuture<>();
        // 检查配置文件中是否保存用户名和密码
        String userName = getUserName();
        if (StringUtils.hasText(userName)) {
            try {
                EmployeeInfo employeeInfo = tryToConnect(userName, getPassword());
                if (employeeInfo.errorCode < 0) {
                    error("登录失败：错误代码=" + employeeInfo.errorCode);
                } else {
                    logined(employeeInfo);
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        } else {
            showUserNameLoginDialog(null);
        }
    }

    private String getPassword() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'getPassword'");
    }

    private String getUserName() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'getUserName'");
    }

    CompletableFuture<List<LoginWidowController.MacIP>> getMacAndIP() {
        return CompletableFuture.supplyAsync(() -> {
            // mac ip
            List<LoginWidowController.MacIP> list = new ArrayList<>();
            try {
                Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
                while (interfaces.hasMoreElements()) {
                    NetworkInterface anInterface = interfaces.nextElement();
                    if (anInterface.isLoopback()) {
                        continue;
                    }
                    byte[] hardwareAddress = anInterface.getHardwareAddress();
                    if (hardwareAddress == null) {
                        continue;
                    }
                    Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses();
                    if (!inetAddresses.hasMoreElements()) {
                        continue;
                    }

                    // -分割16进制表示法
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < hardwareAddress.length; i++) {
                        sb.append(
                                String.format("%02X%s", hardwareAddress[i], i < hardwareAddress.length - 1 ? "-" : ""));
                    }

                    while (inetAddresses.hasMoreElements()) {
                        InetAddress inetAddress = inetAddresses.nextElement();
                        if (inetAddress instanceof Inet4Address) {
                            list.add(new LoginWidowController.MacIP(sb.toString(), inetAddress.getHostAddress()));
                        }
                    }
                }
            } catch (SocketException e) {
                throw new RuntimeException(e);
            }
            return list;
        });
    }

    private EmployeeInfo tryToConnect(String userName, String password) throws SQLException {
        String host = getHost();
        String port = getPort();
        String database = getDatabase();

        if (logger.isDebugEnabled()) {
            logger.debug("try to connect db server host:{},port:{},database:{},user:{},pwd:{}", host, port, database,
                    userName, "*");
        }
        return null;
    }

    private String getDatabase() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'getDatabase'");
    }

    private String getPort() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'getPort'");
    }

    private void createSession(Connection connection, EmployeeInfo employeeInfo) {
        employeeInfo.sessionId = addHistory(connection, employeeInfo.employeeId, employeeInfo.binds.getFirst());
    }

    private int addHistory(Connection connection, int employeeId, MacIP macIP) {
        try {
            String sql = "INSERT INTO EMPLOYEE_LOGIN_HISTORY (IP, MAC, DT, EMPLOYEE_ID) VALUES (?, ?, ?, ?)";
            try (PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
                ps.setString(1, macIP.ip);
                ps.setString(2, macIP.mac);
                ps.setObject(3, LocalDateTime.now()); // 根据数据库字段类型调整
                ps.setInt(4, employeeId);
                ps.executeUpdate();
                // 返回 新的主键值
                ResultSet generatedKeys = ps.getGeneratedKeys();
                if (generatedKeys.next()) {
                    return generatedKeys.getInt(1);
                }
            }
        } catch (SQLException e) {
            holder.error("申请新会话编号失败");
            logger.error("unable insert EMPLOYEE_LOGIN_HISTORY, ", e);
        }

        return -1;
    }

    static class MacIP {
        String ip;
        String mac;

        public MacIP(String mac, String ip) {
            this.mac = mac;
            this.ip = ip;
        }
    }

    static class EmployeeInfo {
        Integer employeeId;
        List<MacIP> binds = new ArrayList<>();
        SimpleStringProperty name = new SimpleStringProperty();
        SimpleBooleanProperty active = new SimpleBooleanProperty();
        int sessionId;
        int errorCode = 0;

        public EmployeeInfo(Integer employeeId) {
            this.employeeId = employeeId;
        }

        public static EmployeeInfo error(int code) {
            EmployeeInfo employeeInfo = new EmployeeInfo(null);
            employeeInfo.errorCode = code;
            return employeeInfo;
        }

        public void addBind(MacIP macIP) {
            binds.add(macIP);
        }
    }

    private CompletableFuture<EmployeeInfo> tryLoginWithEmployeeBind(Connection connection,
            CompletableFuture<List<MacIP>> macAndIP) {
        CompletableFuture<EmployeeInfo> future = new CompletableFuture<>();
        macAndIP.thenAccept(macIPS -> {
            if (macIPS.isEmpty()) {
                future.complete(EmployeeInfo.error(-1));
                return;
            }

            HashMap<Integer, EmployeeInfo> employeeMap = new HashMap<>();
            for (MacIP macIP : macIPS) {
                for (Integer employeeId : findAllBindEmployee(connection, macIP)) {
                    employeeMap.computeIfAbsent(employeeId, k -> new EmployeeInfo(employeeId)).addBind(macIP);
                }
            }

            if (employeeMap.isEmpty()) {
                error("本机未绑定登录信息，请联系管理员更新.");
                // 当前计算机的信息，如用户名，计算机名等
                String username = System.getProperty("user.name");
                String computerName = System.getenv("COMPUTERNAME");
                for (MacIP macIP : macIPS) {
                    if (macIP.ip.equals("127.0.0.1")) {
                        continue;
                    }
                    registerComputer(username, computerName, connection, macIP);
                }
                future.complete(EmployeeInfo.error(-2));
                return;
            }

            if (employeeMap.size() == 1) {
                // 直接登录
                EmployeeInfo employeeInfo = employeeMap.values().stream().findFirst().get();
                // issue #1 登录成功后没有更新员工信息
                fill(connection, employeeInfo);
                future.complete(employeeInfo);
            } else {
                List<EmployeeInfo> list = employeeMap.values().stream().toList();
                // 选择登录
                Platform.runLater(() -> {
                    EmployeeInfo info = showEmployeeSelectDialog(list);
                    future.complete(Objects.requireNonNullElseGet(info, () -> EmployeeInfo.error(-3)));
                });
                for (EmployeeInfo info : list) {
                    fill(connection, info);
                }
            }
        });
        return future;
    }

    private EmployeeInfo showEmployeeSelectDialog(List<EmployeeInfo> list) {
        Stage stage = new Stage();
        stage.initOwner(primaryStage);
        stage.setTitle("请选择账户登录系统");
        stage.setWidth(360);
        stage.setHeight(280);

        Label label = new Label("您的主机关联了以下账户，请选择一个登录");
        label.setPadding(new Insets(10, 0, 10, 10));
        ListView<Label> listView = new ListView<>();

        EventHandler<MouseEvent> eventHandler = event -> {
            if (event.getClickCount() == 2) {
                // listView.getSelectionModel().select(cb);
                stage.close();
            }
        };
        for (EmployeeInfo employeeInfo : list) {
            Label cb = new Label();
            cb.setUserData(employeeInfo);
            cb.textProperty().bind(employeeInfo.name);
            cb.setPadding(new Insets(5));
            cb.setOnMouseClicked(eventHandler);
            listView.getItems().add(cb);
        }

        // 创建 BorderPane 并设置边距
        BorderPane borderPane = new BorderPane();
        borderPane.setPadding(new Insets(10));
        borderPane.setTop(label);
        borderPane.setCenter(listView);

        Button bottom = new Button("确定");
        bottom.setDefaultButton(true);
        bottom.setOnAction(event -> {
            Label selectedItem = listView.getSelectionModel().getSelectedItem();
            if (selectedItem == null) {
                // 没选中，退出继续选择
                return;
            }
            stage.close();
        });
        BorderPane.setAlignment(bottom, javafx.geometry.Pos.CENTER);

        borderPane.setBottom(bottom);
        stage.setScene(new Scene(borderPane));
        stage.setOnCloseRequest(event -> {
            Label selectedItem = listView.getSelectionModel().getSelectedItem();
            if (selectedItem == null) {
                // 关闭时，如何没有做选择，不关闭窗口
                event.consume();
            }
        });

        stage.showAndWait();

        Label selectedItem = listView.getSelectionModel().getSelectedItem();
        if (selectedItem == null) {
            throw new NoSuchElementException("请选择工号登录系统");
        }

        return (EmployeeInfo) selectedItem.getUserData();
    }

    private void fill(Connection connection, EmployeeInfo info) {
        try {
            ResultSet rs = connection.createStatement()
                    .executeQuery("SELECT * FROM EMPLOYEE where ID = " + info.employeeId);
            if (rs.next()) {
                String name = rs.getString("NAME");
                boolean isActive = rs.getBoolean("IS_ACTIVE");
                Platform.runLater(() -> {
                    info.name.set(name);
                    info.active.set(isActive);
                });
            }
        } catch (SQLException e) {
            logger.error("查询{}失败", info.employeeId, e);
        }
    }

    private void registerComputer(String username, String computerName, Connection connection, MacIP macIP) {
        info("正在注册本机信息(MAC:" + macIP.mac + ", IP:" + macIP.ip + ")...");
        String sql = "INSERT INTO EMPLOYEE_AUTH_BIND (IP,MAC,DESCRIPTION)VALUES(?,?,?)";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, macIP.ip);
            stmt.setString(2, macIP.mac);
            // 当前计算机的信息，如用户名，计算机名等
            stmt.setString(3, username + "," + computerName);
            if (stmt.execute()) {
                info("注册成功");
            }
        } catch (SQLException e) {
            error(String.format("注册失败，请联系管理员或重启应用！MAC: %s, IP: %s", macIP.mac, macIP.ip));
            logger.error("注册失败 mac:{}, ip:{}", macIP.mac, macIP.ip, e);
        }
    }

    List<Integer> findAllBindEmployee(Connection connection, MacIP macIP) {
        List<Integer> list = new ArrayList<>();
        // 优化后代码
        String sql = "SELECT * FROM EMPLOYEE_AUTH_BIND WHERE IP = ? AND MAC = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, macIP.ip);
            stmt.setString(2, macIP.mac);
            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    int id = rs.getInt("EMPLOYEE_ID");
                    if (id > 0) {
                        list.add(id);
                    }
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(
                    String.format("查询本机绑定信息异常，请联系管理员或重启应用！MAC: %s, IP: %s", macIP.mac, macIP.ip),
                    e);
        }
        return list;
    }

    private void showUserNameLoginDialog(Exception exception) {
        Stage stage = new Stage();
        stage.initOwner(primaryStage);
        stage.setTitle("登录");

        // 创建 BorderPane 并设置边距
        BorderPane borderPane = new BorderPane();
        borderPane.setPadding(new Insets(10));

        // 创建布局
        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);
        // 为整个 GridPane 设置外边距
        // GridPane.setMargin(grid, new Insets(10));

        // 账户输入框
        Label userLabel = new Label("账户:");
        TextField userField = new TextField();
        {
            String username = getUserName();
            if (StringUtils.hasText(username)) {
                userField.setText(username);
            }
            grid.add(userLabel, 0, 0);
            grid.add(userField, 1, 0);
        }

        // 密码输入框
        Label passwordLabel = new Label("密码:");
        PasswordField passwordField = new PasswordField();
        {
            String password = getPassword();
            if (StringUtils.hasText(password)) {
                passwordField.setText(password);
            }
            grid.add(passwordLabel, 0, 1);
            grid.add(passwordField, 1, 1);
        }

        // 记住密码复选框
        CheckBox rememberCheckBox = new CheckBox("记住账户密码");
        {
            String property = properties.getProperty("username_password.remember", "false");
            if (Boolean.parseBoolean(property)) {
                rememberCheckBox.setSelected(true);
            }
        }
        grid.add(rememberCheckBox, 1, 2);

        // 错误消息提示
        Label exceptionLabel = new Label();
        grid.add(exceptionLabel, 0, 3);
        exceptionLabel.setWrapText(true);
        GridPane.setColumnSpan(exceptionLabel, 2);
        if (exception == null) {
            exceptionLabel.setVisible(false);
        } else {
            exceptionLabel.setText(exception.getMessage());
            exceptionLabel.setVisible(true);
        }

        borderPane.setCenter(grid);

        // 登录按钮
        Button loginButton = new Button("登录");
        loginButton.setDefaultButton(true);
        borderPane.setBottom(loginButton);
        BorderPane.setAlignment(loginButton, javafx.geometry.Pos.CENTER);

        // 登录按钮点击事件
        loginButton.setOnAction(event -> {
            String username = userField.getText();
            String password = passwordField.getText();
            boolean remember = rememberCheckBox.isSelected();

            // 尝试连接数据库
            exceptionLabel.setText("");
            exceptionLabel.setVisible(false);
            try {
                EmployeeInfo employeeInfo = tryToConnect(username, password);
                if (employeeInfo.errorCode < 0) {
                    exceptionLabel.setText("登录失败：错误代码=" + employeeInfo.errorCode);
                    exceptionLabel.setVisible(true);
                } else {
                    logined(employeeInfo);
                }
            } catch (SQLException ex) {
                //
                exceptionLabel.setText("数据库错误：" + ex.getMessage());
                exceptionLabel.setVisible(true);
                return;
            }

            properties.setProperty("db.server.username", username);
            properties.setProperty("db.server.password", password);
            properties.setProperty("username_password.remember", Boolean.toString(remember));

            // 如果勾选了“记住密码”，则更新配置文件
            if (remember) {
                CompletableFuture.runAsync(() -> {
                    storeProperties();
                });
            }

            // 关闭登录窗口
            stage.close();
        });

        // 创建场景并设置到窗口
        Scene scene = new Scene(borderPane, 400, 260);
        stage.setScene(scene);
        // stage.setAlwaysOnTop(true);
        stage.setResizable(false);
        stage.showAndWait();
    }

    private void logined(EmployeeInfo employeeInfo) {
        info("欢迎 " + employeeInfo.name.get());
        if (!SpringApp.isRunning()) {
            info("请稍后...");
        }
        Desktop.instance.setActiveEmployeeId(employeeInfo.employeeId);
        // Desktop.instance.setSessionId(employeeInfo.sessionId);
        tryShowHomeWindow();
    }

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

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