package repository

import (
	"errors"
	"strings"

	"hyapp-admin-server/internal/model"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

var (
	ErrCoinSellerRechargeOrderNotVerified = errors.New("coin seller recharge order is not verified")
	ErrCoinSellerRechargeOrderGranting    = errors.New("coin seller recharge order is granting")
	ErrCoinSellerRechargeOrderGranted     = errors.New("coin seller recharge order already granted")
)

const (
	// RechargeSource 只描述运营录入币商充值时的付款大类；provider_code 仍保留具体支付商，不能用展示分组覆盖审计事实。
	CoinSellerRechargeSourceThirdParty = "third_party"
	CoinSellerRechargeSourceMiFaPay    = "mifapay"
	CoinSellerRechargeSourceV5Pay      = "v5pay"
	CoinSellerRechargeSourceUSDT       = "usdt"
)

// coinSellerRechargeBusinessTimeSQL 让新订单按运营选择的业务时间归档，同时保持历史 order_date_ms=0 数据仍按录入时间查询。
const coinSellerRechargeBusinessTimeSQL = "CASE WHEN order_date_ms > 0 THEN order_date_ms ELSE created_at_ms END"

type CoinSellerRechargeOrderListOptions struct {
	Page           int
	PageSize       int
	PageSizeLimit  int
	AppCode        string
	Status         string
	VerifyStatus   string
	GrantStatus    string
	ProviderCode   string
	RechargeSource string
	Keyword        string
	TargetUserID   int64
	RegionID       int64
	OperatorID     uint
	CreatedFromMS  int64
	CreatedToMS    int64
}

type CoinSellerRechargeOrderStatsOptions struct {
	AppCode         string
	Status          string
	RechargeSource  string
	Keyword         string
	TargetUserID    int64
	RegionID        int64
	CountryID       int64
	CreatedFromMS   int64
	CreatedToMS     int64
	TzOffsetMinutes int32
}

type CoinSellerRechargeOrderStats struct {
	VerifiedCount         int64
	VerifiedUSDMinor      int64
	VerifiedCoinAmount    int64
	GrantedCount          int64
	GrantedUSDMinor       int64
	GrantedCoinAmount     int64
	VerifiedDaily         map[string]CoinSellerRechargeOrderStatsBucket
	GrantedDaily          map[string]CoinSellerRechargeOrderStatsBucket
	VerifiedRegionBuckets []CoinSellerRechargeOrderRegionStatsBucket
	GrantedRegionBuckets  []CoinSellerRechargeOrderRegionStatsBucket
}

type CoinSellerRechargeOrderStatsBucket struct {
	Count      int64
	USDMinor   int64
	CoinAmount int64
}

type CoinSellerRechargeOrderRegionStatsBucket struct {
	RegionID   int64
	CountryID  int64
	Count      int64
	USDMinor   int64
	CoinAmount int64
}

type CoinSellerRechargeOrderVerifyInput struct {
	VerifiedByUserID    uint
	VerifiedByName      string
	VerifiedAtMS        int64
	ProviderOrderID     string
	Status              string
	CurrencyCode        string
	ProviderAmountMinor int64
	ProviderPayloadJSON string
	ReceiveAddress      string
	FailureReason       string
}

type CoinSellerRechargeOrderGrantInput struct {
	GrantedByUserID     uint
	GrantedByName       string
	GrantedAtMS         int64
	WalletCommandID     string
	WalletTransactionID string
	WalletAssetType     string
	WalletAmountDelta   int64
	WalletBalanceAfter  int64
	FailureReason       string
}

func (s *Store) CreateCoinSellerRechargeOrder(order *model.CoinSellerRechargeOrder) error {
	if order == nil {
		return errors.New("coin seller recharge order is required")
	}
	return s.db.Create(order).Error
}

func (s *Store) GetCoinSellerRechargeOrder(id uint) (*model.CoinSellerRechargeOrder, error) {
	var order model.CoinSellerRechargeOrder
	if err := s.db.First(&order, id).Error; err != nil {
		return nil, err
	}
	return &order, nil
}

func (s *Store) ListCoinSellerRechargeOrders(options CoinSellerRechargeOrderListOptions) ([]model.CoinSellerRechargeOrder, int64, error) {
	page, pageSize := normalizeCoinSellerRechargeOrderPage(options.Page, options.PageSize, options.PageSizeLimit)
	query := applyCoinSellerRechargeOrderFilters(s.db.Model(&model.CoinSellerRechargeOrder{}), options)
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	var orders []model.CoinSellerRechargeOrder
	err := query.
		Order(coinSellerRechargeBusinessTimeSQL + " DESC, id DESC").
		Limit(pageSize).
		Offset((page - 1) * pageSize).
		Find(&orders).Error
	return orders, total, err
}

func normalizeCoinSellerRechargeOrderPage(page int, pageSize int, pageSizeLimit int) (int, int) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSizeLimit <= 0 {
		pageSizeLimit = 100
	}
	if pageSize > pageSizeLimit {
		pageSize = pageSizeLimit
	}
	return page, pageSize
}

