package mysql

import (
	"context"
	"encoding/json"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	"strings"
)

// GetWalletOverview 返回钱包首页摘要；开关放在 wallet-service 内，避免 gateway 硬编码账务能力。
func (r *Repository) GetWalletOverview(ctx context.Context, userID int64) (ledger.WalletOverview, error) {

	balances, err := r.GetBalances(ctx, userID, []string{
		ledger.AssetCoin,
		ledger.AssetPoint,
		ledger.AssetPointDiamond,
		ledger.AssetHostSalaryUSD,
		ledger.AssetAgencySalaryUSD,
		ledger.AssetBDSalaryUSD,
		ledger.AssetAdminSalaryUSD,
		ledger.AssetCoinSellerCoin,
		ledger.AssetCoinSellerPoint,
	})
	if err != nil {
		return ledger.WalletOverview{}, err
	}
	return ledger.WalletOverview{
		Balances: balances,
		FeatureFlags: ledger.WalletFeatureFlags{
			RechargeEnabled:        true,
			DiamondExchangeEnabled: false,
		},
	}, nil
}

// GetWalletValueSummary 返回我的页钱包卡片需要的最小金币余额。
func (r *Repository) GetWalletValueSummary(ctx context.Context, userID int64) (ledger.WalletValueSummary, error) {
	balances, err := r.GetBalances(ctx, userID, []string{ledger.AssetCoin})
	if err != nil {
		return ledger.WalletValueSummary{}, err
	}
	if len(balances) == 0 {
		return ledger.WalletValueSummary{}, nil
	}
	return ledger.WalletValueSummary{
		CoinAmount:  balances[0].AvailableAmount,
		UpdatedAtMS: balances[0].UpdatedAtMs,
	}, nil
}

