package commission_calculation

import (
	"context"
	"fmt"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/service/commission_stats"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/utils"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type Service struct {
	db                                   *gorm.DB
	commissionRecordStore                *postgres.CommissionRecordStore
	shopStore                            *postgres.ShopStore
	shopSeriesAllocationStore            *postgres.ShopSeriesAllocationStore
	shopSeriesOneTimeCommissionTierStore *postgres.ShopSeriesOneTimeCommissionTierStore
	iotCardStore                         *postgres.IotCardStore
	deviceStore                          *postgres.DeviceStore
	walletStore                          *postgres.WalletStore
	walletTransactionStore               *postgres.WalletTransactionStore
	orderStore                           *postgres.OrderStore
	orderItemStore                       *postgres.OrderItemStore
	packageStore                         *postgres.PackageStore
	commissionStatsService               *commission_stats.Service
	logger                               *zap.Logger
}

func New(
	db *gorm.DB,
	commissionRecordStore *postgres.CommissionRecordStore,
	shopStore *postgres.ShopStore,
	shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore,
	shopSeriesOneTimeCommissionTierStore *postgres.ShopSeriesOneTimeCommissionTierStore,
	iotCardStore *postgres.IotCardStore,
	deviceStore *postgres.DeviceStore,
	walletStore *postgres.WalletStore,
	walletTransactionStore *postgres.WalletTransactionStore,
	orderStore *postgres.OrderStore,
	orderItemStore *postgres.OrderItemStore,
	packageStore *postgres.PackageStore,
	commissionStatsService *commission_stats.Service,
	logger *zap.Logger,
) *Service {
	return &Service{
		db:                                   db,
		commissionRecordStore:                commissionRecordStore,
		shopStore:                            shopStore,
		shopSeriesAllocationStore:            shopSeriesAllocationStore,
		shopSeriesOneTimeCommissionTierStore: shopSeriesOneTimeCommissionTierStore,
		iotCardStore:                         iotCardStore,
		deviceStore:                          deviceStore,
		walletStore:                          walletStore,
		walletTransactionStore:               walletTransactionStore,
		orderStore:                           orderStore,
		orderItemStore:                       orderItemStore,
		packageStore:                         packageStore,
		commissionStatsService:               commissionStatsService,
		logger:                               logger,
	}
}

func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		order, err := s.orderStore.GetByID(ctx, orderID)
		if err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "获取订单失败")
		}

		if order.CommissionStatus == model.CommissionStatusCalculated {
			s.logger.Warn("订单佣金已计算，跳过", zap.String("order_id", fmt.Sprint(orderID)))
			return nil
		}

		costDiffRecords, err := s.CalculateCostDiffCommission(ctx, order)
		if err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "计算成本价差佣金失败")
		}

		for _, record := range costDiffRecords {
			if err := tx.Create(record).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "创建成本价差佣金记录失败")
			}
			if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
				return errors.Wrap(errors.CodeInternalError, err, "佣金入账失败")
			}
		}

		// 代购订单不触发一次性佣金和累计充值更新
		if !order.IsPurchaseOnBehalf {
			if order.OrderType == model.OrderTypeSingleCard && order.IotCardID != nil {
				if err := s.triggerOneTimeCommissionForCardInTx(ctx, tx, order, *order.IotCardID); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "触发单卡一次性佣金失败")
				}
			} else if order.OrderType == model.OrderTypeDevice && order.DeviceID != nil {
				if err := s.triggerOneTimeCommissionForDeviceInTx(ctx, tx, order, *order.DeviceID); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "触发设备一次性佣金失败")
				}
			}
		}

		if err := tx.Model(&model.Order{}).Where("id = ?", orderID).Update("commission_status", model.CommissionStatusCalculated).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "更新订单佣金状态失败")
		}

		return nil
	})
}

