package play

import (
	"context"
	"errors"
	"game-driver/config/game"
	"game-driver/internal/middleware"
	"game-driver/internal/schema"
	"game-driver/leaf"
	"game-driver/pkg/card_reader"
	"game-driver/pkg/channel"
	"game-driver/pkg/tts"
	"io/fs"
	"os"
	"sync"
	"time"

	"go.uber.org/zap"
)

func WaitCard(ctx context.Context) leaf.HandlerFunc {
	g := (game.C).(game.ConfigWait)

	reader, err := card_reader.NewReader(g.Addr)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			zap.S().Errorf("读卡器串口文件不存在: %s", g.Addr)
			os.Exit(1)
		}
		zap.S().Panicf("读卡器串口连接失败 [%T]: %q", err, err)
	}
	go func() {
		<-ctx.Done()
		_ = reader.Close()
	}()

	err = reader.Init()
	if err != nil {
		zap.S().Panicln("读卡器初始化失败", err)
	}

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

		// 读取卡片等待时间
		var waitCard time.Duration
		if a, ok := payload.Game["wait_card"]; ok {
			if v, ok := a.(float64); ok {
				waitCard = time.Duration(v)
			}
		}
		// 卡片ID预期值
		var cardId string
		if a, ok := payload.Game["card_id"]; ok {
			if v, ok := a.(string); ok {
				cardId = v
			}
		}
		// 卡片比对成功语音内容
		var cardOk string
		if a, ok := payload.Game["card_ok"]; ok {
			if v, ok := a.(string); ok {
				cardOk = v
			}
		}
		// 卡片比对失败语音内容
		var cardError string
		if a, ok := payload.Game["card_error"]; ok {
			if v, ok := a.(string); ok {
				cardError = v
			}
		}

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

		// 卡片信息通道
		cardInfo := channel.NewClosed[string]()
		defer cardInfo.Close()

		// 结束信号通道
		// 使用独立 context 确保读卡器监听完整执行，不受外部取消影响
		c2, cancel := context.WithCancel(context.Background())
		defer cancel()

		wait.Add(1)
		go func() {
			defer wait.Done()
			reader.OnCardInfo(c2, func(info *card_reader.CardInfo) {
				cardInfo.Send(info.ID)
			})
		}()

		// 多次读取，直到读取到正确的卡片
		for isNeed := true; isNeed; {
			isNeed = false

			select {
			case <-c.Done():
			case <-time.After(waitCard * time.Second):
			case id, ok := <-cardInfo.Data(): // 等待卡片插入
				if ok { // 非关闭信号
					// 比对卡号是否正确，不正确则重新读取
					if cardId != id {
						zap.S().Infof("读取到卡片数据%q，与预期卡片数据%q不一致", id, cardId)
						// 播报错误提示
						tts.DefaultTTS.Sound(c, cardError)
						isNeed = true
						break
					}
					// 播报恭喜语音
					tts.DefaultTTS.Sound(c, cardOk)
					//TODO: 打开炫酷光效，屏幕跳转恭喜页面
					zap.S().Infof("读取到卡片数据%q，开始打开炫酷光效", id)
					Default(c)
				}
			}
		}
	}
}
