package mysql

import (
	"context"
	"database/sql"
	"strconv"
	"strings"

	"hyapp/pkg/appcode"
)

type socialUserDayDelta struct {
	AppCode                    string
	StatTZ                     string
	StatDay                    string
	UserID                     int64
	DeviceID                   string
	CountryID                  int64
	RegionID                   int64
	UserRole                   string
	UpdatedAt                  int64
	FirstOpen                  int64
	ProfileSetupView           int64
	Registered                 int64
	Active                     int64
	RoomJoinOK                 int64
	RoomJoinNG                 int64
	MicUp                      int64
	Gift                       int64
	NormalGift                 int64
	LuckyGift                  int64
	SuperGift                  int64
	Recharge                   int64
	FirstRecharge              int64
	RepeatRecharge             int64
	GameBet                    int64
	SelfGameBet                int64
	ProbabilityBet             int64
	Chat                       int64
	FollowUser                 int64
	FollowRoom                 int64
	GiftPanel                  int64
	ProbabilityPanel           int64
	AppStayMS                  int64
	RoomStayMS                 int64
	RoomOnlineMS               int64
	MicStayMS                  int64
	RechargeCount              int64
	RechargeUSDMinor           int64
	GoogleRechargeUSDMinor     int64
	ThirdPartyRechargeUSDMinor int64
	CoinSellerRechargeUSDMinor int64
	FirstRechargeUSDMinor      int64
	RepeatRechargeUSDMinor     int64
	NormalGiftCoin             int64
	LuckyGiftCoin              int64
	SuperGiftCoin              int64
	LuckyGiftPayoutCoin        int64
	SuperGiftPayoutCoin        int64
	GameBetCoin                int64
	GameBetCount               int64
	SelfGameBetCoin            int64
	SelfGameBetCount           int64
	ProbabilityBetCoin         int64
	ProbabilityBetCount        int64
	OtherConsumeCoin           int64
	OutputCoin                 int64
}

