package activity

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"sync"
	"time"
)

// CardType 卡牌类型
type CardType string

// CardTypeConfig 卡牌类型配置
type CardTypeConfig struct {
	Code     CardType `json:"code"`
	Name     string   `json:"name"`
	ImageURL string   `json:"image_url"`
	Quantity int32    `json:"quantity"`
}

// MatchingCard 游戏中的卡牌实例
type MatchingCard struct {
	ID   string   `json:"id"`
	Type CardType `json:"type"`
}

// MatchingGame 对对碰游戏结构
type MatchingGame struct {
	Mu               sync.Mutex       `json:"-"`
	ServerSeed       []byte           `json:"server_seed"`
	ServerSeedHash   string           `json:"server_seed_hash"`
	Nonce            int64            `json:"nonce"`
	CardConfigs      []CardTypeConfig `json:"card_configs"`
	Deck             []*MatchingCard  `json:"deck"`
	Board            [9]*MatchingCard `json:"board"`
	CardIDCounter    int64            `json:"card_id_counter"`
	TotalPairs       int64            `json:"total_pairs"`
	MaxPossiblePairs int64            `json:"max_possible_pairs"`
	LastActivity     time.Time        `json:"last_activity"`
	Position         string           `json:"position"`   // 用户选择的类型，用于服务端验证
	CreatedAt        time.Time        `json:"created_at"` // 游戏创建时间，用于自动开奖超时判断

	ActivityID int64 `json:"activity_id"`
	IssueID    int64 `json:"issue_id"`
	OrderID    int64 `json:"order_id"`
	UserID     int64 `json:"user_id"`
}

const MatchingGameKeyPrefix = "bindbox:matching_game:"

// NewMatchingGameWithConfig 使用配置创建游戏
func NewMatchingGameWithConfig(configs []CardTypeConfig, position string, masterSeed []byte) *MatchingGame {
	if len(masterSeed) == 0 {
		return nil
	}

	g := &MatchingGame{
		CardConfigs:  configs,
		Board:        [9]*MatchingCard{},
		LastActivity: time.Now(),
	}

	h := hmac.New(sha256.New, masterSeed)
	h.Write([]byte(position))
	h.Write([]byte(fmt.Sprintf("%d", time.Now().UnixNano())))
	g.ServerSeed = h.Sum(nil)

	hash := sha256.Sum256(g.ServerSeed)
	g.ServerSeedHash = fmt.Sprintf("%x", hash)

	totalCards := 0
	for _, cfg := range configs {
		totalCards += int(cfg.Quantity)
	}

	allCards := make([]*MatchingCard, 0, totalCards)
	for _, cfg := range configs {
		for i := int32(0); i < cfg.Quantity; i++ {
			g.CardIDCounter++
			id := fmt.Sprintf("c%d", g.CardIDCounter)
			mc := &MatchingCard{
				ID:   id,
				Type: cfg.Code,
			}
			allCards = append(allCards, mc)
		}
	}
	g.Deck = allCards

	g.SecureShuffle()

	for i := 0; i < 9; i++ {
		if len(g.Deck) > 0 {
			card := g.Deck[0]
			g.Deck = g.Deck[1:]
			g.Board[i] = card
		}
	}

	var theoreticalMax int64
	for _, cfg := range configs {
		theoreticalMax += int64(cfg.Quantity / 2)
	}
	g.MaxPossiblePairs = theoreticalMax

	return g
}

func (g *MatchingGame) SecureShuffle() {
	n := len(g.Deck)
	for i := n - 1; i > 0; i-- {
		j := g.secureRandInt(i+1, fmt.Sprintf("shuffle:%d", i))
		g.Deck[i], g.Deck[j] = g.Deck[j], g.Deck[i]
	}
}

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))
}

// GetGameState 获取游戏状态
func (g *MatchingGame) GetGameState() map[string]any {
	return map[string]any{
		"board":            g.Board,
		"deck_count":       len(g.Deck),
		"total_pairs":      g.TotalPairs,
		"server_seed_hash": g.ServerSeedHash,
	}
}

