package app

import (
	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/repository/mysql/dao"
	"context"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"fmt"
	"net/http"
	"time"
)

// CardType 卡牌类型
type CardType string

// CardTypeConfig 卡牌类型配置（从数据库加载）
type CardTypeConfig struct {
	Code     CardType
	Name     string
	ImageURL string
	Quantity int32
}

// MatchingGame 对对碰游戏结构
type MatchingGame struct {
	serverSeed     []byte
	serverSeedHash string
	nonce          int64
	cardConfigs    []CardTypeConfig
	cards          []CardType
	hand           []CardType
	deck           []CardType
	totalPairs     int64
	round          int64
	roundHistory   []MatchingRoundResult
}

type MatchingCard struct {
	ID       int      `json:"id"`
	Type     CardType `json:"type"`
	Name     string   `json:"name,omitempty"`
	ImageURL string   `json:"image_url,omitempty"`
}

type MatchingRoundResult struct {
	Round       int64          `json:"round"`
	HandBefore  []CardType     `json:"hand_before"`
	Pairs       []MatchingPair `json:"pairs"`
	PairsCount  int64          `json:"pairs_count"`
	DrawnCards  []CardType     `json:"drawn_cards"`
	HandAfter   []CardType     `json:"hand_after"`
	CanContinue bool           `json:"can_continue"`
}

type MatchingPair struct {
	CardType CardType `json:"card_type"`
	Count    int64    `json:"count"`
}

// loadCardTypesFromDB 从数据库加载启用的卡牌类型配置
func loadCardTypesFromDB(ctx context.Context, readDB *dao.Query) ([]CardTypeConfig, error) {
	items, err := readDB.MatchingCardTypes.WithContext(ctx).Where(readDB.MatchingCardTypes.Status.Eq(1)).Order(readDB.MatchingCardTypes.Sort.Asc()).Find()
	if err != nil {
		return nil, err
	}
	configs := make([]CardTypeConfig, len(items))
	for i, item := range items {
		configs[i] = CardTypeConfig{
			Code:     CardType(item.Code),
			Name:     item.Name,
			ImageURL: item.ImageURL,
			Quantity: item.Quantity,
		}
	}
	return configs, nil
}

// NewMatchingGameWithConfig 使用数据库配置创建游戏
func NewMatchingGameWithConfig(configs []CardTypeConfig) *MatchingGame {
	g := &MatchingGame{
		cardConfigs:  configs,
		roundHistory: []MatchingRoundResult{},
	}

	// 生成服务器种子
	g.serverSeed = make([]byte, 32)
	rand.Read(g.serverSeed)
	hash := sha256.Sum256(g.serverSeed)
	g.serverSeedHash = fmt.Sprintf("%x", hash)

	// 根据配置生成卡牌
	totalCards := 0
	for _, cfg := range configs {
		totalCards += int(cfg.Quantity)
	}
	g.cards = make([]CardType, 0, totalCards)
	for _, cfg := range configs {
		for i := int32(0); i < cfg.Quantity; i++ {
			g.cards = append(g.cards, cfg.Code)
		}
	}

	// 安全洗牌
	g.secureShuffle()

	// 分配手牌和牌堆（手牌9张）
	handSize := 9
	if len(g.cards) < handSize {
		handSize = len(g.cards)
	}
	g.hand = g.cards[:handSize]
	g.deck = g.cards[handSize:]

	return g
}

// getCardConfig 获取指定卡牌类型的配置
func (g *MatchingGame) getCardConfig(cardType CardType) *CardTypeConfig {
	for i := range g.cardConfigs {
		if g.cardConfigs[i].Code == cardType {
			return &g.cardConfigs[i]
		}
	}
	return nil
}

// 保留原有函数用于向后兼容（使用默认配置）
func NewMatchingGame() *MatchingGame {
	defaultConfigs := []CardTypeConfig{
		{Code: "A", Name: "类型A", Quantity: 9},
		{Code: "B", Name: "类型B", Quantity: 9},
		{Code: "C", Name: "类型C", Quantity: 9},
		{Code: "D", Name: "类型D", Quantity: 9},
		{Code: "E", Name: "类型E", Quantity: 9},
		{Code: "F", Name: "类型F", Quantity: 9},
		{Code: "G", Name: "类型G", Quantity: 9},
		{Code: "H", Name: "类型H", Quantity: 9},
		{Code: "I", Name: "类型I", Quantity: 9},
		{Code: "J", Name: "类型J", Quantity: 9},
		{Code: "K", Name: "类型K", Quantity: 9},
	}
	return NewMatchingGameWithConfig(defaultConfigs)
}

