/*
本程序为云算力平台卖方客户端，主要提供以下功能：
1，将云算力平台上的卖方身份和GPU主机绑定
2，通过本客户端，可以使云算力平台获取到卖方具体每台机器的详细参数，包括GPU型号、显存容量等
3，卖方可通过本客户端自动匹配买方的挖矿需求，即卖方无需再买方下单后手动操作挖矿

卖家在启动客户端之前需要注意以下事项：
1，确定客户端执行主机已经配置好挖矿环境，包括显卡驱动、挖矿软件(指定挖矿软件)、执行权限等，即执行本客户端的用户可以手动通过挖矿软件进行挖矿
2，如果要对本机GPU进行移除(拔出GPU)操作，云算力平台会同步移除对应的GPU
3，如果在相关GPU有租约且没有在平台申请故障处理的情况下直接移除(拔出)GPU，会导致产生罚没，因此在有租约的情况下要移除故障GPU，请第一时间前往平台申请故障处理，在平台确认后再进行移除GPU的操作
4，如果要对本机GPU进行更换(拔出后又新插入GPU)操作，云算力平台会重新读取GPU数据，如果更换型号相同，则会按原有配置上架，如果更换的型号不同，则需在更换后前往卖家中心手动调整上架配置
5，如果在相关GPU有租约且没有在平台申请故障处理的情况下直接更换GPU，可能会导致产生罚没，因此在有租约的情况下要更换故障GPU，请第一时间前往平台申请故障处理，在平台确认后再进行更换GPU的操作
*/
package client

import (
	message "client/internal/msg"
	"client/internal/src"
	"client/internal/src/linux"
	"client/internal/utils"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"runtime"
	"strings"
)

type Client struct {
	Auth        string
	MachineCode string
	ServerConn  net.Conn            // 服务连接
	GPUs        map[int]message.GPU // {"gpu编号": message.GPU{}, ...}
	os          *src.SystemServer
	osName      string
}

func newClient(url string) *Client {
	var client = &Client{}
	// 读取身份文件
	auth, err := utils.ReadFile("./auth")
	if err != nil {
		log.Fatalf("获取客户端身份失败：%v", err)
		return nil
	}

	client.Auth = auth

	os := src.NewSystemServer()
	sys := runtime.GOOS
	if sys == "linux" {
		linux_ := linux.NewLinuxClient(auth)
		os.ResiterSystem("linux", linux_)
	} else {

	}
	client.os = os
	client.osName = sys

	// 读取主机MAC地址信息
	var machine_code string
	machine_code, err = os.GetMACAddress(sys)
	if err != nil {
		log.Fatalln(err)
		panic("获取当前主机信息失败，程序已退出，请检查网络后重新启动本客户端。")
	}
	utils.WirteFile("./machinecode", machine_code)

	client.MachineCode = machine_code

	gpus, err := os.GetGPUInfo(sys)
	if err != nil {
		log.Fatalln(err)
		panic("获取当前主机GPU数据失败，程序已退出，请检查GPU驱动等程序后重新启动本客户端。")
	}
	client.GPUs = gpus
	server_conn, err := net.Dial("tcp", url)
	if err != nil {
		log.Fatalf("客户端连接到服务器失败：%v", err)
		return nil
	}
	defer server_conn.Close()

	client.ServerConn = server_conn
	return client
}

func (c *Client) sendMachineCode() {
	var msg message.ServerMsg
	msg.ID = c.Auth + "." + c.MachineCode
	msg.Method = "auth.machineCode"
	msg.Params = c.GPUs
	msgByte, err := json.Marshal(msg)
	if err != nil {
		log.Fatalf("消息(%v)序列化失败：%v", msg, err)
		return
	}
	c.send(msgByte)
}

func (c *Client) receiveMsg() {
	buffer := make([]byte, 1024)

	for {
		n, err := c.ServerConn.Read(buffer)
		if err != nil {
			if err.Error() == "EOF" {
				// 服务端关闭连接时，退出接收循环
				log.Println("服务端关闭了连接")
				return
			}
			log.Println("接收数据失败:", err)
			return
		}

		msgByte := buffer[:n]
		go c.handleReceiveMsg(msgByte)
	}
}

func (c *Client) send(msg []byte) {
	_, err := c.ServerConn.Write(msg)
	if err != nil {
		log.Fatalf("发送消息失败，消息内容：%s", string(msg))
		return
	}
}

func (c *Client) handleReceiveMsg(msg []byte) {
	var data message.ServerMsg
	err := json.Unmarshal(msg, &data)
	if err != nil {
		log.Fatalf("解析接收到的消息失败：%v", err)
		return
	}
	parts := strings.Split(data.ID, ".")
	if len(parts) != 2 {
		log.Fatalf("解析通信协议(server->client)失败")
		return
	}
	auth, machine_code := parts[0], parts[1]
	if c.Auth != auth || c.MachineCode != machine_code {
		log.Fatalf("客户端接收到错误的服务端消息")
		return
	}
	switch data.Method {
	case "mining.req":
		mining_msg, ok := data.Params.(message.ConfigurationMiningMsg)
		if ok {
			// 这里开始挖矿
			err := c.os.Mining(c.osName, mining_msg)
			if err != nil {
				sendMsg_str := message.ServerMsgResp{
					ID:     c.Auth + "." + c.MachineCode,
					Result: false,
					Data:   err,
				}
				sendMsg_byte, err := json.Marshal(sendMsg_str)
				if err != nil {
					log.Fatalf("序列化%v失败：%v", sendMsg_str, err)
					break
				}
				c.send(sendMsg_byte) // 返回失败消息
			}
			// 挖矿开始
			data := message.ConfigurationMiningResp{
				Coin:          mining_msg.Coin,
				Algo:          mining_msg.Algo,
				Pool:          mining_msg.Pool,
				PoolUrl:       mining_msg.PoolUrl,
				WorkerID:      mining_msg.WorkerID,
				WalletAddress: mining_msg.WalletAddress,
				WatchUrl:      "", // 这里需要根据矿池自动生成
			}
			sendMsg_str := message.ServerMsgResp{
				ID:     c.Auth + "." + c.MachineCode,
				Result: true,
				Data:   data,
			}
			sendMsg_byte, err := json.Marshal(sendMsg_str)
			if err != nil {
				log.Fatalf("序列化%v失败：%v", sendMsg_str, err)
				break
			}
			c.send(sendMsg_byte) // 返回成功消息
		} else {
			sendMsg_str := message.ServerMsgResp{
				ID:     c.Auth + "." + c.MachineCode,
				Result: false,
				Data:   fmt.Errorf("错误的params数据结构：%v", mining_msg),
			}
			sendMsg_byte, err := json.Marshal(sendMsg_str)
			if err != nil {
				log.Fatalf("序列化%v失败：%v", sendMsg_str, err)
				break
			}
			c.send(sendMsg_byte) // 返回失败消息
		}
	}
}

func Star() {
	url := "xxxx"
	client := newClient(url)
	client.sendMachineCode()
	go client.receiveMsg() // 开始接收服务端消息
}