func (s *Store) CoinSellerRechargeOrderStats(options CoinSellerRechargeOrderStatsOptions) (CoinSellerRechargeOrderStats, error) {
	stats := CoinSellerRechargeOrderStats{
		VerifiedDaily: map[string]CoinSellerRechargeOrderStatsBucket{},
		GrantedDaily:  map[string]CoinSellerRechargeOrderStatsBucket{},
	}
	var totalRow struct {
		VerifiedUSDMinor   int64 `gorm:"column:verified_usd_minor"`
		VerifiedCoinAmount int64 `gorm:"column:verified_coin_amount"`
		VerifiedCount      int64 `gorm:"column:verified_count"`
		GrantedUSDMinor    int64 `gorm:"column:granted_usd_minor"`
		GrantedCoinAmount  int64 `gorm:"column:granted_coin_amount"`
		GrantedCount       int64 `gorm:"column:granted_count"`
	}
	if err := applyCoinSellerRechargeOrderStatsFilters(s.db.Model(&model.CoinSellerRechargeOrder{}), options).
		Select(`
			COALESCE(SUM(usd_minor_amount), 0) AS verified_usd_minor,
			COALESCE(SUM(coin_amount), 0) AS verified_coin_amount,
			COUNT(*) AS verified_count,
			COALESCE(SUM(CASE WHEN grant_status = ? AND (wallet_transaction_id <> '' OR wallet_amount_delta <> 0) THEN usd_minor_amount ELSE 0 END), 0) AS granted_usd_minor,
			COALESCE(SUM(CASE WHEN grant_status = ? AND (wallet_transaction_id <> '' OR wallet_amount_delta <> 0) THEN coin_amount ELSE 0 END), 0) AS granted_coin_amount,
			COALESCE(SUM(CASE WHEN grant_status = ? AND (wallet_transaction_id <> '' OR wallet_amount_delta <> 0) THEN 1 ELSE 0 END), 0) AS granted_count`,
			model.CoinSellerRechargeGrantStatusGranted,
			model.CoinSellerRechargeGrantStatusGranted,
			model.CoinSellerRechargeGrantStatusGranted,
		).Scan(&totalRow).Error; err != nil {
		return stats, err
	}
	stats.VerifiedUSDMinor = totalRow.VerifiedUSDMinor
	stats.VerifiedCoinAmount = totalRow.VerifiedCoinAmount
	stats.VerifiedCount = totalRow.VerifiedCount
	stats.GrantedUSDMinor = totalRow.GrantedUSDMinor
	stats.GrantedCoinAmount = totalRow.GrantedCoinAmount
	stats.GrantedCount = totalRow.GrantedCount
	if err := s.scanCoinSellerRechargeOrderDailyStats(options, true, stats.VerifiedDaily); err != nil {
		return stats, err
	}
	if err := s.scanCoinSellerRechargeOrderDailyStats(options, false, stats.GrantedDaily); err != nil {
		return stats, err
	}
	var err error
	stats.VerifiedRegionBuckets, err = s.scanCoinSellerRechargeOrderRegionStats(options, true)
	if err != nil {
		return stats, err
	}
	stats.GrantedRegionBuckets, err = s.scanCoinSellerRechargeOrderRegionStats(options, false)
	if err != nil {
		return stats, err
	}
	return stats, nil
}