// secureShuffle 使用 HMAC-SHA256 的 Fisher-Yates 洗牌
func (g *MatchingGame) secureShuffle() {
	n := len(g.cards)
	for i := n - 1; i > 0; i-- {
		j := g.secureRandInt(i+1, fmt.Sprintf("shuffle:%d", i))
		g.cards[i], g.cards[j] = g.cards[j], g.cards[i]
	}
}

// secureRandInt 使用 HMAC-SHA256 生成安全随机数
func (g *MatchingGame) secureRandInt(max int, context string) int {
	g.nonce++
	message := fmt.Sprintf("%s|nonce:%d", context, g.nonce)
	mac := hmac.New(sha256.New, g.serverSeed)
	mac.Write([]byte(message))
	sum := mac.Sum(nil)
	val := binary.BigEndian.Uint64(sum[:8])
	return int(val % uint64(max))
}

// PlayRound 执行一轮游戏
func (g *MatchingGame) PlayRound(isFirst bool) MatchingRoundResult {
	g.round++
	handBefore := make([]CardType, len(g.hand))
	copy(handBefore, g.hand)
	result := MatchingRoundResult{
		Round:      g.round,
		HandBefore: handBefore,
	}

	// 第一轮：如果有类型A的卡牌，额外抽牌
	if isFirst {
		appleCount := 0
		for _, c := range g.hand {
			if c == "A" {
				appleCount++
			}
		}
		if appleCount > 0 && len(g.deck) >= appleCount {
			extra := g.deck[:appleCount]
			g.hand = append(g.hand, extra...)
			g.deck = g.deck[appleCount:]
			result.DrawnCards = append(result.DrawnCards, extra...)
		}
	}

	// 统计每种牌的数量
	counter := make(map[CardType]int)
	for _, c := range g.hand {
		counter[c]++
	}

	// 找出配对
	pairsCount := int64(0)
	remaining := []CardType{}
	for cardType, count := range counter {
		pairs := count / 2
		if pairs > 0 {
			pairsCount += int64(pairs)
			result.Pairs = append(result.Pairs, MatchingPair{CardType: cardType, Count: int64(pairs * 2)})
		}
		// 剩余单张
		if count%2 == 1 {
			remaining = append(remaining, cardType)
		}
	}

	result.PairsCount = pairsCount
	g.totalPairs += pairsCount

	// 抽取新牌
	if pairsCount > 0 {
		drawCount := int(pairsCount)
		if drawCount > len(g.deck) {
			drawCount = len(g.deck)
		}
		if drawCount > 0 {
			newCards := g.deck[:drawCount]
			g.deck = g.deck[drawCount:]
			remaining = append(remaining, newCards...)
			result.DrawnCards = append(result.DrawnCards, newCards...)
		}
	}

	g.hand = remaining
	handAfter := make([]CardType, len(g.hand))
	copy(handAfter, g.hand)
	result.HandAfter = handAfter
	result.CanContinue = pairsCount > 0

	g.roundHistory = append(g.roundHistory, result)

	return result
}

// GetGameState 获取游戏状态
func (g *MatchingGame) GetGameState() map[string]any {
	return map[string]any{
		"hand":             g.hand,
		"hand_count":       len(g.hand),
		"deck_count":       len(g.deck),
		"total_pairs":      g.totalPairs,
		"round":            g.round,
		"server_seed_hash": g.serverSeedHash,
	}
}

// ========== API Handlers ==========

type matchingGameStartRequest struct {
	IssueID int64 `json:"issue_id"`
}

type matchingGameStartResponse struct {
	GameID         string              `json:"game_id"`
	Hand           []MatchingCard      `json:"hand"`
	DeckCount      int                 `json:"deck_count"`
	ServerSeedHash string              `json:"server_seed_hash"`
	FirstRound     MatchingRoundResult `json:"first_round"`
}

type matchingGamePlayRequest struct {
	GameID string `json:"game_id"`
}

type matchingGamePlayResponse struct {
	Round      MatchingRoundResult `json:"round"`
	TotalPairs int64               `json:"total_pairs"`
	GameOver   bool                `json:"game_over"`
	FinalState map[string]any      `json:"final_state,omitempty"`
}

// 游戏会话存储（生产环境应使用 Redis）
var gameSessionsV2 = make(map[string]*MatchingGame)