// SimulateMaxPairs 服务端模拟计算给定牌组和选中类型的理论最大对数
// 返回值：理论最大可消除对数
func (g *MatchingGame) SimulateMaxPairs() int64 {
	// 重建完整牌组：Board + Deck
	allCards := make([]*MatchingCard, 0, len(g.Board)+len(g.Deck))
	for _, c := range g.Board {
		if c != nil {
			allCards = append(allCards, c)
		}
	}
	allCards = append(allCards, g.Deck...)

	// 🔍 详细日志：牌组信息
	boardTypes := make([]string, 0, 9)
	for _, c := range g.Board {
		if c != nil {
			boardTypes = append(boardTypes, string(c.Type))
		}
	}
	log.Printf("[SimulateMaxPairs] Board(%d张): %s", len(boardTypes), strings.Join(boardTypes, ","))
	log.Printf("[SimulateMaxPairs] Deck长度: %d, 总牌数: %d", len(g.Deck), len(allCards))
	log.Printf("[SimulateMaxPairs] Position: '%s'", g.Position)

	if len(allCards) < 9 {
		log.Printf("[SimulateMaxPairs] 牌数不足9张，返回0")
		return 0
	}

	selectedType := CardType(g.Position)

	// 模拟游戏
	hand := make([]*MatchingCard, 9)
	copy(hand, allCards[:9])
	deckIndex := 9

	// 初始机会 = 手牌中选中类型的数量
	chance := int64(0)
	for _, c := range hand {
		if c != nil && c.Type == selectedType {
			chance++
		}
	}

	// 🔍 详细日志：初始状态
	handTypes := make([]string, 0, len(hand))
	for _, c := range hand {
		if c != nil {
			handTypes = append(handTypes, string(c.Type))
		}
	}
	log.Printf("[SimulateMaxPairs] 初始手牌: %s", strings.Join(handTypes, ","))
	log.Printf("[SimulateMaxPairs] 选中类型: '%s', 初始机会: %d", selectedType, chance)

	totalPairs := int64(0)

	// canEliminate 检查是否有可配对的牌
	canEliminate := func() CardType {
		counts := make(map[CardType]int)
		for _, c := range hand {
			if c == nil {
				continue
			}
			counts[c.Type]++
			if counts[c.Type] >= 2 {
				return c.Type
			}
		}
		return ""
	}

	// eliminatePair 消除一对
	eliminatePair := func(targetType CardType) bool {
		first, second := -1, -1
		for i, c := range hand {
			if c == nil || c.Type != targetType {
				continue
			}
			if first < 0 {
				first = i
			} else {
				second = i
				break
			}
		}
		if first >= 0 && second >= 0 {
			// 移除两张牌（从切片中删除）
			newHand := make([]*MatchingCard, 0, len(hand)-2)
			for i, c := range hand {
				if i != first && i != second {
					newHand = append(newHand, c)
				}
			}
			hand = newHand
			totalPairs++
			chance++
			return true
		}
		return false
	}

	// 游戏循环
	guard := 0
	for guard < 1000 {
		guard++

		// 尝试消除
		if pairType := canEliminate(); pairType != "" {
			eliminatePair(pairType)
			continue
		}

		// 不能消除，尝试摸牌
		if chance > 0 && deckIndex < len(allCards) {
			newCard := allCards[deckIndex]
			hand = append(hand, newCard)
			deckIndex++
			chance--

			continue
		}

		// 既不能消除也不能摸牌，游戏结束
		break
	}

	log.Printf("[SimulateMaxPairs] 模拟结束, 最终对数: %d, 剩余手牌: %d, 已摸牌数: %d", totalPairs, len(hand), deckIndex-9)
	return totalPairs
}

