package payment

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"strconv"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/response"
	walletv1 "hyapp.local/api/proto/wallet/v1"

	"github.com/gin-gonic/gin"
)

const (
	rechargeBillExportPageSize = 500
	// rechargeBillExportMaxRows 限制单次导出行数，避免财务误选超大时间范围拖垮 admin 与 wallet-service。
	rechargeBillExportMaxRows = 50_000
)

// rechargeBillExportTimezone 与页面展示一致，导出统一使用中国时区时间。
var rechargeBillExportTimezone = time.FixedZone("GMT+8", 8*3600)

// ExportRechargeBills 按当前筛选口径导出充值明细 CSV；hyapp 与 legacy 账单源共用同一套列。
func (h *Handler) ExportRechargeBills(c *gin.Context) {
	appCode := appctx.FromContext(c.Request.Context())
	bills, ok := h.collectRechargeBillsForExport(c, appCode)
	if !ok {
		return
	}
	truncated := len(bills) >= rechargeBillExportMaxRows

	regionNames := map[int64]string{}
	if regions, err := h.listRechargeBillRegions(c.Request.Context(), appCode); err == nil {
		for _, region := range regions {
			name := region.Name
			if name == "" {
				name = region.RegionCode
			}
			regionNames[region.RegionID] = name
		}
	}

	buffer := &bytes.Buffer{}
	// UTF-8 BOM 让 Excel 直接双击打开时正确识别中文。
	buffer.Write([]byte{0xEF, 0xBB, 0xBF})
	writer := csv.NewWriter(buffer)
	_ = writer.Write([]string{
		"充值时间", "APP", "充值来源", "交易号", "业务单号", "外部订单号",
		"金币数量", "美金金额(USD)", "账单币种", "账单金额",
		"实付币种", "实付金额", "手续费", "税费", "净收入", "扣款占比",
		"充值用户ID", "充值用户昵称", "币商用户ID", "区域", "状态",
	})
	for _, item := range bills {
		_ = writer.Write(rechargeBillExportRow(item, regionNames))
	}
	if truncated {
		_ = writer.Write([]string{fmt.Sprintf("-- 导出达到上限 %d 行，请缩小时间范围后重新导出 --", rechargeBillExportMaxRows)})
	}
	writer.Flush()
	if err := writer.Error(); err != nil {
		response.ServerError(c, "生成导出文件失败")
		return
	}

	fileName := fmt.Sprintf("recharge-bills-%s-%s.csv", appCode, time.Now().In(rechargeBillExportTimezone).Format("20060102-150405"))
	shared.OperationLog(c, h.audit, "export-recharge-bills", "wallet_recharge_records", "success", fmt.Sprintf("%s:%d rows", appCode, len(bills)))
	c.Header("Content-Disposition", "attachment; filename="+fileName)
	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Writer.WriteHeader(200)
	_, _ = c.Writer.Write(buffer.Bytes())
}

