package exporter

import (
	"context"
	"strconv"
	"time"

	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/pkg/constants"
)

// AgentWalletTransactionDataSource 代理主钱包流水导出数据源。
type AgentWalletTransactionDataSource struct {
	db *gorm.DB
}

// NewAgentWalletTransactionDataSource 创建代理主钱包流水导出数据源。
func NewAgentWalletTransactionDataSource(db *gorm.DB) *AgentWalletTransactionDataSource {
	return &AgentWalletTransactionDataSource{db: db}
}

// Scene 返回导出场景编码。
func (s *AgentWalletTransactionDataSource) Scene() string {
	return constants.ExportTaskSceneAgentWalletTransaction
}

// Count 统计代理主钱包流水导出行数。
func (s *AgentWalletTransactionDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
	var total int64
	query := s.applyFilters(s.baseQuery(ctx), params)
	if err := query.Count(&total).Error; err != nil {
		return 0, err
	}
	return int(total), nil
}

// Headers 返回代理主钱包流水导出表头。
func (s *AgentWalletTransactionDataSource) Headers(context.Context, ExportParams) ([]string, error) {
	return []string{
		"店铺名称", "交易类型", "交易金额(元)", "状态", "资产类型", "资产标识", "交易时间",
		"交易前金额(元)", "交易后金额(元)", "购买套餐名称", "操作人", "交易ID", "关联业务订单号", "交易渠道/支付方式",
	}, nil
}

// Fetch 按 offset/limit 查询代理主钱包流水导出数据。
func (s *AgentWalletTransactionDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
	if limit <= 0 {
		return [][]string{}, nil
	}

	var items []agentWalletTransactionExportRow
	query := s.applyFilters(s.baseQuery(ctx), params).
		Select(`
			t.id,
			t.transaction_type,
			t.amount,
			t.status,
			t.asset_type,
			t.asset_identifier,
			t.created_at,
			t.balance_before,
			t.balance_after,
			COALESCE(sh.shop_name, '') AS shop_name,
			COALESCE(items.package_names, t.metadata ->> 'package_name', '') AS package_name,
			COALESCE(ac.username, '') AS operator_name,
			COALESCE(
				CASE t.reference_type
					WHEN ? THEN o.order_no
					WHEN ? THEN ar.recharge_no
					WHEN ? THEN rr.refund_no
					WHEN ? THEN wr.withdrawal_no
					WHEN ? THEN ex.exchange_no
				END,
				''
			) AS business_order_no,
			COALESCE(t.metadata ->> 'payment_method', '') AS payment_method
		`, constants.ReferenceTypeOrder, constants.ReferenceTypeTopup, constants.ReferenceTypeRefund, constants.ReferenceTypeWithdrawal, constants.ReferenceTypeExchange).
		Joins("LEFT JOIN tb_shop AS sh ON sh.id = t.shop_id").
		Joins("LEFT JOIN tb_account AS ac ON ac.id = COALESCE(NULLIF(t.creator, 0), t.user_id)").
		Joins("LEFT JOIN tb_order AS o ON t.reference_type = ? AND o.id = t.reference_id AND o.deleted_at IS NULL", constants.ReferenceTypeOrder).
		Joins("LEFT JOIN tb_agent_recharge_record AS ar ON t.reference_type = ? AND ar.id = t.reference_id AND ar.deleted_at IS NULL", constants.ReferenceTypeTopup).
		Joins("LEFT JOIN tb_refund_request AS rr ON t.reference_type = ? AND rr.id = t.reference_id AND rr.deleted_at IS NULL", constants.ReferenceTypeRefund).
		Joins("LEFT JOIN tb_commission_withdrawal_request AS wr ON t.reference_type = ? AND wr.id = t.reference_id AND wr.deleted_at IS NULL", constants.ReferenceTypeWithdrawal).
		Joins("LEFT JOIN tb_exchange_order AS ex ON t.reference_type = ? AND ex.id = t.reference_id AND ex.deleted_at IS NULL", constants.ReferenceTypeExchange).
		Joins(`LEFT JOIN LATERAL (
			SELECT string_agg(oi.package_name, ',' ORDER BY oi.id) AS package_names
			FROM tb_order_item AS oi
			WHERE t.reference_type = ?
				AND oi.order_id = t.reference_id
				AND oi.deleted_at IS NULL
		) AS items ON TRUE`, constants.ReferenceTypeOrder).
		Order("t.id ASC").
		Limit(limit).
		Offset(offset)
	if err := query.Scan(&items).Error; err != nil {
		return nil, err
	}

	rows := make([][]string, 0, len(items))
	for _, item := range items {
		paymentMethod := item.PaymentMethod
		if paymentMethod == "" && item.TransactionType == constants.AgentTransactionTypeDeduct {
			paymentMethod = constants.PaymentMethodWallet
		}
		rows = append(rows, []string{
			item.ShopName,
			constants.GetAgentTransactionTypeName(item.TransactionType),
			formatMoneyYuan(item.Amount),
			constants.GetTransactionStatusName(item.Status),
			constants.GetWalletAssetTypeName(item.AssetType),
			item.AssetIdentifier,
			item.CreatedAt.Format(exportTimeLayout),
			formatMoneyYuan(item.BalanceBefore),
			formatMoneyYuan(item.BalanceAfter),
			item.PackageName,
			item.OperatorName,
			strconv.FormatUint(uint64(item.ID), 10),
			item.BusinessOrderNo,
			constants.GetBusinessPaymentMethodName(paymentMethod),
		})
	}
	return rows, nil
}