func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.Order) ([]*model.CommissionRecord, error) {
	var records []*model.CommissionRecord

	if order.SellerShopID == nil {
		return records, nil
	}

	if order.SeriesID == nil {
		s.logger.Warn("订单缺少系列ID，跳过成本价差佣金计算", zap.Uint("order_id", order.ID))
		return records, nil
	}

	sellerShop, err := s.shopStore.GetByID(ctx, *order.SellerShopID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "获取销售店铺失败")
	}

	sellerProfit := order.TotalAmount - order.SellerCostPrice
	if sellerProfit > 0 {
		records = append(records, &model.CommissionRecord{
			BaseModel: model.BaseModel{
				Creator: order.Creator,
				Updater: order.Updater,
			},
			ShopID:           *order.SellerShopID,
			OrderID:          order.ID,
			IotCardID:        order.IotCardID,
			DeviceID:         order.DeviceID,
			CommissionSource: model.CommissionSourceCostDiff,
			Amount:           sellerProfit,
			Status:           model.CommissionStatusReleased,
		})
	}

	childCostPrice := order.SellerCostPrice
	currentShopID := sellerShop.ParentID

	for currentShopID != nil && *currentShopID > 0 {
		currentShop, err := s.shopStore.GetByID(ctx, *currentShopID)
		if err != nil {
			s.logger.Error("获取上级店铺失败", zap.Uint("shop_id", *currentShopID), zap.Error(err))
			break
		}

		allocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, currentShop.ID, *order.SeriesID)
		if err != nil {
			s.logger.Warn("上级店铺未分配该系列，跳过", zap.Uint("shop_id", currentShop.ID), zap.Uint("series_id", *order.SeriesID))
			break
		}

		myCostPrice := s.calculateCostPrice(allocation, order.TotalAmount)
		profit := childCostPrice - myCostPrice
		if profit > 0 {
			records = append(records, &model.CommissionRecord{
				BaseModel: model.BaseModel{
					Creator: order.Creator,
					Updater: order.Updater,
				},
				ShopID:           currentShop.ID,
				OrderID:          order.ID,
				IotCardID:        order.IotCardID,
				DeviceID:         order.DeviceID,
				CommissionSource: model.CommissionSourceCostDiff,
				Amount:           profit,
				Status:           model.CommissionStatusReleased,
			})
		}

		childCostPrice = myCostPrice
		currentShopID = currentShop.ParentID
	}

	return records, nil
}

func (s *Service) calculateCostPrice(allocation *model.ShopSeriesAllocation, orderAmount int64) int64 {
	return utils.CalculateCostPrice(allocation, orderAmount)
}

func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *gorm.DB, order *model.Order, cardID uint) error {
	// 代购订单不触发一次性佣金和累计充值更新
	if order.IsPurchaseOnBehalf {
		return nil
	}

	card, err := s.iotCardStore.GetByID(ctx, cardID)
	if err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "获取卡信息失败")
	}

	if card.SeriesID == nil || card.ShopID == nil {
		return nil
	}

	allocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, *card.ShopID, *card.SeriesID)
	if err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "获取系列分配失败")
	}

	if !allocation.EnableOneTimeCommission {
		return nil
	}

	if allocation.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
		newAccumulated := card.AccumulatedRecharge + order.TotalAmount
		if err := tx.Model(&model.IotCard{}).Where("id = ?", cardID).
			Update("accumulated_recharge", newAccumulated).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "更新卡累计充值金额失败")
		}
		card.AccumulatedRecharge = newAccumulated
	}

	if card.FirstCommissionPaid {
		return nil
	}

	var rechargeAmount int64
	switch allocation.OneTimeCommissionTrigger {
	case model.OneTimeCommissionTriggerSingleRecharge:
		rechargeAmount = order.TotalAmount
	case model.OneTimeCommissionTriggerAccumulatedRecharge:
		rechargeAmount = card.AccumulatedRecharge
	default:
		return nil
	}

	if rechargeAmount < allocation.OneTimeCommissionThreshold {
		return nil
	}

	commissionAmount, err := s.calculateOneTimeCommission(ctx, allocation, order.TotalAmount)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "计算一次性佣金失败")
	}

	if commissionAmount <= 0 {
		return nil
	}

	if card.ShopID == nil {
		return errors.New(errors.CodeInvalidParam, "卡未归属任何店铺，无法发放佣金")
	}

	record := &model.CommissionRecord{
		BaseModel: model.BaseModel{
			Creator: order.Creator,
			Updater: order.Updater,
		},
		ShopID:           *card.ShopID,
		OrderID:          order.ID,
		IotCardID:        &cardID,
		CommissionSource: model.CommissionSourceOneTime,
		Amount:           commissionAmount,
		Status:           model.CommissionStatusReleased,
		Remark:           "一次性佣金",
	}

	if err := tx.Create(record).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
	}

	if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
	}

	if err := tx.Model(&model.IotCard{}).Where("id = ?", cardID).
		Update("first_commission_paid", true).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "更新卡佣金发放状态失败")
	}

	return nil
}