// collectRechargeBillsForExport 复用列表筛选逐页拉取账单；返回 ok=false 表示已写出错误响应。
func (h *Handler) collectRechargeBillsForExport(c *gin.Context, appCode string) ([]rechargeBillDTO, bool) {
	options := shared.ListOptions(c)
	tzOffsetMinutes := int32(queryInt64(c, "tz_offset_minutes", "tzOffsetMinutes"))
	if tzOffsetMinutes == 0 {
		// legacy 币商充值导出必须和页面列表使用同一个自然日解释方式。
		tzOffsetMinutes = defaultLegacyFinanceTZOffsetMinutes
	}
	rechargeType := strings.TrimSpace(firstQuery(c, "recharge_type", "rechargeType"))
	normalizedType := normalizedRechargeType(rechargeType)
	if normalizedType == "" || isFinanceCoinSellerRechargeType(normalizedType) {
		userFilter := shared.UserIdentityFilterFromQuery(c, "")
		userID, userMatched, ok := h.resolveRechargeBillUserFilter(c, appCode, userFilter, "user_id 参数不正确", "user_id", "userId")
		if !ok {
			return nil, false
		}
		sellerFilter := shared.UserIdentityFilterFromQuery(c, "seller")
		sellerUserID, sellerMatched, ok := h.resolveRechargeBillUserFilter(c, appCode, sellerFilter, "seller_user_id 参数不正确", "seller_user_id", "sellerUserId")
		if !ok {
			return nil, false
		}
		if (!userFilter.IsEmpty() && !userMatched) || (!sellerFilter.IsEmpty() && !sellerMatched) {
			return []rechargeBillDTO{}, true
		}
		query := financeCoinSellerRechargeQuery{
			RechargeType:      rechargeType,
			Status:            options.Status,
			PaidState:         strings.TrimSpace(firstQuery(c, "paid_state", "paidState")),
			Keyword:           options.Keyword,
			RegionID:          queryInt64(c, "region_id", "regionId"),
			StartAtMS:         queryInt64(c, "start_at_ms", "startAtMs"),
			EndAtMS:           queryInt64(c, "end_at_ms", "endAtMs"),
			TargetUserID:      sellerUserID,
			HasUserOnlyFilter: userID > 0 && sellerUserID == 0,
			Page:              1,
			PageSize:          rechargeBillExportMaxRows,
			MaxPageSize:       rechargeBillExportMaxRows,
			RequestID:         middleware.CurrentRequestID(c),
			UserID:            userID,
		}
		var items []rechargeBillDTO
		var err error
		if isFinanceCoinSellerRechargeType(normalizedType) {
			items, _, err = h.listFinanceCoinSellerRechargeBills(c.Request.Context(), appCode, query)
		} else {
			items, _, err = h.listFinanceAllRechargeBills(c.Request.Context(), appCode, h.billSources[appCode], query)
		}
		if err != nil {
			response.ServerError(c, "导出账单失败")
			return nil, false
		}
		if err := h.fillRechargeBillUsers(c.Request.Context(), appCode, items); err != nil {
			response.ServerError(c, "导出账单失败")
			return nil, false
		}
		return capRechargeBillExportRows(items), true
	}
	if source, ok := h.billSources[appCode]; ok {
		bills := make([]rechargeBillDTO, 0, rechargeBillExportPageSize)
		for page := 1; len(bills) < rechargeBillExportMaxRows; page++ {
			items, total, err := source.ListRechargeBills(c.Request.Context(), legacyRechargeBillQuery{
				Keyword:         options.Keyword,
				RechargeType:    rechargeType,
				PaidState:       strings.TrimSpace(firstQuery(c, "paid_state", "paidState")),
				RegionID:        queryInt64(c, "region_id", "regionId"),
				StartAtMS:       queryInt64(c, "start_at_ms", "startAtMs"),
				EndAtMS:         queryInt64(c, "end_at_ms", "endAtMs"),
				TzOffsetMinutes: tzOffsetMinutes,
				Page:            page,
				PageSize:        rechargeBillExportPageSize,
			})
			if err != nil {
				response.ServerError(c, "导出账单失败")
				return nil, false
			}
			bills = append(bills, items...)
			if len(items) < rechargeBillExportPageSize || int64(len(bills)) >= total {
				break
			}
		}
		return capRechargeBillExportRows(bills), true
	}

	userFilter := shared.UserIdentityFilterFromQuery(c, "")
	userID, userMatched, ok := h.resolveRechargeBillUserFilter(c, appCode, userFilter, "user_id 参数不正确", "user_id", "userId")
	if !ok {
		return nil, false
	}
	sellerFilter := shared.UserIdentityFilterFromQuery(c, "seller")
	sellerUserID, sellerMatched, ok := h.resolveRechargeBillUserFilter(c, appCode, sellerFilter, "seller_user_id 参数不正确", "seller_user_id", "sellerUserId")
	if !ok {
		return nil, false
	}
	if (!userFilter.IsEmpty() && !userMatched) || (!sellerFilter.IsEmpty() && !sellerMatched) {
		return []rechargeBillDTO{}, true
	}

	bills := make([]rechargeBillDTO, 0, rechargeBillExportPageSize)
	for page := 1; len(bills) < rechargeBillExportMaxRows; page++ {
		resp, err := h.wallet.ListRechargeBills(c.Request.Context(), &walletv1.ListRechargeBillsRequest{
			RequestId:    middleware.CurrentRequestID(c),
			AppCode:      appCode,
			UserId:       userID,
			SellerUserId: sellerUserID,
			RegionId:     queryInt64(c, "region_id", "regionId"),
			RechargeType: strings.TrimSpace(firstQuery(c, "recharge_type", "rechargeType")),
			PaidState:    strings.TrimSpace(firstQuery(c, "paid_state", "paidState")),
			Status:       options.Status,
			Keyword:      options.Keyword,
			StartAtMs:    queryInt64(c, "start_at_ms", "startAtMs"),
			EndAtMs:      queryInt64(c, "end_at_ms", "endAtMs"),
			Page:         int32(page),
			PageSize:     rechargeBillExportPageSize,
		})
		if err != nil {
			writeWalletError(c, err, "导出账单失败")
			return nil, false
		}
		items := make([]rechargeBillDTO, 0, len(resp.GetBills()))
		for _, item := range resp.GetBills() {
			items = append(items, rechargeBillFromProto(item))
		}
		if err := h.fillRechargeBillUsers(c.Request.Context(), appCode, items); err != nil {
			response.ServerError(c, "导出账单用户资料失败")
			return nil, false
		}
		bills = append(bills, items...)
		if len(items) < rechargeBillExportPageSize || int64(len(bills)) >= resp.GetTotal() {
			break
		}
	}
	return capRechargeBillExportRows(bills), true
}