// ReconstructMatchingGame 根据订单号还原游戏状态
func (s *service) ReconstructMatchingGame(ctx context.Context, orderNo string) (*MatchingGame, error) {
	order, err := s.readDB.Orders.WithContext(ctx).Where(s.readDB.Orders.OrderNo.Eq(orderNo)).First()
	if err != nil {
		return nil, err
	}

	// 1. Get DrawLog first
	drawLog, err := s.readDB.ActivityDrawLogs.WithContext(ctx).
		Where(s.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).
		First()
	if err != nil {
		return nil, fmt.Errorf("draw log not found: %w", err)
	}

	// 2. Get Receipt using DrawLogID
	receipt, err := s.readDB.ActivityDrawReceipts.WithContext(ctx).
		Where(s.readDB.ActivityDrawReceipts.DrawLogID.Eq(drawLog.ID)).
		First()
	if err != nil {
		return nil, fmt.Errorf("draw receipt not found: %w", err)
	}

	serverSeed, err := hex.DecodeString(receipt.ServerSubSeed)
	if err != nil || len(serverSeed) == 0 {
		return nil, fmt.Errorf("invalid server seed in receipt")
	}

	// Retrieve ActivityID from Issue
	issue, err := s.readDB.ActivityIssues.WithContext(ctx).Where(s.readDB.ActivityIssues.ID.Eq(drawLog.IssueID)).First()
	var activityID int64
	if err == nil && issue != nil {
		activityID = issue.ActivityID
	}

	configs, err := s.loadCardTypesFromDB(ctx)
	if err != nil || len(configs) == 0 {
		return nil, fmt.Errorf("failed to load card types")
	}

	game := &MatchingGame{
		CardConfigs:    configs,
		Board:          [9]*MatchingCard{},
		LastActivity:   time.Now(),
		ServerSeed:     serverSeed,
		ServerSeedHash: receipt.ServerSeedHash,
		Nonce:          0,
		IssueID:        drawLog.IssueID,
		OrderID:        drawLog.OrderID,
		UserID:         order.UserID,
		ActivityID:     activityID,
	}

	allCards := make([]*MatchingCard, 0, 99)
	for _, cfg := range configs {
		for i := int32(0); i < cfg.Quantity; i++ {
			game.CardIDCounter++
			id := fmt.Sprintf("c%d", game.CardIDCounter)
			mc := &MatchingCard{
				ID:   id,
				Type: cfg.Code,
			}
			allCards = append(allCards, mc)
		}
	}
	game.Deck = allCards

	game.SecureShuffle()

	for i := 0; i < 9; i++ {
		if len(game.Deck) > 0 {
			card := game.Deck[0]
			game.Deck = game.Deck[1:]
			game.Board[i] = card
		}
	}

	var theoreticalMax int64
	for _, cfg := range configs {
		theoreticalMax += int64(cfg.Quantity / 2)
	}
	game.MaxPossiblePairs = theoreticalMax

	return game, nil
}

func (s *service) ListMatchingCardTypes(ctx context.Context) ([]CardTypeConfig, error) {
	return s.loadCardTypesFromDB(ctx)
}

func (s *service) loadCardTypesFromDB(ctx context.Context) ([]CardTypeConfig, error) {
	items, err := s.readDB.MatchingCardTypes.WithContext(ctx).Where(s.readDB.MatchingCardTypes.Status.Eq(1)).Order(s.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
}

// GetMatchingGameFromRedis 从 Redis 加载游戏状态
func (s *service) GetMatchingGameFromRedis(ctx context.Context, gameID string) (*MatchingGame, error) {
	data, err := s.redis.Get(ctx, MatchingGameKeyPrefix+gameID).Bytes()
	if err != nil {
		return nil, err
	}
	var game MatchingGame
	if err := json.Unmarshal(data, &game); err != nil {
		return nil, err
	}
	return &game, nil
}

// SaveMatchingGameToRedis 保存游戏状态到 Redis
func (s *service) SaveMatchingGameToRedis(ctx context.Context, gameID string, game *MatchingGame) error {
	data, err := json.Marshal(game)
	if err != nil {
		return err
	}
	return s.redis.Set(ctx, MatchingGameKeyPrefix+gameID, data, 30*time.Minute).Err()
}