func (s *Service) TriggerOneTimeCommissionForCard(ctx context.Context, order *model.Order, cardID uint) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		return s.triggerOneTimeCommissionForCardInTx(ctx, tx, order, cardID)
	})
}

func (s *Service) triggerOneTimeCommissionForDeviceInTx(ctx context.Context, tx *gorm.DB, order *model.Order, deviceID uint) error {
	// 代购订单不触发一次性佣金和累计充值更新
	if order.IsPurchaseOnBehalf {
		return nil
	}

	device, err := s.deviceStore.GetByID(ctx, deviceID)
	if err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "获取设备信息失败")
	}

	if device.SeriesID == nil || device.ShopID == nil {
		return nil
	}

	allocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, *device.ShopID, *device.SeriesID)
	if err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "获取系列分配失败")
	}

	if !allocation.EnableOneTimeCommission {
		return nil
	}

	if allocation.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
		newAccumulated := device.AccumulatedRecharge + order.TotalAmount
		if err := tx.Model(&model.Device{}).Where("id = ?", deviceID).
			Update("accumulated_recharge", newAccumulated).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "更新设备累计充值金额失败")
		}
		device.AccumulatedRecharge = newAccumulated
	}

	if device.FirstCommissionPaid {
		return nil
	}

	var rechargeAmount int64
	switch allocation.OneTimeCommissionTrigger {
	case model.OneTimeCommissionTriggerSingleRecharge:
		rechargeAmount = order.TotalAmount
	case model.OneTimeCommissionTriggerAccumulatedRecharge:
		rechargeAmount = device.AccumulatedRecharge
	default:
		return nil
	}

	if rechargeAmount < allocation.OneTimeCommissionThreshold {
		return nil
	}

	commissionAmount, err := s.calculateOneTimeCommission(ctx, allocation, order.TotalAmount)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "计算一次性佣金失败")
	}

	if commissionAmount <= 0 {
		return nil
	}

	if device.ShopID == nil {
		return errors.New(errors.CodeInvalidParam, "设备未归属任何店铺，无法发放佣金")
	}

	record := &model.CommissionRecord{
		BaseModel: model.BaseModel{
			Creator: order.Creator,
			Updater: order.Updater,
		},
		ShopID:           *device.ShopID,
		OrderID:          order.ID,
		DeviceID:         &deviceID,
		CommissionSource: model.CommissionSourceOneTime,
		Amount:           commissionAmount,
		Status:           model.CommissionStatusReleased,
		Remark:           "一次性佣金（设备）",
	}

	if err := tx.Create(record).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
	}

	if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
	}

	if err := tx.Model(&model.Device{}).Where("id = ?", deviceID).
		Update("first_commission_paid", true).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "更新设备佣金发放状态失败")
	}

	return nil
}

func (s *Service) TriggerOneTimeCommissionForDevice(ctx context.Context, order *model.Order, deviceID uint) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		return s.triggerOneTimeCommissionForDeviceInTx(ctx, tx, order, deviceID)
	})
}

func (s *Service) calculateOneTimeCommission(ctx context.Context, allocation *model.ShopSeriesAllocation, orderAmount int64) (int64, error) {
	switch allocation.OneTimeCommissionType {
	case model.OneTimeCommissionTypeFixed:
		return s.calculateFixedCommission(allocation.OneTimeCommissionMode, allocation.OneTimeCommissionValue, orderAmount), nil
	case model.OneTimeCommissionTypeTiered:
		return s.calculateTieredCommission(ctx, allocation.ID, orderAmount)
	}
	return 0, nil
}