func (s *Store) MarkCoinSellerRechargeOrderVerified(id uint, input CoinSellerRechargeOrderVerifyInput) (*model.CoinSellerRechargeOrder, error) {
	return s.updateCoinSellerRechargeVerifyState(id, input, true)
}

func (s *Store) MarkCoinSellerRechargeOrderVerifyFailed(id uint, input CoinSellerRechargeOrderVerifyInput) (*model.CoinSellerRechargeOrder, error) {
	return s.updateCoinSellerRechargeVerifyState(id, input, false)
}

func (s *Store) updateCoinSellerRechargeVerifyState(id uint, input CoinSellerRechargeOrderVerifyInput, verified bool) (*model.CoinSellerRechargeOrder, error) {
	if id == 0 {
		return nil, errors.New("coin seller recharge order id is required")
	}
	var order model.CoinSellerRechargeOrder
	err := s.db.Transaction(func(tx *gorm.DB) error {
		// 校验可以在失败后重试，但已发放订单不能再改 provider 快照，避免审计证据和钱包事实分叉。
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, id).Error; err != nil {
			return err
		}
		if order.GrantStatus == model.CoinSellerRechargeGrantStatusGranted {
			return ErrCoinSellerRechargeOrderGranted
		}
		status := model.CoinSellerRechargeOrderStatusVerifyFailed
		verifyStatus := model.CoinSellerRechargeVerifyStatusFailed
		if verified {
			status = model.CoinSellerRechargeOrderStatusVerified
			verifyStatus = model.CoinSellerRechargeVerifyStatusVerified
		}
		updates := map[string]any{
			"status":                status,
			"verify_status":         verifyStatus,
			"failure_reason":        strings.TrimSpace(input.FailureReason),
			"provider_payload_json": strings.TrimSpace(input.ProviderPayloadJSON),
			"verified_by_user_id":   input.VerifiedByUserID,
			"verified_by_name":      strings.TrimSpace(input.VerifiedByName),
			"verified_at_ms":        input.VerifiedAtMS,
			"updated_at_ms":         input.VerifiedAtMS,
		}
		if input.ProviderAmountMinor > 0 {
			updates["provider_amount_minor"] = input.ProviderAmountMinor
		}
		if input.ProviderOrderID != "" {
			updates["provider_order_id"] = strings.TrimSpace(input.ProviderOrderID)
		}
		if input.Status != "" {
			updates["provider_status"] = strings.TrimSpace(input.Status)
		}
		if input.CurrencyCode != "" {
			updates["provider_currency_code"] = strings.ToUpper(strings.TrimSpace(input.CurrencyCode))
		}
		if input.ReceiveAddress != "" {
			updates["receive_address"] = strings.TrimSpace(input.ReceiveAddress)
		}
		return tx.Model(&order).Updates(updates).Error
	})
	if err != nil {
		return nil, err
	}
	return s.GetCoinSellerRechargeOrder(id)
}

