package mysql

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"strings"
	"time"

	gamev1 "hyapp.local/api/proto/game/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
)

const roomRPSGameID = "room_rps"

type roomRPSStakeGiftSnapshot struct {
	GiftID           int64  `json:"gift_id"`
	GiftIDText       string `json:"gift_id_text"`
	GiftName         string `json:"gift_name"`
	GiftIconURL      string `json:"gift_icon_url"`
	GiftAnimationURL string `json:"gift_animation_url"`
	GiftPriceCoin    int64  `json:"gift_price_coin"`
	Enabled          bool   `json:"enabled"`
	SortOrder        int32  `json:"sort_order"`
}

// GetRoomRPSConfig 返回房内猜拳配置事实；没有行时返回 NotFound，让 service 决定是否使用默认配置。
func (r *Repository) GetRoomRPSConfig(ctx context.Context, appCode string, gameID string) (*gamev1.RoomRPSConfig, error) {
	if r == nil || r.db == nil {
		return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	app := appcode.Normalize(appCode)
	gameID = normalizeRoomRPSGameID(gameID)
	row := r.db.QueryRowContext(ctx,
		`SELECT app_code, game_id, status, challenge_timeout_ms, reveal_countdown_ms,
		        COALESCE(CAST(stake_gifts_json AS CHAR), '[]'), created_at_ms, updated_at_ms
		   FROM game_room_rps_configs
		  WHERE app_code = ? AND game_id = ?`,
		app, gameID,
	)
	config := &gamev1.RoomRPSConfig{}
	var giftsJSON string
	if err := row.Scan(
		&config.AppCode, &config.GameId, &config.Status, &config.ChallengeTimeoutMs,
		&config.RevealCountdownMs, &giftsJSON, &config.CreatedAtMs, &config.UpdatedAtMs,
	); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, xerr.New(xerr.NotFound, "room rps config not found")
		}
		return nil, err
	}
	gifts, err := roomRPSStakeGiftsFromJSON(giftsJSON)
	if err != nil {
		return nil, err
	}
	config.StakeGifts = gifts
	return config, nil
}

// UpsertRoomRPSConfig 持久化后台保存后的完整快照；重复保存只更新时间和可运营字段，不重写首次创建时间。
func (r *Repository) UpsertRoomRPSConfig(ctx context.Context, config *gamev1.RoomRPSConfig) (*gamev1.RoomRPSConfig, error) {
	if r == nil || r.db == nil {
		return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	if config == nil {
		return nil, xerr.New(xerr.InvalidArgument, "room rps config is required")
	}
	app := appcode.Normalize(config.GetAppCode())
	if app == "" {
		return nil, xerr.New(xerr.InvalidArgument, "app_code is required")
	}
	gameID := normalizeRoomRPSGameID(config.GetGameId())
	nowMS := time.Now().UnixMilli()
	createdAtMS := config.GetCreatedAtMs()
	if createdAtMS <= 0 {
		createdAtMS = nowMS
	}
	updatedAtMS := config.GetUpdatedAtMs()
	if updatedAtMS <= 0 {
		updatedAtMS = nowMS
	}
	rawGifts, err := json.Marshal(roomRPSStakeGiftSnapshots(config.GetStakeGifts()))
	if err != nil {
		return nil, err
	}
	if _, err := r.db.ExecContext(ctx,
		`INSERT INTO game_room_rps_configs (
		   app_code, game_id, status, challenge_timeout_ms, reveal_countdown_ms,
		   stake_gifts_json, created_at_ms, updated_at_ms
		 ) VALUES (?, ?, ?, ?, ?, CAST(? AS JSON), ?, ?)
		 ON DUPLICATE KEY UPDATE
		   status = VALUES(status),
		   challenge_timeout_ms = VALUES(challenge_timeout_ms),
		   reveal_countdown_ms = VALUES(reveal_countdown_ms),
		   stake_gifts_json = VALUES(stake_gifts_json),
		   updated_at_ms = VALUES(updated_at_ms)`,
		app, gameID, strings.TrimSpace(config.GetStatus()), config.GetChallengeTimeoutMs(), config.GetRevealCountdownMs(),
		string(rawGifts), createdAtMS, updatedAtMS,
	); err != nil {
		return nil, err
	}
	return r.GetRoomRPSConfig(ctx, app, gameID)
}

func normalizeRoomRPSGameID(gameID string) string {
	gameID = strings.TrimSpace(gameID)
	if gameID == "" {
		return roomRPSGameID
	}
	return gameID
}

func roomRPSStakeGiftSnapshots(gifts []*gamev1.RoomRPSStakeGift) []roomRPSStakeGiftSnapshot {
	result := make([]roomRPSStakeGiftSnapshot, 0, len(gifts))
	for _, gift := range gifts {
		if gift == nil {
			continue
		}
		result = append(result, roomRPSStakeGiftSnapshot{
			GiftID:           gift.GetGiftId(),
			GiftIDText:       strings.TrimSpace(gift.GetGiftIdText()),
			GiftName:         strings.TrimSpace(gift.GetGiftName()),
			GiftIconURL:      strings.TrimSpace(gift.GetGiftIconUrl()),
			GiftAnimationURL: strings.TrimSpace(gift.GetGiftAnimationUrl()),
			GiftPriceCoin:    gift.GetGiftPriceCoin(),
			Enabled:          gift.GetEnabled(),
			SortOrder:        gift.GetSortOrder(),
		})
	}
	return result
}

func roomRPSStakeGiftsFromJSON(value string) ([]*gamev1.RoomRPSStakeGift, error) {
	value = strings.TrimSpace(value)
	if value == "" || value == "null" {
		return []*gamev1.RoomRPSStakeGift{}, nil
	}
	var snapshots []roomRPSStakeGiftSnapshot
	if err := json.Unmarshal([]byte(value), &snapshots); err != nil {
		return nil, err
	}
	result := make([]*gamev1.RoomRPSStakeGift, 0, len(snapshots))
	for _, snapshot := range snapshots {
		result = append(result, &gamev1.RoomRPSStakeGift{
			GiftId:           snapshot.GiftID,
			GiftIdText:       strings.TrimSpace(snapshot.GiftIDText),
			GiftName:         strings.TrimSpace(snapshot.GiftName),
			GiftIconUrl:      strings.TrimSpace(snapshot.GiftIconURL),
			GiftAnimationUrl: strings.TrimSpace(snapshot.GiftAnimationURL),
			GiftPriceCoin:    snapshot.GiftPriceCoin,
			Enabled:          snapshot.Enabled,
			SortOrder:        snapshot.SortOrder,
		})
	}
	return result, nil
}
