package org.wcs.plugin.tcp;

import lombok.extern.slf4j.Slf4j;
import org.wcs.plugin.tcp.enums.SocketStatusEnum;
import org.wcs.plugin.tcp.intf.ISocketClientEvent;
import org.wcs.plugin.tcp.model.SocketDataItem;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * Socket 操作类
 */
@Slf4j
public class SocketClient {

    /**
     * Socket 数据项列表
     */
    private List<SocketDataItem> socketDataItemList;

    /**
     * Socket 客户端事件
     */
    private ISocketClientEvent socketClientEvent;


    /**
     * 设置Socket 客户端事件
     * @param socketClientEvent Socket 客户端事件
     */
    public synchronized void setSocketClientEvent(ISocketClientEvent socketClientEvent) {
        this.socketClientEvent = socketClientEvent;
    }


    /**
     * 添加Socket数据项
     * @param socketDataItem Socket数据项
     */
    public synchronized void addSocketDataItem(SocketDataItem socketDataItem) {
        if (socketDataItemList == null) {
            socketDataItemList = new ArrayList<>();
        }
        List<SocketDataItem> checkExist = socketDataItemList.stream().filter(item -> item.getSocketId().equals(socketDataItem.getSocketId())).toList();
        if (!checkExist.isEmpty()) {
            throw new RuntimeException("SocketId已存在");
        }
        socketDataItemList.add(socketDataItem);
    }

    /**
     * 移除Socket数据项
     * @param socketId SocketId
     */
    public synchronized void removeSocketDataItem(String socketId) {
        if (socketDataItemList == null) {
            return;
        }
        List<SocketDataItem> socketDataItems = socketDataItemList.stream().filter(item -> item.getSocketId().equals(socketId)).toList();
        for (SocketDataItem socketDataItem : socketDataItems) {
            if(socketDataItem.getSocket() != null) {
                try {
                    socketDataItem.getSocket().getOutputStream().close();
                    socketDataItem.getSocket().getInputStream().close();
                    socketDataItem.getSocket().shutdownOutput();
                    socketDataItem.getSocket().shutdownInput();
                    socketDataItem.getSocket().close();
                } catch (IOException ignored) {}
            }
            socketDataItemList.remove(socketDataItem);
        }
    }

    /**
     * 移除所有Socket数据项
     */
    public synchronized void removeAllSocketDataItem() {
        if (socketDataItemList == null) {
            return;
        }
        for (SocketDataItem socketDataItem : socketDataItemList) {
            if(socketDataItem.getSocket() != null) {
                try {
                    socketDataItem.getSocket().getOutputStream().close();
                    socketDataItem.getSocket().getInputStream().close();
                    socketDataItem.getSocket().shutdownOutput();
                    socketDataItem.getSocket().shutdownInput();
                    socketDataItem.getSocket().close();
                } catch (IOException ignored) {}
            }
            socketDataItemList.remove(socketDataItem);
        }
    }

    /**
     * 连接所有Socket
     */
    public synchronized void connectAll() {
        if (socketDataItemList == null) {
            return;
        }
        connect(socketDataItemList);
    }

    /**
     * 连接Socket
     * @param socketDataItemList Socket数据项列表
     */
    private void connect(List<SocketDataItem> socketDataItemList) {
        for (SocketDataItem socketDataItem : socketDataItemList) {
            Thread thread = new Thread(() -> {
                connect(socketDataItem);
            });
            thread.start();
        }
    }