func (s *Store) ClaimCoinSellerRechargeOrderGrant(id uint, operatorUserID uint, operatorName string, nowMS int64) (*model.CoinSellerRechargeOrder, error) {
	if id == 0 {
		return nil, errors.New("coin seller recharge order id is required")
	}
	var order model.CoinSellerRechargeOrder
	err := s.db.Transaction(func(tx *gorm.DB) error {
		// 发放是唯一账务副作用：先锁行并切到 processing，再释放事务调用下游，阻断并发双发。
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, id).Error; err != nil {
			return err
		}
		switch order.GrantStatus {
		case model.CoinSellerRechargeGrantStatusGranted:
			return nil
		case model.CoinSellerRechargeGrantStatusRunning:
			return ErrCoinSellerRechargeOrderGranting
		}
		if order.VerifyStatus != model.CoinSellerRechargeVerifyStatusVerified {
			return ErrCoinSellerRechargeOrderNotVerified
		}
		return tx.Model(&order).Updates(map[string]any{
			"status":             model.CoinSellerRechargeOrderStatusGranting,
			"grant_status":       model.CoinSellerRechargeGrantStatusRunning,
			"failure_reason":     "",
			"granted_by_user_id": operatorUserID,
			"granted_by_name":    strings.TrimSpace(operatorName),
			"updated_at_ms":      nowMS,
		}).Error
	})
	if err != nil {
		return nil, err
	}
	return s.GetCoinSellerRechargeOrder(id)
}

func (s *Store) MarkCoinSellerRechargeOrderGranted(id uint, input CoinSellerRechargeOrderGrantInput) (*model.CoinSellerRechargeOrder, error) {
	if id == 0 {
		return nil, errors.New("coin seller recharge order id is required")
	}
	var order model.CoinSellerRechargeOrder
	err := s.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, id).Error; err != nil {
			return err
		}
		if order.GrantStatus == model.CoinSellerRechargeGrantStatusGranted {
			return nil
		}
		return tx.Model(&order).Updates(map[string]any{
			"status":                model.CoinSellerRechargeOrderStatusGranted,
			"grant_status":          model.CoinSellerRechargeGrantStatusGranted,
			"failure_reason":        "",
			"wallet_command_id":     strings.TrimSpace(input.WalletCommandID),
			"wallet_transaction_id": strings.TrimSpace(input.WalletTransactionID),
			"wallet_asset_type":     strings.TrimSpace(input.WalletAssetType),
			"wallet_amount_delta":   input.WalletAmountDelta,
			"wallet_balance_after":  input.WalletBalanceAfter,
			"granted_by_user_id":    input.GrantedByUserID,
			"granted_by_name":       strings.TrimSpace(input.GrantedByName),
			"granted_at_ms":         input.GrantedAtMS,
			"updated_at_ms":         input.GrantedAtMS,
		}).Error
	})
	if err != nil {
		return nil, err
	}
	return s.GetCoinSellerRechargeOrder(id)
}

func (s *Store) MarkCoinSellerRechargeOrderGrantFailed(id uint, input CoinSellerRechargeOrderGrantInput) (*model.CoinSellerRechargeOrder, error) {
	if id == 0 {
		return nil, errors.New("coin seller recharge order id is required")
	}
	var order model.CoinSellerRechargeOrder
	err := s.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, id).Error; err != nil {
			return err
		}
		if order.GrantStatus == model.CoinSellerRechargeGrantStatusGranted {
			return nil
		}
		return tx.Model(&order).Updates(map[string]any{
			"status":             model.CoinSellerRechargeOrderStatusGrantFailed,
			"grant_status":       model.CoinSellerRechargeGrantStatusFailed,
			"failure_reason":     strings.TrimSpace(input.FailureReason),
			"granted_by_user_id": input.GrantedByUserID,
			"granted_by_name":    strings.TrimSpace(input.GrantedByName),
			"updated_at_ms":      input.GrantedAtMS,
		}).Error
	})
	if err != nil {
		return nil, err
	}
	return s.GetCoinSellerRechargeOrder(id)
}

