package databi

import (
	"encoding/json"
	"math"
	"strconv"
	"strings"
)

// additiveMetricKeys 是各统计源（statistics-service / dashboard 外接源）国家行里可以线性相加的指标；
// 国家按用户归属互斥，所以计数与金额按国家求和即为区域/整体口径，比例类字段一律丢弃后重算。
var additiveMetricKeys = []string{
	"new_users",
	"registered_users",
	"registrations",
	"active_users",
	"paid_users",
	"new_paid_users",
	"recharge_users",
	"recharge_usd_minor",
	"total_recharge_usd_minor",
	"user_recharge_usd_minor",
	"new_user_recharge_usd_minor",
	"coin_seller_recharge_usd_minor",
	"coin_seller_stock_coin",
	"coin_seller_transfer_coin",
	"new_dealer_recharge_usd_minor",
	"mifapay_recharge_usd_minor",
	"google_recharge_usd_minor",
	"coin_total",
	"consumed_coin",
	"output_coin",
	"platform_grant_coin",
	"manual_grant_coin",
	"salary_usd_minor",
	"salary_exchange_usd_minor",
	"salary_transfer_usd_minor",
	"salary_transfer_coin",
	"mic_online_ms",
	"mic_online_users",
	"gift_coin_spent",
	"lucky_gift_turnover",
	"lucky_gift_payout",
	"lucky_gift_payers",
	"lucky_gift_anchor_share",
	"room_lucky_gift_turnover",
	"game_turnover",
	"game_payout",
	"game_refund",
	"game_players",
	"super_lucky_gift_turnover",
	"super_lucky_gift_payout",
	"d1_retention_users",
	"d1_retention_base_users",
	"d7_retention_users",
	"d7_retention_base_users",
	"d30_retention_users",
	"d30_retention_base_users",
}

type metricAccumulator struct {
	values  map[string]int64
	present map[string]bool
}

func newMetricAccumulator() *metricAccumulator {
	return &metricAccumulator{values: map[string]int64{}, present: map[string]bool{}}
}

func (acc *metricAccumulator) addRow(row map[string]any) {
	if row == nil {
		return
	}
	for _, key := range additiveMetricKeys {
		raw, ok := row[key]
		if !ok || raw == nil {
			continue
		}
		value, ok := metricInt64(raw)
		if !ok {
			continue
		}
		acc.values[key] += value
		acc.present[key] = true
	}
}

// toMap 输出与统计源同名的指标字段：可加字段直接给累加值，派生字段（比例/均值/利润）统一重算；
// 某个字段在所有来源行里都缺失（legacy 口径未接入）时输出 nil，前端照旧显示 "--"。
func (acc *metricAccumulator) toMap() map[string]any {
	out := map[string]any{}
	for _, key := range additiveMetricKeys {
		if acc.present[key] {
			out[key] = acc.values[key]
		} else {
			out[key] = nil
		}
	}

	recharge, hasRecharge := acc.value("recharge_usd_minor")
	active, hasActive := acc.value("active_users")
	paid, hasPaid := acc.value("paid_users")
	rechargeUsers, hasRechargeUsers := acc.value("recharge_users")

	out["arpu_usd_minor"] = nilUnless(hasRecharge && hasActive, divideRound(recharge, active))
	out["arppu_usd_minor"] = nilUnless(hasRecharge && hasPaid, divideRound(recharge, paid))
	out["payer_rate"] = nilUnless(hasPaid && hasActive, safeRatio(paid, active))
	out["paid_conversion_rate"] = nilUnless(hasPaid && hasActive, safeRatio(paid, active))
	out["recharge_conversion_rate"] = nilUnless(hasRechargeUsers && hasActive, safeRatio(rechargeUsers, active))

	gameTurnover, hasGameTurnover := acc.value("game_turnover")
	gamePayout, hasGamePayout := acc.value("game_payout")
	if hasGameTurnover && hasGamePayout {
		profit := gameTurnover - gamePayout
		out["game_profit"] = profit
		out["game_profit_rate"] = safeRatio(profit, gameTurnover)
	} else {
		out["game_profit"] = nil
		out["game_profit_rate"] = nil
	}

	luckyTurnover, hasLuckyTurnover := acc.value("lucky_gift_turnover")
	luckyPayout, hasLuckyPayout := acc.value("lucky_gift_payout")
	if hasLuckyTurnover && hasLuckyPayout {
		profit := luckyTurnover - luckyPayout
		out["lucky_gift_profit"] = profit
		out["lucky_gift_payout_rate"] = safeRatio(luckyPayout, luckyTurnover)
		out["lucky_gift_profit_rate"] = safeRatio(profit, luckyTurnover)
	} else {
		out["lucky_gift_profit"] = nil
		out["lucky_gift_payout_rate"] = nil
		out["lucky_gift_profit_rate"] = nil
	}

	superTurnover, hasSuperTurnover := acc.value("super_lucky_gift_turnover")
	superPayout, hasSuperPayout := acc.value("super_lucky_gift_payout")
	if hasSuperTurnover && hasSuperPayout {
		profit := superTurnover - superPayout
		out["super_lucky_gift_profit"] = profit
		out["super_lucky_gift_profit_rate"] = safeRatio(profit, superTurnover)
	} else {
		out["super_lucky_gift_profit"] = nil
		out["super_lucky_gift_profit_rate"] = nil
	}

	consumed, hasConsumed := acc.value("consumed_coin")
	output, hasOutput := acc.value("output_coin")
	out["consume_output_ratio"] = nilUnless(hasConsumed && hasOutput, safeRatio(consumed, output))
	out["consume_output_delta"] = nilUnless(hasConsumed && hasOutput, consumed-output)

	micMS, hasMicMS := acc.value("mic_online_ms")
	micUsers, hasMicUsers := acc.value("mic_online_users")
	out["avg_mic_online_ms"] = nilUnless(hasMicMS && hasMicUsers, divideRound(micMS, micUsers))

	for _, day := range []string{"d1", "d7", "d30"} {
		users, hasUsers := acc.value(day + "_retention_users")
		base, hasBase := acc.value(day + "_retention_base_users")
		out[day+"_retention_rate"] = nilUnless(hasUsers && hasBase, safeRatio(users, base))
	}
	return out
}