    /**
     * 连接Socket
     * @param socketDataItem Socket数据项
     */
    private void connect(SocketDataItem socketDataItem) {
        Socket socket = socketDataItem.getSocket();
        if(socket != null) {
            try {
                socket.getOutputStream().close();
                socket.getInputStream().close();
                socket.shutdownOutput();
                socket.shutdownInput();
                socket.close();
            } catch (IOException ignored) {}
        }
        try {
            socketDataItem.setSocketStatus(SocketStatusEnum.CONNECTING);
            socket = new Socket(socketDataItem.getSocketIp(), socketDataItem.getSocketPort());
            // 连接成功的结果
            socketDataItem.setLastReceiveMessageTime(LocalDateTime.now());
            socketDataItem.setSocketStatus(SocketStatusEnum.CONNECTED);
            socketDataItem.setSocket(socket);
            if(socketClientEvent != null) {
                socketClientEvent.onConnect(socketDataItem); // 触发连接成功事件
            }
            while (true) {
                try {
                    InputStream inputStream = socket.getInputStream();
                    byte[] bytes = inputStream.readAllBytes();
                    socketDataItem.setLastReceiveMessageTime(LocalDateTime.now());
                    if(socketClientEvent != null) {
                        socketClientEvent.onBytes(socketDataItem, bytes);
                        String message = new String(bytes, StandardCharsets.US_ASCII);
                        socketClientEvent.onMessage(socketDataItem, message); // 触发收到数据事件
                    }
                } catch (Exception e) {
                    // 断开的结果
                    socket = null;
                    socketDataItem.setSocketStatus(SocketStatusEnum.DISCONNECTED);
                    socketDataItem.setSocket(socket);
                    if(socketClientEvent != null) {
                        socketClientEvent.onDisconnect(socketDataItem); // 触发断开连接事件
                    }
                    return;
                }
            }
        } catch (IOException e) {
            // 连接失败的结果
            socket = null;
            socketDataItem.setSocketStatus(SocketStatusEnum.ERROR);
            socketDataItem.setSocket(socket);
            if(socketClientEvent != null) {
                socketClientEvent.onError(socketDataItem, e); // 触发连接失败事件
            }
        }
    }

    /**
     * 检查并重新连接
     */
    public void checkAndReconnect() {
        List<SocketDataItem> disConnectSocketDataItems = getDisConnectSocket();
        if (disConnectSocketDataItems != null) {
            connect(disConnectSocketDataItems);
        }
    }


    /**
     * 获取断开的Socket数据项
     * @return 断开的Socket数据项
     */
    private synchronized List<SocketDataItem> getDisConnectSocket() {
        if(socketDataItemList == null) {
            return null;
        }
        List<SocketDataItem> sidConnectSocketDataItems = new ArrayList<>(); // 断开的Socket数据项
        for (SocketDataItem socketDataItem : socketDataItemList) {
            boolean connected = isConnected(socketDataItem);
            if(connected) {
                continue;
            }
            sidConnectSocketDataItems.add(socketDataItem);
            if(socketClientEvent != null) {
                socketClientEvent.onDisconnect(socketDataItem); // 触发断开连接事件
            }
        }
        return sidConnectSocketDataItems;
    }

    /**
     * 判断Socket是否连接
     * @param socketDataItem Socket数据项
     * @return 是否连接
     */
    private boolean isConnected(SocketDataItem socketDataItem) {
        if(socketDataItem.getSocketStatus() == SocketStatusEnum.CONNECTING || socketDataItem.getSocketStatus() == SocketStatusEnum.CREATE) {
            return true; // 正在连接或者新创建的视为已连接
        }
        if(socketDataItem.getSocketStatus() != SocketStatusEnum.CONNECTED || socketDataItem.getSocket() == null) {
            socketDataItem.setSocketStatus(SocketStatusEnum.DISCONNECTED);
            return false;
        }
        // 如果上次收到消息时间和当前时间差距不超过5秒则默认认为连接正常
        LocalDateTime lastReceiveMessageTime = socketDataItem.getLastReceiveMessageTime();
        if(lastReceiveMessageTime == null || lastReceiveMessageTime.plusSeconds(5).isBefore(LocalDateTime.now())) {
            return true;
        }
        // 检测Socket是否断开，发送心跳包
        Socket socket = socketDataItem.getSocket();
        if(socket == null) {
            socketDataItem.setSocketStatus(SocketStatusEnum.DISCONNECTED);
            return false;
        }
        if(socketDataItem.getHeartBeatString() == null) {
            socketDataItem.setHeartBeatString("");
        }
        byte[] bytes = socketDataItem.getHeartBeatString().getBytes(StandardCharsets.US_ASCII);
        try {
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException e) {
            // 发送失败了，则认为断开了
            try {
                socket.getOutputStream().close();
                socket.getInputStream().close();
                socket.shutdownOutput();
                socket.shutdownInput();
                socket.close();
            } catch (IOException ignored) {}
            socket = null;
            socketDataItem.setSocketStatus(SocketStatusEnum.DISCONNECTED);
            socketDataItem.setSocket(socket);
            return false;
        }
        try {
            if(socket.isClosed() || !socket.isConnected()) {
                socket = null;
                socketDataItem.setSocketStatus(SocketStatusEnum.DISCONNECTED);
                socketDataItem.setSocket(socket);
                return false;
            }
            return true;
        } catch (Exception ignored) {
            socket = null;
            socketDataItem.setSocketStatus(SocketStatusEnum.DISCONNECTED);
            socketDataItem.setSocket(socket);
            return false;
        }
    }

