package com.wms_main.repository.tcp;

import com.wms_main.repository.tcp.constant.SocketStatusEnum;
import com.wms_main.repository.tcp.model.po.SocketServerData;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TcpClient {

    // 基础的服务端的信息
    private List<SocketServerData> socketServerDataList;

    // 事件回调
    @Setter
    private TcpEvent tcpEvent;

    // 心跳包数据
    @Setter
    private byte[] keepAliveBytes = "".getBytes(StandardCharsets.US_ASCII);

    protected Map<Integer, LocalDateTime> lastReceiveTime;

    /**
     * 设置基础的服务端信息
     *
     * @param id   服务端ID
     * @param ip   服务端ip
     * @param port 服务端端口
     * @param name 服务端名称
     */
    public void setBaseServerInfo(Integer id, String ip, Integer port, String name) {
        if(socketServerDataList == null) {
            socketServerDataList = new ArrayList<>();
        }
        SocketServerData socketServerData = new SocketServerData();
        socketServerData.setId(id);
        socketServerData.setIp(ip);
        socketServerData.setPort(port);
        socketServerData.setName(name);
        socketServerData.setConnectStatus(SocketStatusEnum.WAIT_CONNECT);
        socketServerDataList.add(socketServerData);
    }

    /**
     * 连接服务端
     */
    public void connect() {
        if(socketServerDataList == null || socketServerDataList.isEmpty()) {
            return;
        }
        for (SocketServerData socketServerData : socketServerDataList) {
            Thread thread = new Thread(()->{
                if(socketServerData.getConnectStatus() == SocketStatusEnum.CONNECTING
                        || socketServerData.getConnectStatus() == SocketStatusEnum.CONNECTED) {
                    return;
                }
                try {
                    socketServerData.setConnectStatus(SocketStatusEnum.CONNECTING);
                    socketServerData.setSocket(new Socket(socketServerData.getIp(), socketServerData.getPort()));
                    socketServerData.setConnectStatus(SocketStatusEnum.CONNECTED);
                    if (tcpEvent != null) {
                        tcpEvent.connected(socketServerData);
                    }
                    while (true) {
                        InputStream inputStream = socketServerData.getSocket().getInputStream();// 接收消息
                        byte[] receiveBytes = new byte[128];
                        int readCount = inputStream.read(receiveBytes);
                        if (tcpEvent != null) {
                            if(lastReceiveTime == null) {
                                lastReceiveTime = new java.util.HashMap<>();
                            }
                            lastReceiveTime.put(socketServerData.getId(), LocalDateTime.now());
                            tcpEvent.receiveData(socketServerData, receiveBytes);
                        }
                        if(readCount == 0 || Arrays.equals(receiveBytes, new byte[128])) {
                            // 断开连接
                            try {
                                socketServerData.getSocket().close();
                            } catch (IOException ignored) {
                            }
                            socketServerData.setSocket(null);
                            if (tcpEvent != null) {
                                tcpEvent.disConnected(socketServerData);
                            }
                            break;
                        }
                    }
                    socketServerData.setSocket(null);
                    socketServerData.setConnectStatus(SocketStatusEnum.DISCONNECTED);
                    if (tcpEvent != null) {
                        tcpEvent.disConnected(socketServerData);
                    }
                } catch (IOException e) {
                    if (socketServerData.getSocket() != null) {
                        try {
                            socketServerData.getSocket().close(); // 连接失败时候释放Socket资源
                        } catch (IOException ignored) {
                        }
                        socketServerData.setSocket(null);
                    }
                    socketServerData.setConnectStatus(SocketStatusEnum.DISCONNECTED);
                    if (tcpEvent != null) {
                        tcpEvent.connectFailed(socketServerData, e); // 连接失败
                    }
                }
            });
            thread.start();
        }
    }

    /**
     * 定时器监控 Socket 是否断开
     */
    public void keepAlive() {
       new Thread(() -> {
            while (socketServerDataList != null && !socketServerDataList.isEmpty()) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    for (SocketServerData socketServerData : socketServerDataList) {
                        if (socketServerData.getConnectStatus() == SocketStatusEnum.CONNECTED && socketServerData.getSocket() != null) {
                            /* 检测连接中的Socket是否断开，若断开则设置状态为连接断开并且释放Socket资源 */
                            boolean isConnected = isConnected(socketServerData);
                            if (isConnected) {
                                continue;
                            }
                            socketServerData.setConnectStatus(SocketStatusEnum.DISCONNECTED);
                            try {
                                socketServerData.getSocket().close();
                            } catch (IOException ignored) {
                            }
                            socketServerData.setSocket(null);
                            if (tcpEvent != null) {
                                tcpEvent.disConnected(socketServerData);
                            }
                            break;
                        }
                    }
                    connect(); // 重新调用连接方法
                } catch (Exception ignored) {}
            }
        }).start();
    }

    /**
     * 校验是否断开连接
     * @param socketServerData socket
     * @return 是否断开连接
     */
    public boolean isConnected(@NotNull SocketServerData socketServerData) {
        boolean canPing = ping(socketServerData.getIp());
        if(!canPing) {
            return false;
        }
        try {
            socketServerData.getSocket().getOutputStream().write(keepAliveBytes);
            return true;
        } catch (IOException e) {
           return false;
        }
    }


    /**
     * ping 一个ip 地址
     * @param ip ip
     * @return ping 是否成功的结果
     */
    private boolean ping(String ip) {
        try {
            return InetAddress.getByName(ip).isReachable(500);
        } catch (IOException e) {
            return false;
        }
    }


    /**
     * 向一个 Socket id 发送信息
     * @param id id
     * @param data 信息
     * @return 错误信息，为 null 则表示发送成功
     */
    public String sendData(Integer id, byte[] data) {
        if(id == null) {
            return "传入的 id 为 null";
        }
        if(data == null || data.length == 0) {
            return "要发送的数据为空";
        }
        if(socketServerDataList == null || socketServerDataList.isEmpty()) {
            return "没有Socket资源";
        }
        for (SocketServerData socketServerData : socketServerDataList) {
            if(socketServerData.getId().equals(id)) {
                try {
                    if(socketServerData.getConnectStatus() != SocketStatusEnum.CONNECTED) {
                        return "Socket 未连接或者已经断开";
                    }
                    if(socketServerData.getSocket() == null) {
                        return "Socket 连接为空";
                    }
                    OutputStream outputStream = socketServerData.getSocket().getOutputStream();
                    outputStream.write(data);
                    outputStream.flush();
                    return null;
                } catch (IOException e) {
                    return "发送数据失败";
                }
            }
        }
        return "未找到对应 id 的 Socket";
    }

}
