package mysql

import (
	"context"
	"sort"
	"time"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
)

const dayMS = int64(86_400_000)

// GetRechargeBillOverview 汇总财务概览所需的按日趋势、区域分布与谷歌实付覆盖度；筛选口径与账单列表一致。
func (r *Repository) GetRechargeBillOverview(ctx context.Context, query ledger.ListRechargeBillsQuery, tzOffsetMinutes int32) (ledger.RechargeBillOverview, error) {
	if r == nil || r.db == nil {
		return ledger.RechargeBillOverview{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, query.AppCode)
	query = normalizeRechargeBillsQuery(query)
	query.AppCode = appcode.FromContext(ctx)
	tzOffsetMS := int64(tzOffsetMinutes) * 60_000

	overview := ledger.RechargeBillOverview{}
	dailyBuckets := map[int64]*ledger.RechargeBillDailyBucket{}
	regionBuckets := map[int64]*ledger.RechargeBillRegionBucket{}

	includeRecords, includeStock := rechargeBillBranches(query)
	if includeRecords {
		if err := r.overviewRecordDaily(ctx, query, tzOffsetMS, dailyBuckets); err != nil {
			return ledger.RechargeBillOverview{}, err
		}
		if err := r.overviewRecordRegions(ctx, query, regionBuckets); err != nil {
			return ledger.RechargeBillOverview{}, err
		}
	}
	if includeStock {
		if err := r.overviewStockDaily(ctx, query, tzOffsetMS, dailyBuckets); err != nil {
			return ledger.RechargeBillOverview{}, err
		}
		if err := r.overviewStockRegions(ctx, query, regionBuckets); err != nil {
			return ledger.RechargeBillOverview{}, err
		}
	}
	if includeRecords && (query.RechargeType == "" || query.RechargeType == bizTypeGooglePlayRecharge) {
		stats, err := r.overviewGooglePaidStats(ctx, query)
		if err != nil {
			return ledger.RechargeBillOverview{}, err
		}
		overview.GooglePaid = stats
	}
	// 用户提现（转币商）与充值渠道筛选无关，始终按 App/时间/区域口径统计。
	salaryTransfer, err := r.overviewSalaryTransferStats(ctx, query)
	if err != nil {
		return ledger.RechargeBillOverview{}, err
	}
	overview.SalaryTransfer = salaryTransfer

	dayIndexes := make([]int64, 0, len(dailyBuckets))
	for dayIndex := range dailyBuckets {
		dayIndexes = append(dayIndexes, dayIndex)
	}
	sort.Slice(dayIndexes, func(i, j int) bool { return dayIndexes[i] < dayIndexes[j] })
	for _, dayIndex := range dayIndexes {
		bucket := dailyBuckets[dayIndex]
		// day_index 已按查询时区平移，再按 UTC 取日期即得到该时区的日历日。
		bucket.Date = time.UnixMilli(dayIndex * dayMS).UTC().Format("2006-01-02")
		overview.Daily = append(overview.Daily, *bucket)
	}
	for _, bucket := range regionBuckets {
		overview.Regions = append(overview.Regions, *bucket)
	}
	sort.Slice(overview.Regions, func(i, j int) bool {
		return overview.Regions[i].USDMinorAmount > overview.Regions[j].USDMinorAmount
	})
	return overview, nil
}

func (r *Repository) overviewRecordDaily(ctx context.Context, query ledger.ListRechargeBillsQuery, tzOffsetMS int64, buckets map[int64]*ledger.RechargeBillDailyBucket) error {
	where, args := rechargeBillsWhereSQL(query)
	rows, err := r.db.QueryContext(ctx, `
		SELECT FLOOR((rr.created_at_ms + ?) / 86400000) AS day_index,
		       COALESCE(SUM(CASE WHEN wt.biz_type = ? THEN rr.usd_minor_amount ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN wt.biz_type = ? THEN rr.coin_amount ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN wt.biz_type <> ? THEN rr.usd_minor_amount ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN wt.biz_type <> ? THEN rr.coin_amount ELSE 0 END), 0)
		FROM wallet_recharge_records rr
		JOIN wallet_transactions wt ON wt.app_code = rr.app_code AND wt.transaction_id = rr.transaction_id
		`+where+`
		GROUP BY day_index`,
		append([]any{tzOffsetMS, bizTypeGooglePlayRecharge, bizTypeGooglePlayRecharge, bizTypeGooglePlayRecharge, bizTypeGooglePlayRecharge}, args...)...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		var dayIndex, googleUSD, googleCoin, thirdUSD, thirdCoin int64
		if err := rows.Scan(&dayIndex, &googleUSD, &googleCoin, &thirdUSD, &thirdCoin); err != nil {
			return err
		}
		bucket := overviewDailyBucket(buckets, dayIndex)
		bucket.GoogleUSDMinor += googleUSD
		bucket.GoogleCoinAmount += googleCoin
		bucket.ThirdPartyUSDMinor += thirdUSD
		bucket.ThirdPartyCoinAmount += thirdCoin
	}
	return rows.Err()
}

func (r *Repository) overviewStockDaily(ctx context.Context, query ledger.ListRechargeBillsQuery, tzOffsetMS int64, buckets map[int64]*ledger.RechargeBillDailyBucket) error {
	where, args := rechargeBillStockWhereSQL(query)
	rows, err := r.db.QueryContext(ctx, `
		SELECT FLOOR((sr.created_at_ms + ?) / 86400000) AS day_index,
		       COALESCE(SUM(CASE WHEN sr.paid_currency_code IN ('USDT', 'USD') THEN sr.paid_amount_micro DIV 10000 ELSE 0 END), 0),
		       COALESCE(SUM(sr.coin_amount), 0)
		FROM coin_seller_stock_records sr
		JOIN wallet_transactions wt ON wt.app_code = sr.app_code AND wt.transaction_id = sr.transaction_id
		`+where+`
		GROUP BY day_index`,
		append([]any{tzOffsetMS}, args...)...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		var dayIndex, usdMinor, coinAmount int64
		if err := rows.Scan(&dayIndex, &usdMinor, &coinAmount); err != nil {
			return err
		}
		bucket := overviewDailyBucket(buckets, dayIndex)
		bucket.CoinSellerUSDMinor += usdMinor
		bucket.CoinSellerCoinAmount += coinAmount
	}
	return rows.Err()
}

func overviewDailyBucket(buckets map[int64]*ledger.RechargeBillDailyBucket, dayIndex int64) *ledger.RechargeBillDailyBucket {
	bucket, ok := buckets[dayIndex]
	if !ok {
		bucket = &ledger.RechargeBillDailyBucket{}
		buckets[dayIndex] = bucket
	}
	return bucket
}

func (r *Repository) overviewRecordRegions(ctx context.Context, query ledger.ListRechargeBillsQuery, buckets map[int64]*ledger.RechargeBillRegionBucket) error {
	where, args := rechargeBillsWhereSQL(query)
	rows, err := r.db.QueryContext(ctx, `
		SELECT rr.target_region_id, COUNT(*), COALESCE(SUM(rr.usd_minor_amount), 0)
		FROM wallet_recharge_records rr
		JOIN wallet_transactions wt ON wt.app_code = rr.app_code AND wt.transaction_id = rr.transaction_id
		`+where+`
		GROUP BY rr.target_region_id`,
		args...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()
	return scanOverviewRegionRows(rows, buckets)
}

func (r *Repository) overviewStockRegions(ctx context.Context, query ledger.ListRechargeBillsQuery, buckets map[int64]*ledger.RechargeBillRegionBucket) error {
	where, args := rechargeBillStockWhereSQL(query)
	rows, err := r.db.QueryContext(ctx, `
		SELECT COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(sr.metadata_json, '$.seller_region_id')) AS SIGNED), 0),
		       COUNT(*),
		       COALESCE(SUM(CASE WHEN sr.paid_currency_code IN ('USDT', 'USD') THEN sr.paid_amount_micro DIV 10000 ELSE 0 END), 0)
		FROM coin_seller_stock_records sr
		JOIN wallet_transactions wt ON wt.app_code = sr.app_code AND wt.transaction_id = sr.transaction_id
		`+where+`
		GROUP BY 1`,
		args...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()
	return scanOverviewRegionRows(rows, buckets)
}

type overviewRegionRows interface {
	Next() bool
	Scan(dest ...any) error
	Err() error
}

func scanOverviewRegionRows(rows overviewRegionRows, buckets map[int64]*ledger.RechargeBillRegionBucket) error {
	for rows.Next() {
		var regionID, billCount, usdMinor int64
		if err := rows.Scan(&regionID, &billCount, &usdMinor); err != nil {
			return err
		}
		if regionID <= 0 {
			continue
		}
		bucket, ok := buckets[regionID]
		if !ok {
			bucket = &ledger.RechargeBillRegionBucket{RegionID: regionID}
			buckets[regionID] = bucket
		}
		bucket.BillCount += billCount
		bucket.USDMinorAmount += usdMinor
	}
	return rows.Err()
}

// overviewSalaryTransferStats 汇总用户工资转币商的 USDT：金额在交易 metadata 的 salary_usd_minor，
// 该业务量级小（人工触发），JSON 提取聚合不构成性能压力。
func (r *Repository) overviewSalaryTransferStats(ctx context.Context, query ledger.ListRechargeBillsQuery) (ledger.RechargeBillSalaryTransferStats, error) {
	where := `WHERE app_code = ? AND biz_type = ?`
	args := []any{query.AppCode, bizTypeSalaryTransferToCoinSeller}
	if query.Status != "" {
		where += ` AND status = ?`
		args = append(args, query.Status)
	}
	if query.RegionID > 0 {
		where += ` AND COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.region_id')) AS SIGNED), 0) = ?`
		args = append(args, query.RegionID)
	}
	if query.StartAtMS > 0 {
		where += ` AND created_at_ms >= ?`
		args = append(args, query.StartAtMS)
	}
	if query.EndAtMS > 0 {
		where += ` AND created_at_ms < ?`
		args = append(args, query.EndAtMS)
	}
	var stats ledger.RechargeBillSalaryTransferStats
	err := r.db.QueryRowContext(ctx, `
		SELECT COUNT(*), COALESCE(SUM(COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.salary_usd_minor')) AS SIGNED), 0)), 0)
		FROM wallet_transactions
		`+where,
		args...,
	).Scan(&stats.TransferCount, &stats.TransferUSDMinor)
	if err != nil {
		return ledger.RechargeBillSalaryTransferStats{}, err
	}
	return stats, nil
}

// overviewGooglePaidStats 统计谷歌实付同步覆盖度，并把已同步账单的税/费/净收按实付占比折算到 USD 口径。
// 估算只覆盖“已同步且净收入有效”的账单；负差额按 0 处理，避免退款单把手续费冲成负数。
func (r *Repository) overviewGooglePaidStats(ctx context.Context, query ledger.ListRechargeBillsQuery) (ledger.RechargeBillGooglePaidStats, error) {
	googleQuery := query
	googleQuery.RechargeType = bizTypeGooglePlayRecharge
	googleQuery.PaidState = ""
	where, args := rechargeBillsWhereSQL(googleQuery)
	row := r.db.QueryRowContext(ctx, `
		SELECT COUNT(*),
		       COALESCE(SUM(CASE WHEN COALESCE(po.paid_synced_at_ms, 0) > 0 THEN 1 ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN po.paid_amount_micro > 0 AND po.paid_net_micro > 0 THEN rr.usd_minor_amount ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN po.paid_amount_micro > 0 AND po.paid_net_micro > 0
		           THEN ROUND(rr.usd_minor_amount * GREATEST(po.paid_amount_micro - po.paid_net_micro - po.paid_tax_micro, 0) / po.paid_amount_micro) ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN po.paid_amount_micro > 0 AND po.paid_net_micro > 0
		           THEN ROUND(rr.usd_minor_amount * po.paid_tax_micro / po.paid_amount_micro) ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN po.paid_amount_micro > 0 AND po.paid_net_micro > 0
		           THEN ROUND(rr.usd_minor_amount * po.paid_net_micro / po.paid_amount_micro) ELSE 0 END), 0)
		FROM wallet_recharge_records rr
		JOIN wallet_transactions wt ON wt.app_code = rr.app_code AND wt.transaction_id = rr.transaction_id
		LEFT JOIN payment_orders po ON po.app_code = rr.app_code AND po.wallet_transaction_id = rr.transaction_id
		`+where,
		args...,
	)
	var stats ledger.RechargeBillGooglePaidStats
	if err := row.Scan(
		&stats.GoogleBillCount,
		&stats.SyncedCount,
		&stats.CoveredUSDMinor,
		&stats.EstFeeUSDMinor,
		&stats.EstTaxUSDMinor,
		&stats.EstNetUSDMinor,
	); err != nil {
		return ledger.RechargeBillGooglePaidStats{}, err
	}
	stats.UnsyncedCount = stats.GoogleBillCount - stats.SyncedCount
	return stats, nil
}