    /**
     * 发送消息，返回异常信息，发送成功返回 null
     * @param socketId SocketId
     * @param message 消息
     * @return 错误信息
     */
    public String sendMessage(String socketId, String message) {
        if(socketId == null || socketDataItemList.isEmpty()) {
            return "Socket连接不存在";
        }
        for (SocketDataItem socketDataItem : socketDataItemList) {
            if(socketDataItem.getSocketId().equals(socketId)) {
                return sendMessage(socketDataItem, message);
            }
        }
        return "Socket连接不存在";
    }

    /**
     * 发送消息，返回异常信息，发送成功返回 null
     * @param socketDataItem Socket数据项
     * @param message 消息
     * @return 发送结果
     */
    public String sendMessage(SocketDataItem socketDataItem, String message) {
        if(socketDataItem == null || socketDataItem.getSocket() == null) {
            return "Socket连接不存在或者未连接";
        }
        Socket socket = socketDataItem.getSocket();
        synchronized (socket) {
            try(OutputStream outputStream = socket.getOutputStream()) {
                try {
                    byte[] bytes = message.getBytes(StandardCharsets.US_ASCII);

                    outputStream.write(bytes);
                    outputStream.flush();
                    return null;
                } catch (Exception e) {
                    return "发送失败，异常信息：" + e.getMessage();
                }
            } catch (IOException e) {
                return "发送失败，异常信息：" + e.getMessage();
            }
        }
    }

    /**
     * 发送消息，返回异常信息，发送成功返回 null
     * @param socketId SocketId
     * @param bytes 消息
     * @return 错误信息
     */
    public String sendBytes(String socketId, byte[] bytes) {
        if(socketId == null || socketDataItemList.isEmpty()) {
            return "Socket连接不存在";
        }
        for (SocketDataItem socketDataItem : socketDataItemList) {
            if(socketDataItem.getSocketId().equals(socketId)) {
                return sendBytes(socketDataItem, bytes);
            }
        }
        return "Socket连接不存在";
    }

    /**
     * 发送消息，返回异常信息，发送成功返回 null
     * @param socketDataItem Socket数据项
     * @param bytes 消息
     * @return 发送结果
     */
    public String sendBytes(SocketDataItem socketDataItem, byte[] bytes) {
        if(socketDataItem == null || socketDataItem.getSocket() == null) {
            return "Socket连接不存在或者未连接";
        }
        Socket socket = socketDataItem.getSocket();
        synchronized (socket) {
            try (OutputStream outputStream = socket.getOutputStream()) {
                try {
                    outputStream.write(bytes);
                    outputStream.flush();
                    return null;
                } catch (Exception e) {
                    return "发送失败，异常信息：" + e.getMessage();
                }
            } catch (Exception e) {
                return "发送失败，异常信息：" + e.getMessage();
            }
        }
    }



}