func applyCoinSellerRechargeOrderFilters(query *gorm.DB, options CoinSellerRechargeOrderListOptions) *gorm.DB {
	if appCode := strings.TrimSpace(options.AppCode); appCode != "" {
		query = query.Where("app_code = ?", appCode)
	}
	if status := strings.TrimSpace(options.Status); status != "" && status != "all" {
		query = query.Where("status = ?", status)
	}
	if verifyStatus := strings.TrimSpace(options.VerifyStatus); verifyStatus != "" && verifyStatus != "all" {
		query = query.Where("verify_status = ?", verifyStatus)
	}
	if grantStatus := strings.TrimSpace(options.GrantStatus); grantStatus != "" && grantStatus != "all" {
		query = query.Where("grant_status = ?", grantStatus)
	}
	if providerCode := strings.TrimSpace(options.ProviderCode); providerCode != "" && providerCode != "all" {
		query = query.Where("provider_code = ?", providerCode)
	}
	query = applyCoinSellerRechargeSourceFilter(query, options.RechargeSource)
	if options.TargetUserID > 0 {
		query = query.Where("target_user_id = ?", options.TargetUserID)
	}
	if options.RegionID > 0 {
		query = query.Where("target_region_id = ?", options.RegionID)
	}
	if options.OperatorID > 0 {
		query = query.Where("operator_user_id = ?", options.OperatorID)
	}
	if options.CreatedFromMS > 0 {
		query = query.Where(coinSellerRechargeBusinessTimeSQL+" >= ?", options.CreatedFromMS)
	}
	if options.CreatedToMS > 0 {
		query = query.Where(coinSellerRechargeBusinessTimeSQL+" < ?", options.CreatedToMS)
	}
	if keyword := strings.TrimSpace(options.Keyword); keyword != "" {
		like := "%" + keyword + "%"
		query = query.Where("external_order_no LIKE ? OR provider_order_id LIKE ? OR target_display_user_id LIKE ? OR wallet_transaction_id LIKE ? OR operator_name LIKE ? OR verified_by_name LIKE ? OR granted_by_name LIKE ? OR remark LIKE ? OR CAST(target_user_id AS CHAR) LIKE ? OR CAST(id AS CHAR) LIKE ?", like, like, like, like, like, like, like, like, like, like)
	}
	return query
}

func applyCoinSellerRechargeOrderStatsFilters(query *gorm.DB, options CoinSellerRechargeOrderStatsOptions) *gorm.DB {
	// 财务三方统计只承接“创建并校验通过”的币商充值订单；发放状态只影响去重扣减，不决定是否入统计。
	query = query.Where("verify_status = ?", model.CoinSellerRechargeVerifyStatusVerified)
	if appCode := strings.TrimSpace(options.AppCode); appCode != "" && appCode != "all" {
		query = query.Where("app_code = ?", appCode)
	}
	if status := strings.TrimSpace(options.Status); status != "" && status != "all" {
		query = query.Where("status = ?", status)
	}
	query = applyCoinSellerRechargeSourceFilter(query, options.RechargeSource)
	if options.TargetUserID > 0 {
		query = query.Where("target_user_id = ?", options.TargetUserID)
	}
	if options.RegionID > 0 {
		query = query.Where("target_region_id = ?", options.RegionID)
	}
	if options.CountryID > 0 {
		query = query.Where("target_country_id = ?", options.CountryID)
	}
	if options.CreatedFromMS > 0 {
		query = query.Where(coinSellerRechargeBusinessTimeSQL+" >= ?", options.CreatedFromMS)
	}
	if options.CreatedToMS > 0 {
		query = query.Where(coinSellerRechargeBusinessTimeSQL+" < ?", options.CreatedToMS)
	}
	if keyword := strings.TrimSpace(options.Keyword); keyword != "" {
		like := "%" + keyword + "%"
		query = query.Where("external_order_no LIKE ? OR provider_order_id LIKE ? OR target_display_user_id LIKE ? OR wallet_transaction_id LIKE ? OR operator_name LIKE ? OR verified_by_name LIKE ? OR granted_by_name LIKE ? OR remark LIKE ? OR CAST(target_user_id AS CHAR) LIKE ? OR CAST(id AS CHAR) LIKE ?", like, like, like, like, like, like, like, like, like, like)
	}
	return query
}