func capRechargeBillExportRows(bills []rechargeBillDTO) []rechargeBillDTO {
	if len(bills) > rechargeBillExportMaxRows {
		return bills[:rechargeBillExportMaxRows]
	}
	return bills
}

func rechargeBillExportRow(item rechargeBillDTO, regionNames map[int64]string) []string {
	deductionMicro := item.ProviderFeeMicro + item.ProviderTaxMicro
	deductionRatio := ""
	if deductionMicro > 0 && item.UserPaidAmountMicro > 0 {
		deductionRatio = fmt.Sprintf("%.2f%%", float64(deductionMicro)/float64(item.UserPaidAmountMicro)*100)
	}
	billAmountMinor := item.USDMinorAmount
	if item.ProviderAmountMinor > 0 {
		billAmountMinor = item.ProviderAmountMinor
	}
	chargeUserID := ""
	if item.User.UserID != "" {
		chargeUserID = item.User.UserID
	} else if item.UserID > 0 {
		chargeUserID = strconv.FormatInt(item.UserID, 10)
	}
	sellerUserID := ""
	if item.SellerUserID > 0 {
		sellerUserID = strconv.FormatInt(item.SellerUserID, 10)
	}
	regionID := item.TargetRegionID
	if regionID == 0 {
		regionID = item.SellerRegionID
	}
	regionText := ""
	if regionID > 0 {
		if name, ok := regionNames[regionID]; ok && name != "" {
			regionText = name
		} else {
			regionText = strconv.FormatInt(regionID, 10)
		}
	}
	return []string{
		time.UnixMilli(item.CreatedAtMS).In(rechargeBillExportTimezone).Format("2006-01-02 15:04:05"),
		item.AppCode,
		rechargeSourceLabel(item.RechargeType),
		item.TransactionID,
		item.CommandID,
		item.ExternalRef,
		strconv.FormatInt(item.CoinAmount, 10),
		csvMinorAmount(item.USDMinorAmount),
		item.CurrencyCode,
		csvMinorAmount(billAmountMinor),
		item.UserPaidCurrencyCode,
		csvMicroAmount(item.UserPaidAmountMicro),
		csvMicroAmount(item.ProviderFeeMicro),
		csvMicroAmount(item.ProviderTaxMicro),
		csvMicroAmount(item.ProviderNetMicro),
		deductionRatio,
		chargeUserID,
		item.User.Username,
		sellerUserID,
		regionText,
		item.Status,
	}
}

// rechargeSourceLabel 与前端 rechargeTypeLabel 保持同一套中文口径，导出文件可直接给财务阅读。
func rechargeSourceLabel(rechargeType string) string {
	switch rechargeType {
	case "google_play_recharge":
		return "谷歌充值"
	case "coin_seller":
		return "币商充值"
	case financeCoinSellerThirdPartyType:
		return "币商充值-三方"
	case financeCoinSellerMiFaPayType:
		return "币商充值-MiFaPay"
	case financeCoinSellerV5PayType:
		return "币商充值-V5Pay"
	case financeCoinSellerUSDTType:
		return "币商充值-USDT"
	case "coin_seller_stock_purchase":
		return "币商进货"
	case "coin_seller_stock_deduction":
		return "币商冲回"
	case "coin_seller_transfer":
		return "币商充值"
	case "mifapay":
		return "三方充值-MiFaPay"
	case "v5pay":
		return "三方充值-V5Pay"
	case "usdt_trc20":
		return "三方充值-USDT"
	case "apple_recharge":
		return "苹果充值"
	case "huawei_recharge":
		return "华为充值"
	case "telegram_recharge":
		return "Telegram充值"
	case "web_recharge":
		return "三方充值-Web"
	default:
		return rechargeType
	}
}

func csvMinorAmount(minor int64) string {
	return strconv.FormatFloat(float64(minor)/100, 'f', 2, 64)
}

func csvMicroAmount(micro int64) string {
	if micro == 0 {
		return "0"
	}
	negative := micro < 0
	if negative {
		micro = -micro
	}
	text := formatUSDTMicro(micro)
	if negative {
		return "-" + text
	}
	return text
}