// GetUserGiftWall 读取送礼事务维护的收礼聚合表；它只表达已扣费成功的礼物事实。
func (r *Repository) GetUserGiftWall(ctx context.Context, userID int64) (ledger.UserGiftWall, error) {
	if r == nil || r.db == nil {
		return ledger.UserGiftWall{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	appCode := appcode.FromContext(ctx)
	rows, err := r.db.QueryContext(ctx, `
		SELECT gift_id, gift_name, resource_id, COALESCE(CAST(resource_snapshot_json AS CHAR), '{}'),
		       gift_type_code, COALESCE(CAST(presentation_json AS CHAR), '{}'), gift_count,
		       total_value, total_coin_value, total_diamond_value, total_gift_point, total_heat_value,
		       charge_asset_type, last_price_version, first_received_at_ms, last_received_at_ms, sort_order
		FROM user_gift_wall
		WHERE app_code = ? AND user_id = ?
		ORDER BY sort_order DESC, total_value DESC, last_received_at_ms DESC, gift_id ASC`,
		appCode, userID,
	)
	if err != nil {
		return ledger.UserGiftWall{}, err
	}
	defer rows.Close()

	wall := ledger.UserGiftWall{Items: make([]ledger.GiftWallItem, 0)}
	for rows.Next() {
		var item ledger.GiftWallItem
		if err := rows.Scan(
			&item.GiftID,
			&item.GiftName,
			&item.ResourceID,
			&item.ResourceSnapshotJSON,
			&item.GiftTypeCode,
			&item.PresentationJSON,
			&item.GiftCount,
			&item.TotalValue,
			&item.TotalCoinValue,
			&item.TotalDiamondValue,
			&item.TotalGiftPoint,
			&item.TotalHeatValue,
			&item.ChargeAssetType,
			&item.LastPriceVersion,
			&item.FirstReceivedAtMS,
			&item.LastReceivedAtMS,
			&item.SortOrder,
		); err != nil {
			return ledger.UserGiftWall{}, err
		}
		item.Resource = giftWallResourceFromJSON(item.ResourceSnapshotJSON)
		wall.Items = append(wall.Items, item)
		wall.GiftKindCount++
		wall.GiftTotalCount += item.GiftCount
		wall.TotalValue += item.TotalValue
		wall.TotalCoinValue += item.TotalCoinValue
		wall.TotalDiamondValue += item.TotalDiamondValue
		wall.TotalGiftPoint += item.TotalGiftPoint
		wall.TotalHeatValue += item.TotalHeatValue
	}
	if err := rows.Err(); err != nil {
		return ledger.UserGiftWall{}, err
	}
	return wall, nil
}

func giftWallResourceFromJSON(raw string) ledger.GiftWallResource {
	var resource ledger.GiftWallResource
	if strings.TrimSpace(raw) == "" {
		return resource
	}

	_ = json.Unmarshal([]byte(raw), &resource)
	return resource
}

// GetDiamondExchangeConfig 返回后台配置的钻石兑换规则，未配置时返回空列表而不是 gateway 默认值。
func (r *Repository) GetDiamondExchangeConfig(ctx context.Context, _ int64) ([]ledger.DiamondExchangeRule, error) {
	if r == nil || r.db == nil {
		return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	return []ledger.DiamondExchangeRule{}, nil
}

// ListWalletTransactions 分页读取当前用户钱包分录，并带出交易业务类型。
func (r *Repository) ListWalletTransactions(ctx context.Context, query ledger.ListWalletTransactionsQuery) ([]ledger.WalletTransaction, int64, error) {
	if r == nil || r.db == nil {
		return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, query.AppCode)
	query.AppCode = appcode.FromContext(ctx)
	query.Page, query.PageSize = normalizePage(query.Page, query.PageSize)
	where := `WHERE e.app_code = ? AND e.user_id = ?`
	args := []any{query.AppCode, query.UserID}
	if strings.TrimSpace(query.AssetType) != "" {
		where += ` AND e.asset_type = ?`
		args = append(args, query.AssetType)
	}
	if strings.EqualFold(strings.TrimSpace(query.AssetType), ledger.AssetPointDiamond) {
		// 审核通过的 settle 只把 frozen 扣成 0，用户在首次 freeze 时已经看到一笔提现；
		// 在 SQL 分页条件内隐藏内部完成分录，保证 items 与 total 完全一致，release 仍单独展示为退回。
		where += ` AND wt.biz_type <> ?`
		args = append(args, bizTypeSalaryWithdrawalSettle)
	}
	if query.StartAtMS > 0 {
		where += ` AND e.created_at_ms >= ? AND e.created_at_ms < ?`
		args = append(args, query.StartAtMS, query.EndAtMS)
	}

	var total int64
	if err := r.db.QueryRowContext(ctx, `
		SELECT COUNT(*)
		FROM wallet_entries e
		JOIN wallet_transactions wt ON wt.app_code = e.app_code AND wt.transaction_id = e.transaction_id
		`+where,
		args...,
	).Scan(&total); err != nil {
		return nil, 0, err
	}

	rows, err := r.db.QueryContext(ctx, `
		SELECT e.entry_id, e.transaction_id, wt.biz_type, e.asset_type,
		       e.available_delta, e.frozen_delta, e.available_after, e.frozen_after,
		       e.counterparty_user_id, e.room_id, e.created_at_ms,
		       COALESCE(CAST(wt.metadata_json AS CHAR), '{}')
		FROM wallet_entries e
		JOIN wallet_transactions wt ON wt.app_code = e.app_code AND wt.transaction_id = e.transaction_id
		`+where+`
		ORDER BY e.created_at_ms DESC, e.entry_id DESC
		LIMIT ? OFFSET ?`,
		append(args, query.PageSize, resourceOffset(query.Page, query.PageSize))...,
	)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	items := make([]ledger.WalletTransaction, 0, query.PageSize)
	for rows.Next() {
		var item ledger.WalletTransaction
		var metadataJSON string
		if err := rows.Scan(
			&item.EntryID,
			&item.TransactionID,
			&item.BizType,
			&item.AssetType,
			&item.AvailableDelta,
			&item.FrozenDelta,
			&item.AvailableAfter,
			&item.FrozenAfter,
			&item.CounterpartyUserID,
			&item.RoomID,
			&item.CreatedAtMS,
			&metadataJSON,
		); err != nil {
			return nil, 0, err
		}
		// H5 只需要展示用户实际转给币商的金额；这里统一从交易 metadata 提取安全字段，避免 gateway 或前端理解各类内部 JSON。
		item.TransferUSDMinor, item.TransferCurrencyCode = walletTransactionTransferAmount(item, metadataJSON)
		item.EntryRole, item.HostBaseAmount, item.AgencyAmount = walletTransactionEntryComponents(query.UserID, item, metadataJSON)
		item.PolicyID, item.PolicyVersion, item.RatioPointAmount, item.RatioCoinAmount,
			item.GrossUSDMinor, item.FeeUSDMinor, item.NetUSDMinor, item.WithdrawFeeBPS = walletTransactionPolicySnapshot(item.BizType, metadataJSON)
		items = append(items, item)
	}
	return items, total, rows.Err()
}

// walletTransactionPolicySnapshot 把各类 POINT_DIAMOND 交易首次落库的政策字段归一成稳定历史契约。
// Gateway 只转成字符串，不再理解 gift/exchange/seller/withdraw 各自不同的 metadata key。
func walletTransactionPolicySnapshot(bizType string, metadataJSON string) (uint64, uint64, int64, int64, int64, int64, int64, int64) {
	var metadata struct {
		HostSalaryPolicyID      uint64 `json:"host_salary_policy_id"`
		HostSalaryPolicyVersion uint64 `json:"host_salary_policy_version"`
		PointDiamondsPerUSD     int64  `json:"point_diamonds_per_usd"`
		PolicyID                uint64 `json:"policy_id"`
		PolicyVersion           uint64 `json:"policy_version"`
		PointPolicyID           uint64 `json:"point_policy_id"`
		PointPolicyVersion      uint64 `json:"point_policy_version"`
		RatioPointAmount        int64  `json:"ratio_point_amount"`
		RatioCoinAmount         int64  `json:"ratio_coin_amount"`
		RatioSellerCoinAmount   int64  `json:"ratio_seller_coin_amount"`
		PointsPerUSD            int64  `json:"points_per_usd"`
		CoinsPerUSD             int64  `json:"coins_per_usd"`
		GrossUSDMinor           int64  `json:"gross_usd_minor"`
		FeeUSDMinor             int64  `json:"fee_usd_minor"`
		NetUSDMinor             int64  `json:"net_usd_minor"`
		WithdrawFeeBPS          int64  `json:"withdraw_fee_bps"`
		PointWithdrawFeeBPS     int64  `json:"point_withdraw_fee_bps"`
	}
	if strings.TrimSpace(metadataJSON) == "" || json.Unmarshal([]byte(metadataJSON), &metadata) != nil {
		return 0, 0, 0, 0, 0, 0, 0, 0
	}
	switch strings.TrimSpace(bizType) {
	case bizTypeGiftDebit, bizTypeDirectGiftDebit:
		return metadata.HostSalaryPolicyID, metadata.HostSalaryPolicyVersion, metadata.PointDiamondsPerUSD, metadata.CoinsPerUSD, 0, 0, 0, int64(0)
	case bizTypePointExchangeToCoin:
		return metadata.PolicyID, metadata.PolicyVersion, metadata.RatioPointAmount, metadata.RatioCoinAmount, 0, 0, 0, 0
	case bizTypePointTransferToCoinSeller:
		return metadata.PolicyID, metadata.PolicyVersion, metadata.RatioPointAmount, metadata.RatioSellerCoinAmount,
			metadata.GrossUSDMinor, metadata.FeeUSDMinor, metadata.NetUSDMinor, metadata.WithdrawFeeBPS
	case bizTypeSalaryWithdrawalFreeze, bizTypeSalaryWithdrawalSettle, bizTypeSalaryWithdrawalRelease:
		return metadata.PointPolicyID, metadata.PointPolicyVersion, metadata.PointsPerUSD, metadata.CoinsPerUSD,
			metadata.GrossUSDMinor, metadata.FeeUSDMinor, metadata.NetUSDMinor, metadata.PointWithdrawFeeBPS
	default:
		return 0, 0, 0, 0, 0, 0, 0, 0
	}
}

func walletTransactionEntryComponents(entryUserID int64, item ledger.WalletTransaction, metadataJSON string) (string, int64, int64) {
	switch item.BizType {
	case bizTypeGiftDebit, bizTypeDirectGiftDebit:
		var metadata struct {
			TargetUserID      int64 `json:"target_user_id"`
			AgencyOwnerUserID int64 `json:"target_agency_owner_user_id"`
			HostBaseAmount    int64 `json:"host_base_amount"`
			AgencyAmount      int64 `json:"agency_amount"`
		}
		if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil {
			return "gift_income", 0, 0
		}
		if entryUserID == metadata.TargetUserID && entryUserID == metadata.AgencyOwnerUserID && metadata.HostBaseAmount > 0 && metadata.AgencyAmount > 0 {
			// 同一钱包分录的 available_delta 是两部分之和；同时返回拆分值，客户端不得把整笔误标为 Agency 分成。
			return "host_gift_income_and_agency_share", metadata.HostBaseAmount, metadata.AgencyAmount
		}
		if entryUserID == metadata.TargetUserID && metadata.HostBaseAmount > 0 {
			return "host_gift_income", metadata.HostBaseAmount, 0
		}
		if entryUserID == metadata.AgencyOwnerUserID && metadata.AgencyAmount > 0 {
			return "agency_gift_share", 0, metadata.AgencyAmount
		}
		return "gift_income", 0, 0
	case bizTypePointExchangeToCoin:
		return "exchange_to_coins", 0, 0
	case bizTypePointTransferToCoinSeller:
		return "coin_seller_withdrawal", 0, 0
	case bizTypeSalaryWithdrawalFreeze:
		return "platform_withdrawal", 0, 0
	case bizTypeSalaryWithdrawalSettle:
		return "platform_withdrawal_settled", 0, 0
	case bizTypeSalaryWithdrawalRelease:
		return "withdrawal_release", 0, 0
	default:
		return item.BizType, 0, 0
	}
}

// ListHostPeriodDiamondTransactions 从主播周期收入明细分页读取钻石历史；按 app/user/time 的复合索引完成筛选和倒序，避免日期查询退化为全表扫描。
func (r *Repository) ListHostPeriodDiamondTransactions(ctx context.Context, query ledger.ListWalletTransactionsQuery) ([]ledger.WalletTransaction, int64, error) {
	if r == nil || r.db == nil {
		return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, query.AppCode)
	query.AppCode = appcode.FromContext(ctx)
	query.Page, query.PageSize = normalizePage(query.Page, query.PageSize)
	where := `WHERE app_code = ? AND user_id = ?`
	args := []any{query.AppCode, query.UserID}
	if query.StartAtMS > 0 {
		where += ` AND created_at_ms >= ? AND created_at_ms < ?`
		args = append(args, query.StartAtMS, query.EndAtMS)
	}

	var total int64
	if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM host_period_diamond_entries `+where, args...).Scan(&total); err != nil {
		return nil, 0, err
	}

	rows, err := r.db.QueryContext(ctx, `
		SELECT entry_id, transaction_id, diamond_delta, diamond_after,
		       sender_user_id, room_id, created_at_ms
		FROM host_period_diamond_entries
		`+where+`
		ORDER BY created_at_ms DESC, entry_id DESC
		LIMIT ? OFFSET ?`,
		append(args, query.PageSize, resourceOffset(query.Page, query.PageSize))...,
	)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	items := make([]ledger.WalletTransaction, 0, query.PageSize)
	for rows.Next() {
		var item ledger.WalletTransaction
		if err := rows.Scan(
			&item.EntryID,
			&item.TransactionID,
			&item.AvailableDelta,
			&item.AvailableAfter,
			&item.CounterpartyUserID,
			&item.RoomID,
			&item.CreatedAtMS,
		); err != nil {
			return nil, 0, err
		}
		item.BizType = "host_period_diamond_income"
		item.AssetType = ledger.AssetHostPeriodDiamond
		items = append(items, item)
	}
	return items, total, rows.Err()
}

// walletTransactionTransferAmount 把工资转币商、H5 币商进货和后台进货的不同 metadata 统一成展示金额；解析失败时返回空值，避免流水页展示错误金额。
func walletTransactionTransferAmount(item ledger.WalletTransaction, metadataJSON string) (int64, string) {
	var metadata struct {
		SalaryUSDMinor       int64  `json:"salary_usd_minor"`
		RechargeUSDMinor     int64  `json:"recharge_usd_minor"`
		PaidAmountMicro      int64  `json:"paid_amount_micro"`
		PaidCurrencyCode     string `json:"paid_currency_code"`
		RechargeCurrencyCode string `json:"recharge_currency_code"`
		GrossUSDMinor        int64  `json:"gross_usd_minor"`
	}
	if strings.TrimSpace(metadataJSON) == "" {
		return 0, ""
	}
	if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil {
		return 0, ""
	}
	switch strings.TrimSpace(item.BizType) {
	case bizTypeSalaryTransferToCoinSeller, bizTypeSalaryWithdrawalFreeze, bizTypeSalaryWithdrawalSettle, bizTypeSalaryWithdrawalRelease:
		if ledger.ValidPointWithdrawalAssetType(item.AssetType) {
			// POINT/POINT_DIAMOND 的 salary_usd_minor 字段承载冻结积分数，不是美元分；只有首次
			// 政策快照中的 gross_usd_minor 才能用于法币展示，历史 POINT 没有该值时保持空。
			if metadata.GrossUSDMinor > 0 {
				return metadata.GrossUSDMinor, "USD"
			}
			return 0, ""
		}
		if metadata.SalaryUSDMinor > 0 {
			return metadata.SalaryUSDMinor, "USD"
		}
	case "coin_seller_recharge":
		if metadata.RechargeUSDMinor > 0 {
			return metadata.RechargeUSDMinor, walletTransactionCurrency(metadata.RechargeCurrencyCode, "USD")
		}
		if metadata.PaidAmountMicro > 0 {
			return metadata.PaidAmountMicro / 10000, walletTransactionCurrency(metadata.PaidCurrencyCode, "USD")
		}
	case bizTypeCoinSellerStockPurchase, bizTypeCoinSellerStockDeduction:
		if metadata.PaidAmountMicro != 0 {
			return metadata.PaidAmountMicro / 10000, walletTransactionCurrency(metadata.PaidCurrencyCode, "USD")
		}
	}
	if metadata.SalaryUSDMinor > 0 {
		return metadata.SalaryUSDMinor, "USD"
	}
	if metadata.RechargeUSDMinor > 0 {
		return metadata.RechargeUSDMinor, walletTransactionCurrency(metadata.RechargeCurrencyCode, "USD")
	}
	return 0, ""
}

func walletTransactionCurrency(value string, fallback string) string {
	value = strings.ToUpper(strings.TrimSpace(value))
	if value != "" {
		return value
	}
	return fallback
}