// applyCoinSellerRechargeSourceFilter 把财务展示分组还原到台账的权威 provider_code。
// 当前创建入口只允许 mifapay/v5pay/usdt；聚合三方使用小集合 IN，厂商和 USDT 使用等值条件，均可继续利用 provider_code 索引。
func applyCoinSellerRechargeSourceFilter(query *gorm.DB, rechargeSource string) *gorm.DB {
	switch strings.ToLower(strings.TrimSpace(rechargeSource)) {
	case CoinSellerRechargeSourceThirdParty:
		return query.Where("provider_code IN ?", []string{"mifapay", "v5pay"})
	case CoinSellerRechargeSourceMiFaPay:
		return query.Where("provider_code = ?", "mifapay")
	case CoinSellerRechargeSourceV5Pay:
		return query.Where("provider_code = ?", "v5pay")
	case CoinSellerRechargeSourceUSDT:
		return query.Where("provider_code = ?", "usdt")
	default:
		return query
	}
}

func (s *Store) scanCoinSellerRechargeOrderDailyStats(options CoinSellerRechargeOrderStatsOptions, includeAllVerified bool, out map[string]CoinSellerRechargeOrderStatsBucket) error {
	if out == nil {
		return nil
	}
	query := applyCoinSellerRechargeOrderStatsFilters(s.db.Model(&model.CoinSellerRechargeOrder{}), options)
	if !includeAllVerified {
		query = query.Where("grant_status = ? AND (wallet_transaction_id <> '' OR wallet_amount_delta <> 0)", model.CoinSellerRechargeGrantStatusGranted)
	}
	type row struct {
		Date       string
		Count      int64
		USDMinor   int64
		CoinAmount int64
	}
	var rows []row
	tzOffsetMS := int64(options.TzOffsetMinutes) * 60_000
	// 财务筛选和趋势都按订单业务时间取数；历史记录自动回退录入时间，并继续按前端统计时区偏移切日。
	if err := query.Select(`
			DATE_FORMAT(FROM_UNIXTIME((`+coinSellerRechargeBusinessTimeSQL+` + ?) / 1000), '%Y-%m-%d') AS date,
			COUNT(*) AS count,
			COALESCE(SUM(usd_minor_amount), 0) AS usd_minor,
			COALESCE(SUM(coin_amount), 0) AS coin_amount`, tzOffsetMS).
		Group("date").
		Order("date ASC").
		Scan(&rows).Error; err != nil {
		return err
	}
	for _, row := range rows {
		if row.Date == "" {
			continue
		}
		out[row.Date] = CoinSellerRechargeOrderStatsBucket{Count: row.Count, USDMinor: row.USDMinor, CoinAmount: row.CoinAmount}
	}
	return nil
}

func (s *Store) scanCoinSellerRechargeOrderRegionStats(options CoinSellerRechargeOrderStatsOptions, includeAllVerified bool) ([]CoinSellerRechargeOrderRegionStatsBucket, error) {
	query := applyCoinSellerRechargeOrderStatsFilters(s.db.Model(&model.CoinSellerRechargeOrder{}), options)
	if !includeAllVerified {
		query = query.Where("grant_status = ? AND (wallet_transaction_id <> '' OR wallet_amount_delta <> 0)", model.CoinSellerRechargeGrantStatusGranted)
	}
	var rows []CoinSellerRechargeOrderRegionStatsBucket
	// target_region_id/target_country_id 是后台工单创建时的币商归属快照；财务区域统计必须使用这组字段。
	err := query.Select(`
			target_region_id AS region_id,
			target_country_id AS country_id,
			COUNT(*) AS count,
			COALESCE(SUM(usd_minor_amount), 0) AS usd_minor,
			COALESCE(SUM(coin_amount), 0) AS coin_amount`).
		Group("target_region_id, target_country_id").
		Order("usd_minor DESC").
		Scan(&rows).Error
	return rows, err
}