func (acc *metricAccumulator) value(key string) (int64, bool) {
	return acc.values[key], acc.present[key]
}

func nilUnless(condition bool, value any) any {
	if !condition {
		return nil
	}
	return value
}

func divideRound(numerator int64, denominator int64) int64 {
	if denominator == 0 {
		return 0
	}
	return int64(math.Round(float64(numerator) / float64(denominator)))
}

func safeRatio(numerator int64, denominator int64) float64 {
	if denominator == 0 {
		return 0
	}
	return float64(numerator) / float64(denominator)
}

func metricInt64(value any) (int64, bool) {
	switch typed := value.(type) {
	case nil:
		return 0, false
	case int64:
		return typed, true
	case int32:
		return int64(typed), true
	case int:
		return int64(typed), true
	case uint:
		return int64(typed), true
	case uint64:
		return int64(typed), true
	case float64:
		return int64(math.Round(typed)), true
	case float32:
		return int64(math.Round(float64(typed))), true
	case json.Number:
		if parsed, err := typed.Int64(); err == nil {
			return parsed, true
		}
		if parsed, err := typed.Float64(); err == nil {
			return int64(math.Round(parsed)), true
		}
		return 0, false
	case string:
		trimmed := strings.TrimSpace(typed)
		if trimmed == "" {
			return 0, false
		}
		if parsed, err := strconv.ParseInt(trimmed, 10, 64); err == nil {
			return parsed, true
		}
		if parsed, err := strconv.ParseFloat(trimmed, 64); err == nil {
			return int64(math.Round(parsed)), true
		}
		return 0, false
	default:
		return 0, false
	}
}

func anyRowSlice(value any) []map[string]any {
	switch typed := value.(type) {
	case []map[string]any:
		return typed
	case []any:
		out := make([]map[string]any, 0, len(typed))
		for _, item := range typed {
			if row, ok := item.(map[string]any); ok {
				out = append(out, row)
			}
		}
		return out
	default:
		return nil
	}
}

func rowString(row map[string]any, keys ...string) string {
	for _, key := range keys {
		if raw, ok := row[key]; ok && raw != nil {
			if text, ok := raw.(string); ok {
				if trimmed := strings.TrimSpace(text); trimmed != "" {
					return trimmed
				}
			}
		}
	}
	return ""
}

func rowInt64(row map[string]any, keys ...string) (int64, bool) {
	for _, key := range keys {
		if raw, ok := row[key]; ok && raw != nil {
			if value, ok := metricInt64(raw); ok {
				return value, true
			}
		}
	}
	return 0, false
}