func (s *Service) calculateFixedCommission(mode string, value int64, orderAmount int64) int64 {
	if mode == model.CommissionModeFixed {
		return value
	} else if mode == model.CommissionModePercent {
		return orderAmount * value / 1000
	}
	return 0
}

func (s *Service) calculateTieredCommission(ctx context.Context, allocationID uint, orderAmount int64) (int64, error) {
	tiers, err := s.shopSeriesOneTimeCommissionTierStore.ListByAllocationID(ctx, allocationID)
	if err != nil {
		return 0, errors.Wrap(errors.CodeDatabaseError, err, "获取梯度配置失败")
	}

	if len(tiers) == 0 {
		return 0, nil
	}

	stats, err := s.commissionStatsService.GetCurrentStats(ctx, allocationID, "all_time")
	if err != nil {
		s.logger.Error("获取销售业绩统计失败", zap.Uint("allocation_id", allocationID), zap.Error(err))
		return 0, nil
	}

	if stats == nil {
		return 0, nil
	}

	var matchedTier *model.ShopSeriesOneTimeCommissionTier
	for _, tier := range tiers {
		var salesValue int64
		if tier.TierType == model.TierTypeSalesCount {
			salesValue = stats.TotalSalesCount
		} else if tier.TierType == model.TierTypeSalesAmount {
			salesValue = stats.TotalSalesAmount
		} else {
			continue
		}

		if salesValue >= tier.ThresholdValue {
			if matchedTier == nil || tier.ThresholdValue > matchedTier.ThresholdValue {
				matchedTier = tier
			}
		}
	}

	if matchedTier == nil {
		return 0, nil
	}

	if matchedTier.CommissionMode == model.CommissionModeFixed {
		return matchedTier.CommissionValue, nil
	} else if matchedTier.CommissionMode == model.CommissionModePercent {
		return orderAmount * matchedTier.CommissionValue / 1000, nil
	}

	return 0, nil
}

func (s *Service) creditCommissionInTx(ctx context.Context, tx *gorm.DB, record *model.CommissionRecord) error {
	var wallet model.Wallet
	if err := tx.Where("resource_type = ? AND resource_id = ? AND wallet_type = ?", "shop", record.ShopID, "commission").First(&wallet).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "获取店铺钱包失败")
	}

	balanceBefore := wallet.Balance
	result := tx.Model(&model.Wallet{}).
		Where("id = ? AND version = ?", wallet.ID, wallet.Version).
		Updates(map[string]any{
			"balance": gorm.Expr("balance + ?", record.Amount),
			"version": gorm.Expr("version + 1"),
		})
	if result.Error != nil {
		return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新钱包余额失败")
	}
	if result.RowsAffected == 0 {
		return errors.New(errors.CodeInternalError, "钱包版本冲突，请重试")
	}

	now := time.Now()
	if err := tx.Model(record).Updates(map[string]any{
		"balance_after": balanceBefore + record.Amount,
		"status":        model.CommissionStatusReleased,
		"released_at":   now,
	}).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "更新佣金记录失败")
	}

	remark := "佣金入账"
	transaction := &model.WalletTransaction{
		WalletID:        wallet.ID,
		UserID:          record.Creator,
		TransactionType: "commission",
		Amount:          record.Amount,
		BalanceBefore:   balanceBefore,
		BalanceAfter:    balanceBefore + record.Amount,
		Status:          1,
		ReferenceType:   stringPtr("commission"),
		ReferenceID:     &record.ID,
		Remark:          &remark,
		Creator:         record.Creator,
	}
	if err := tx.Create(transaction).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建钱包交易记录失败")
	}

	return nil
}

func (s *Service) CreditCommission(ctx context.Context, record *model.CommissionRecord) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		return s.creditCommissionInTx(ctx, tx, record)
	})
}

func stringPtr(s string) *string {
	return &s
}
