package play

import (
	"context"
	"encoding/json"
	"errors"
	"game-driver/config/game"
	"game-driver/internal/middleware"
	"game-driver/internal/routes/play/card_pusher"
	"game-driver/internal/schema"
	"game-driver/leaf"
	"game-driver/pkg/card_reader"
	"game-driver/pkg/utils"
	"github.com/eclipse/paho.golang/paho"
	"go.uber.org/zap"
	"io/fs"
	"os"
	"sync"
	"time"
)

type ResponseBody struct {
	CardId string `json:"card_id"`
	Empty  int    `json:"empty"`
	Error  int    `json:"error"`
	OutOk  int    `json:"out_ok"`
	num    int
}

func PushCard(ctx context.Context) leaf.HandlerFunc {
	g := (game.C).(game.ConfigPush)

	readers := make([]card_reader.Reader, len(g.PushGroups))
	devices := make([]*card_pusher.Device, len(g.PushGroups))
	for i, group := range g.PushGroups {
		// 解析配置
		gv, _ := json.Marshal(group)
		zap.S().Infof("关卡配置[%v]: %s", i, string(gv))

		// 开始连接读卡器
		if group.Read != "" {
			reader, err := card_reader.NewReader(group.Read)
			if err != nil {
				if errors.Is(err, fs.ErrNotExist) {
					zap.S().Errorf("读卡器串口文件不存在: %s", group.Read)
					os.Exit(1)
				}
				zap.S().Panicf("读卡器串口 %q 连接失败 [%T]: %q", group.Read, err, err)
			}
			// 初始化读卡器
			err = reader.Init()
			if err != nil {
				reader.Close()
				zap.S().Panicf("读卡器 %q 初始配置失败: %s", group.Read, err)
			}
			// 保存读卡器
			readers[i] = reader
		}

		// 开始连接发卡器
		device, err := card_pusher.New(group)
		if err != nil {
			zap.S().Panicln("初始化发卡器失败: ", err)
		}

		// 保存发卡器
		devices[i] = device
	}
	go func() {
		<-ctx.Done()
		for _, reader := range readers {
			if reader != nil {
				_ = reader.Close()
			}
		}
		for _, device := range devices {
			if device != nil {
				_ = device.Close()
			}
		}
	}()

	return func(c *leaf.Context) {
		payload := leaf.Value[*schema.PlayModal](c, middleware.PayloadJSONKey)

		var action time.Duration
		if a, ok := payload.Game["action"]; ok {
			if v, ok := a.(float64); ok {
				action = time.Duration(v)
			}
		}

		// 等待组
		var wait sync.WaitGroup
		defer wait.Wait()

		// 结束信号通道
		a := make(chan struct{})
		// 发送结束信号
		defer close(a)

		wait.Add(1)
		go func() {
			defer wait.Done()
			select {
			case <-a:
			case <-time.After(action * time.Second):
				body := &ResponseBody{}
				for _, device := range devices {
					body.Empty += device.GetEmpty()
					body.Error += device.GetError()
				}
				for i, device := range devices {
					if device.GetEmpty() == 0 && device.GetError() == 0 && device.GetOutOk() == 0 {
						body.num = i + 1
						device.PushCard()
						break
					}
				}
				// 延迟1秒获取结果并发送消息
				time.AfterFunc(time.Second, func() {
					if body.num != 0 {
						// 若卡片就位，读取卡片信息
						if devices[body.num-1].GetOutOk() == 1 && readers[body.num-1] != nil {
							if info := readers[body.num-1].GetCardInfo(); info != nil {
								body.CardId = info.ID
							}
						}

						body.OutOk += devices[body.num-1].GetOutOk()
					}
					publishBody(ctx, c.Properties.ResponseTopic, body)
				})
			}
		}()

		// 执行等待信号
		Default(c)
	}
}

// 发布消息
func publishBody(ctx context.Context, topic string, body *ResponseBody) {
	if topic != "" {
		bytes, _ := json.Marshal(body)
		utils.GlobalMqttClient.Publish(ctx, &paho.Publish{
			Topic:   topic,
			Payload: bytes,
			QoS:     1,
		})
	}
}