// StartMatchingGame 开始对对碰游戏
// @Summary 开始对对碰游戏
// @Description 创建新的对对碰游戏会话，返回初始手牌和第一轮结果
// @Tags APP端.活动
// @Accept json
// @Produce json
// @Security LoginVerifyToken
// @Param RequestBody body matchingGameStartRequest true "请求参数"
// @Success 200 {object} matchingGameStartResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/matching/start [post]
func (h *handler) StartMatchingGame() core.HandlerFunc {
	return func(ctx core.Context) {
		userID := int64(ctx.SessionUserInfo().Id)

		// 从数据库加载卡牌类型配置
		configs, err := loadCardTypesFromDB(ctx.RequestContext(), h.readDB)
		if err != nil || len(configs) == 0 {
			// 如果数据库没有配置，使用默认配置
			configs = []CardTypeConfig{
				{Code: "A", Name: "类型A", Quantity: 9},
				{Code: "B", Name: "类型B", Quantity: 9},
				{Code: "C", Name: "类型C", Quantity: 9},
				{Code: "D", Name: "类型D", Quantity: 9},
				{Code: "E", Name: "类型E", Quantity: 9},
				{Code: "F", Name: "类型F", Quantity: 9},
				{Code: "G", Name: "类型G", Quantity: 9},
				{Code: "H", Name: "类型H", Quantity: 9},
				{Code: "I", Name: "类型I", Quantity: 9},
				{Code: "J", Name: "类型J", Quantity: 9},
				{Code: "K", Name: "类型K", Quantity: 9},
			}
		}

		// 创建新游戏
		game := NewMatchingGameWithConfig(configs)

		// 生成游戏ID
		gameID := fmt.Sprintf("MG%d%d", userID, time.Now().UnixNano())

		// 存储游戏会话
		gameSessionsV2[gameID] = game

		// 执行第一轮
		firstRound := game.PlayRound(true)

		// 构建手牌展示（包含卡牌名称和图片）
		cards := make([]MatchingCard, len(game.hand))
		for i, c := range game.hand {
			mc := MatchingCard{ID: i, Type: c}
			if cfg := game.getCardConfig(c); cfg != nil {
				mc.Name = cfg.Name
				mc.ImageURL = cfg.ImageURL
			}
			cards[i] = mc
		}

		rsp := &matchingGameStartResponse{
			GameID:         gameID,
			Hand:           cards,
			DeckCount:      len(game.deck),
			ServerSeedHash: game.serverSeedHash,
			FirstRound:     firstRound,
		}

		ctx.Payload(rsp)
	}
}

// PlayMatchingGame 执行一轮对对碰游戏
// @Summary 执行一轮对对碰游戏
// @Description 执行一轮配对，返回配对结果和新抽取的牌
// @Tags APP端.活动
// @Accept json
// @Produce json
// @Security LoginVerifyToken
// @Param RequestBody body matchingGamePlayRequest true "请求参数"
// @Success 200 {object} matchingGamePlayResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/matching/play [post]
func (h *handler) PlayMatchingGame() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(matchingGamePlayRequest)
		if err := ctx.ShouldBindJSON(req); err != nil || req.GameID == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "game_id required"))
			return
		}

		// 获取游戏会话
		game, ok := gameSessionsV2[req.GameID]
		if !ok {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "game session not found"))
			return
		}

		// 执行一轮
		round := game.PlayRound(false)

		rsp := &matchingGamePlayResponse{
			Round:      round,
			TotalPairs: game.totalPairs,
			GameOver:   !round.CanContinue,
		}

		// 如果游戏结束，返回最终状态
		if !round.CanContinue {
			rsp.FinalState = game.GetGameState()
			// 清理会话
			delete(gameSessionsV2, req.GameID)
		}

		ctx.Payload(rsp)
	}
}

// GetMatchingGameState 获取对对碰游戏状态
// @Summary 获取对对碰游戏状态
// @Description 获取当前游戏的完整状态
// @Tags APP端.活动
// @Accept json
// @Produce json
// @Security LoginVerifyToken
// @Param game_id query string true "游戏ID"
// @Success 200 {object} map[string]any
// @Failure 400 {object} code.Failure
// @Router /api/app/matching/state [get]
func (h *handler) GetMatchingGameState() core.HandlerFunc {
	return func(ctx core.Context) {
		gameID := ctx.RequestInputParams().Get("game_id")
		if gameID == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "game_id required"))
			return
		}

		game, ok := gameSessionsV2[gameID]
		if !ok {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "game session not found"))
			return
		}

		ctx.Payload(game.GetGameState())
	}
}