func applySocialUserDayDelta(ctx context.Context, tx *sql.Tx, delta socialUserDayDelta) error {
	subjectKey := socialSubjectKey(delta.UserID, delta.DeviceID)
	if subjectKey == "" {
		return nil
	}
	app := appcode.Normalize(delta.AppCode)
	statTZ := normalizeStatTZ(delta.StatTZ)
	day := strings.TrimSpace(delta.StatDay)
	if app == "" || day == "" {
		return nil
	}
	countryID, regionID := normalizeDimension(delta.CountryID, delta.RegionID)
	role := normalizeSocialUserRole(delta.UserRole)
	// 这张表是 P0-P5 的唯一宽表投影。外层 withEvent 或 app_tracking_events 的 INSERT IGNORE 已经完成事件级幂等；
	// 这里允许同一事务内分步骤 upsert+update，保证角色和维度快照可以随任意事实补齐，但金额类字段只随已幂等的事实累加一次。
	if _, err := tx.ExecContext(ctx, `
		INSERT INTO stat_social_user_day (
			app_code, stat_tz, stat_day, subject_key, user_id, device_id,
			country_id, region_id, user_role, updated_at_ms
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			user_id = CASE WHEN VALUES(user_id) > 0 THEN VALUES(user_id) ELSE user_id END,
			device_id = CASE WHEN VALUES(device_id) <> '' THEN VALUES(device_id) ELSE device_id END,
			country_id = CASE WHEN VALUES(country_id) > 0 THEN VALUES(country_id) ELSE country_id END,
			region_id = CASE WHEN VALUES(region_id) > 0 THEN VALUES(region_id) ELSE region_id END,
			user_role = CASE WHEN VALUES(user_role) = 'host' OR user_role = 'host' THEN 'host' ELSE 'user' END,
			updated_at_ms = VALUES(updated_at_ms)
	`, app, statTZ, day, subjectKey, normalizeID(delta.UserID), strings.TrimSpace(delta.DeviceID), countryID, regionID, role, delta.UpdatedAt); err != nil {
		return err
	}
	_, err := tx.ExecContext(ctx, `
		UPDATE stat_social_user_day
		SET app_first_open = GREATEST(app_first_open, ?),
		    profile_setup_view = GREATEST(profile_setup_view, ?),
		    registered_success = GREATEST(registered_success, ?),
		    active_user = GREATEST(active_user, ?),
		    room_join_success = GREATEST(room_join_success, ?),
		    room_join_fail = GREATEST(room_join_fail, ?),
		    mic_up_success = GREATEST(mic_up_success, ?),
		    gift_sent = GREATEST(gift_sent, ?),
		    normal_gift_sent = GREATEST(normal_gift_sent, ?),
		    lucky_gift_sent = GREATEST(lucky_gift_sent, ?),
		    super_lucky_gift_sent = GREATEST(super_lucky_gift_sent, ?),
		    recharge_user = GREATEST(recharge_user, ?),
		    first_recharge_user = GREATEST(first_recharge_user, ?),
		    repeat_recharge_user = GREATEST(repeat_recharge_user, ?),
		    game_bet_user = GREATEST(game_bet_user, ?),
		    self_game_bet_user = GREATEST(self_game_bet_user, ?),
		    probability_game_bet_user = GREATEST(probability_game_bet_user, ?),
		    chat_sent = GREATEST(chat_sent, ?),
		    follow_user = GREATEST(follow_user, ?),
		    follow_room = GREATEST(follow_room, ?),
		    gift_panel_open = GREATEST(gift_panel_open, ?),
		    probability_game_panel_open = GREATEST(probability_game_panel_open, ?),
		    app_stay_ms = app_stay_ms + ?,
		    room_stay_ms = room_stay_ms + ?,
		    room_online_ms = room_online_ms + ?,
		    mic_stay_ms = mic_stay_ms + ?,
		    recharge_count = recharge_count + ?,
		    recharge_usd_minor = recharge_usd_minor + ?,
		    google_recharge_usd_minor = google_recharge_usd_minor + ?,
		    third_party_recharge_usd_minor = third_party_recharge_usd_minor + ?,
		    coin_seller_recharge_usd_minor = coin_seller_recharge_usd_minor + ?,
		    first_recharge_usd_minor = first_recharge_usd_minor + ?,
		    repeat_recharge_usd_minor = repeat_recharge_usd_minor + ?,
		    normal_gift_coin = normal_gift_coin + ?,
		    lucky_gift_coin = lucky_gift_coin + ?,
		    super_lucky_gift_coin = super_lucky_gift_coin + ?,
		    lucky_gift_payout_coin = lucky_gift_payout_coin + ?,
		    super_lucky_gift_payout_coin = super_lucky_gift_payout_coin + ?,
		    game_bet_coin = game_bet_coin + ?,
		    game_bet_count = game_bet_count + ?,
		    self_game_bet_coin = self_game_bet_coin + ?,
		    self_game_bet_count = self_game_bet_count + ?,
		    probability_game_bet_coin = probability_game_bet_coin + ?,
		    probability_game_bet_count = probability_game_bet_count + ?,
		    other_consume_coin = other_consume_coin + ?,
		    output_coin = output_coin + ?,
		    updated_at_ms = ?
		WHERE app_code = ? AND stat_tz = ? AND stat_day = ? AND subject_key = ?
	`, flagValue(delta.FirstOpen), flagValue(delta.ProfileSetupView), flagValue(delta.Registered), flagValue(delta.Active), flagValue(delta.RoomJoinOK), flagValue(delta.RoomJoinNG),
		flagValue(delta.MicUp), flagValue(delta.Gift), flagValue(delta.NormalGift), flagValue(delta.LuckyGift), flagValue(delta.SuperGift),
		flagValue(delta.Recharge), flagValue(delta.FirstRecharge), flagValue(delta.RepeatRecharge), flagValue(delta.GameBet), flagValue(delta.SelfGameBet),
		flagValue(delta.ProbabilityBet), flagValue(delta.Chat), flagValue(delta.FollowUser), flagValue(delta.FollowRoom), flagValue(delta.GiftPanel),
		flagValue(delta.ProbabilityPanel), normalizeID(delta.AppStayMS), normalizeID(delta.RoomStayMS), normalizeID(delta.RoomOnlineMS), normalizeID(delta.MicStayMS),
		normalizeID(delta.RechargeCount), normalizeID(delta.RechargeUSDMinor), normalizeID(delta.GoogleRechargeUSDMinor), normalizeID(delta.ThirdPartyRechargeUSDMinor),
		normalizeID(delta.CoinSellerRechargeUSDMinor), normalizeID(delta.FirstRechargeUSDMinor), normalizeID(delta.RepeatRechargeUSDMinor),
		normalizeID(delta.NormalGiftCoin), normalizeID(delta.LuckyGiftCoin), normalizeID(delta.SuperGiftCoin), normalizeID(delta.LuckyGiftPayoutCoin),
		normalizeID(delta.SuperGiftPayoutCoin), normalizeID(delta.GameBetCoin), normalizeID(delta.GameBetCount), normalizeID(delta.SelfGameBetCoin),
		normalizeID(delta.SelfGameBetCount), normalizeID(delta.ProbabilityBetCoin), normalizeID(delta.ProbabilityBetCount), normalizeID(delta.OtherConsumeCoin),
		normalizeID(delta.OutputCoin), delta.UpdatedAt, app, statTZ, day, subjectKey)
	return err
}