func (s *AgentWalletTransactionDataSource) baseQuery(ctx context.Context) *gorm.DB {
	return s.db.WithContext(ctx).
		Table("tb_agent_wallet_transaction AS t").
		Joins("INNER JOIN tb_agent_wallet AS w ON w.id = t.agent_wallet_id AND w.wallet_type = ? AND w.deleted_at IS NULL", constants.AgentWalletTypeMain).
		Where("t.deleted_at IS NULL")
}

func (s *AgentWalletTransactionDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
	query = applyExportShopScope(query, params, "t.shop_id")
	if shopID, ok := filterUint(params.Filters, "shop_id"); ok {
		query = query.Where("t.shop_id = ?", shopID)
	}
	if transactionType, ok := filterString(params.Filters, "transaction_type"); ok {
		query = query.Where("t.transaction_type = ?", transactionType)
	}
	if start, ok := filterTime(params.Filters, "start_date"); ok {
		query = query.Where("t.created_at >= ?", start)
	}
	if end, ok := filterEndDate(params.Filters, "end_date"); ok {
		query = query.Where("t.created_at <= ?", end)
	}
	if assetIdentifier, ok := filterString(params.Filters, "asset_identifier"); ok {
		query = query.Where("t.asset_identifier = ?", assetIdentifier)
	}
	return query
}

type agentWalletTransactionExportRow struct {
	ID              uint      `gorm:"column:id"`
	ShopName        string    `gorm:"column:shop_name"`
	TransactionType string    `gorm:"column:transaction_type"`
	Amount          int64     `gorm:"column:amount"`
	Status          int       `gorm:"column:status"`
	AssetType       string    `gorm:"column:asset_type"`
	AssetIdentifier string    `gorm:"column:asset_identifier"`
	CreatedAt       time.Time `gorm:"column:created_at"`
	BalanceBefore   int64     `gorm:"column:balance_before"`
	BalanceAfter    int64     `gorm:"column:balance_after"`
	PackageName     string    `gorm:"column:package_name"`
	OperatorName    string    `gorm:"column:operator_name"`
	BusinessOrderNo string    `gorm:"column:business_order_no"`
	PaymentMethod   string    `gorm:"column:payment_method"`
}