func socialSubjectKey(userID int64, deviceID string) string {
	if userID > 0 {
		return "u:" + strconv.FormatInt(userID, 10)
	}
	deviceID = strings.TrimSpace(deviceID)
	if deviceID == "" {
		return ""
	}
	return "d:" + deviceID
}

func flagValue(value int64) int64 {
	if value > 0 {
		return 1
	}
	return 0
}

func normalizeSocialUserRole(value string) string {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "host", "anchor", "owner", "room_owner", "agency", "guild", "guild_leader":
		return "host"
	default:
		return "user"
	}
}

func socialGiftKind(event RoomGiftEvent) (normalFlag, luckyFlag, superFlag int64, normalCoin, luckyCoin, superCoin int64) {
	coin := normalizeID(event.CoinSpent)
	if coin == 0 {
		coin = normalizeID(event.GiftValue)
	}
	switch realRoomRobotGiftKind(event) {
	case "super_lucky":
		return 0, 0, 1, 0, 0, coin
	case "lucky":
		return 0, 1, 0, 0, coin, 0
	default:
		return 1, 0, 0, coin, 0, 0
	}
}

func socialDeltaForAppTrackingEvent(event AppTrackingEvent, scope statDayScope, nowMS int64) (socialUserDayDelta, bool) {
	delta := socialUserDayDelta{
		AppCode:   event.AppCode,
		StatTZ:    scope.tz,
		StatDay:   scope.day,
		UserID:    event.UserID,
		DeviceID:  event.DeviceID,
		CountryID: event.CountryID,
		RegionID:  event.RegionID,
		UpdatedAt: nowMS,
	}
	switch strings.ToLower(strings.TrimSpace(event.EventName)) {
	case "app_first_open":
		delta.FirstOpen = 1
		delta.Active = 1
	case "profile_start":
		// Flutter 仅在未完成资料的新用户真正进入 profile_setup 页面后上报；
		// 用户退出重进可能重复上报，因此读模型只保留主体日级曝光标记。
		delta.ProfileSetupView = 1
	case "home_view":
		delta.Active = 1
	case "app_session":
		delta.Active = 1
		delta.AppStayMS = event.DurationMS
	case "room_join_success":
		delta.Active = 1
		delta.RoomJoinOK = 1
	case "room_join_fail":
		delta.Active = 1
		delta.RoomJoinNG = 1
	case "room_stay":
		delta.Active = 1
		delta.RoomJoinOK = 1
		delta.RoomStayMS = event.DurationMS
	case "mic_up":
		// 只有 ConfirmMicPublishing 已确认麦位进入 publishing 后，Flutter 才会上报 success=true；
		// 权限拒绝或无错误码的异常同样不能因为事件名是 mic_up 就被当成成功。
		if !event.Success {
			return socialUserDayDelta{}, false
		}
		delta.Active = 1
		// 上麦成功隐含进房成功，补置进房标志，保证进房人数 >= 上麦人数。
		delta.RoomJoinOK = 1
		delta.MicUp = 1
	case "send_message":
		if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
			return socialUserDayDelta{}, false
		}
		delta.Active = 1
		delta.Chat = 1
	case "follow_host", "add_friend":
		if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
			return socialUserDayDelta{}, false
		}
		delta.Active = 1
		delta.FollowUser = 1
	case "follow_room":
		if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
			return socialUserDayDelta{}, false
		}
		delta.Active = 1
		delta.FollowRoom = 1
	case "gift_panel_open":
		delta.Active = 1
		delta.GiftPanel = 1
	case "probability_game_panel_open":
		delta.Active = 1
		delta.ProbabilityPanel = 1
	default:
		return socialUserDayDelta{}, false
	}
	return delta, true
}
