// Package agent_recharge 提供代理预充值的业务逻辑服务
// 包含充值订单创建、线下确认、支付回调处理、列表查询等功能
package agent_recharge

import (
	"context"
	"fmt"
	"math/rand"
	"time"

	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
)

// AuditServiceInterface 审计日志服务接口
type AuditServiceInterface interface {
	LogOperation(ctx context.Context, log *model.AccountOperationLog)
}

// WechatConfigServiceInterface 支付配置服务接口
type WechatConfigServiceInterface interface {
	GetActiveConfig(ctx context.Context) (*model.WechatConfig, error)
}

// Service 代理预充值业务服务
// 负责代理钱包充值订单的创建、线下确认、回调处理等业务逻辑
type Service struct {
	db                  *gorm.DB
	agentRechargeStore  *postgres.AgentRechargeStore
	agentWalletStore    *postgres.AgentWalletStore
	agentWalletTxStore  *postgres.AgentWalletTransactionStore
	shopStore           *postgres.ShopStore
	accountStore        *postgres.AccountStore
	wechatConfigService WechatConfigServiceInterface
	auditService        AuditServiceInterface
	redis               *redis.Client
	logger              *zap.Logger
}

// New 创建代理预充值服务实例
func New(
	db *gorm.DB,
	agentRechargeStore *postgres.AgentRechargeStore,
	agentWalletStore *postgres.AgentWalletStore,
	agentWalletTxStore *postgres.AgentWalletTransactionStore,
	shopStore *postgres.ShopStore,
	accountStore *postgres.AccountStore,
	wechatConfigService WechatConfigServiceInterface,
	auditService AuditServiceInterface,
	rdb *redis.Client,
	logger *zap.Logger,
) *Service {
	return &Service{
		db:                  db,
		agentRechargeStore:  agentRechargeStore,
		agentWalletStore:    agentWalletStore,
		agentWalletTxStore:  agentWalletTxStore,
		shopStore:           shopStore,
		accountStore:        accountStore,
		wechatConfigService: wechatConfigService,
		auditService:        auditService,
		redis:               rdb,
		logger:              logger,
	}
}

// Create 创建代理充值订单
// POST /api/admin/agent-recharges
func (s *Service) Create(ctx context.Context, req *dto.CreateAgentRechargeRequest) (*dto.AgentRechargeResponse, error) {
	userID := middleware.GetUserIDFromContext(ctx)
	userType := middleware.GetUserTypeFromContext(ctx)
	userShopID := middleware.GetShopIDFromContext(ctx)

	// 代理只能充自己店铺
	if userType == constants.UserTypeAgent && req.ShopID != userShopID {
		return nil, errors.New(errors.CodeForbidden, "代理只能为自己的店铺充值")
	}

	// 线下充值仅平台可用
	if req.PaymentMethod == "offline" && userType != constants.UserTypePlatform && userType != constants.UserTypeSuperAdmin {
		return nil, errors.New(errors.CodeForbidden, "线下充值仅平台管理员可操作")
	}

	if req.Amount < constants.AgentRechargeMinAmount || req.Amount > constants.AgentRechargeMaxAmount {
		return nil, errors.New(errors.CodeInvalidParam, "充值金额超出允许范围")
	}

	// 查找目标店铺的主钱包
	wallet, err := s.agentWalletStore.GetMainWallet(ctx, req.ShopID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "目标店铺主钱包不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询店铺主钱包失败")
	}

	// 查询店铺名称
	shop, err := s.shopStore.GetByID(ctx, req.ShopID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询店铺失败")
	}

	// 在线支付需要查询生效的支付配置
	var paymentConfigID *uint
	var paymentChannel string
	if req.PaymentMethod == "wechat" {
		activeConfig, cfgErr := s.wechatConfigService.GetActiveConfig(ctx)
		if cfgErr != nil || activeConfig == nil {
			return nil, errors.New(errors.CodeNoPaymentConfig, "当前无可用的支付配置，请联系管理员")
		}
		paymentConfigID = &activeConfig.ID
		paymentChannel = activeConfig.ProviderType
	} else {
		paymentChannel = "offline"
	}

	rechargeNo := s.generateRechargeNo()

	record := &model.AgentRechargeRecord{
		UserID:          userID,
		AgentWalletID:   wallet.ID,
		ShopID:          req.ShopID,
		RechargeNo:      rechargeNo,
		Amount:          req.Amount,
		PaymentMethod:   req.PaymentMethod,
		PaymentChannel:  &paymentChannel,
		PaymentConfigID: paymentConfigID,
		Status:          constants.RechargeStatusPending,
		ShopIDTag:       wallet.ShopIDTag,
		EnterpriseIDTag: wallet.EnterpriseIDTag,
	}

	if err := s.agentRechargeStore.Create(ctx, record); err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建充值订单失败")
	}

	s.logger.Info("创建代理充值订单成功",
		zap.Uint("recharge_id", record.ID),
		zap.String("recharge_no", rechargeNo),
		zap.Int64("amount", req.Amount),
		zap.Uint("shop_id", req.ShopID),
		zap.Uint("user_id", userID),
	)

	return toResponse(record, shop.ShopName), nil
}

// OfflinePay 线下充值确认
// POST /api/admin/agent-recharges/:id/offline-pay
func (s *Service) OfflinePay(ctx context.Context, id uint, req *dto.AgentOfflinePayRequest) (*dto.AgentRechargeResponse, error) {
	userID := middleware.GetUserIDFromContext(ctx)
	userType := middleware.GetUserTypeFromContext(ctx)

	// 仅平台账号可操作
	if userType != constants.UserTypePlatform && userType != constants.UserTypeSuperAdmin {
		return nil, errors.New(errors.CodeForbidden, "仅平台管理员可确认线下充值")
	}

	// 验证操作密码
	account, err := s.accountStore.GetByID(ctx, userID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询操作人账号失败")
	}
	if err := bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(req.OperationPassword)); err != nil {
		return nil, errors.New(errors.CodeInvalidParam, "操作密码错误")
	}

	record, err := s.agentRechargeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "充值记录不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录失败")
	}

	if record.PaymentMethod != "offline" {
		return nil, errors.New(errors.CodeInvalidParam, "该订单非线下充值，不支持此操作")
	}
	if record.Status != constants.RechargeStatusPending {
		return nil, errors.New(errors.CodeInvalidParam, "该订单状态不允许确认支付")
	}

	// 查询钱包（事务内需要用到 version）
	wallet, err := s.agentWalletStore.GetByID(ctx, record.AgentWalletID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询代理钱包失败")
	}

	now := time.Now()
	err = s.db.Transaction(func(tx *gorm.DB) error {
		// 条件更新充值记录状态
		result := tx.Model(&model.AgentRechargeRecord{}).
			Where("id = ? AND status = ?", record.ID, constants.RechargeStatusPending).
			Updates(map[string]interface{}{
				"status":       constants.RechargeStatusCompleted,
				"paid_at":      now,
				"completed_at": now,
			})
		if result.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新充值记录状态失败")
		}
		if result.RowsAffected == 0 {
			return errors.New(errors.CodeInvalidParam, "充值记录状态已变更")
		}

		// 增加钱包余额（乐观锁）
		balanceResult := tx.Model(&model.AgentWallet{}).
			Where("id = ? AND version = ?", wallet.ID, wallet.Version).
			Updates(map[string]interface{}{
				"balance": gorm.Expr("balance + ?", record.Amount),
				"version": gorm.Expr("version + 1"),
			})
		if balanceResult.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, balanceResult.Error, "更新钱包余额失败")
		}
		if balanceResult.RowsAffected == 0 {
			return errors.New(errors.CodeInternalError, "钱包余额更新冲突，请重试")
		}

		// 创建钱包交易记录
		remark := "线下充值确认"
		refType := "topup"
		txRecord := &model.AgentWalletTransaction{
			AgentWalletID:   wallet.ID,
			ShopID:          record.ShopID,
			UserID:          userID,
			TransactionType: constants.AgentTransactionTypeRecharge,
			Amount:          record.Amount,
			BalanceBefore:   wallet.Balance,
			BalanceAfter:    wallet.Balance + record.Amount,
			Status:          constants.TransactionStatusSuccess,
			ReferenceType:   &refType,
			ReferenceID:     &record.ID,
			Remark:          &remark,
			Creator:         userID,
			ShopIDTag:       wallet.ShopIDTag,
			EnterpriseIDTag: wallet.EnterpriseIDTag,
		}
		if err := s.agentWalletTxStore.CreateWithTx(ctx, tx, txRecord); err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "创建钱包交易记录失败")
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	// 异步记录审计日志
	go s.auditService.LogOperation(ctx, &model.AccountOperationLog{
		OperatorID:    userID,
		OperatorType:  userType,
		OperationType: "offline_recharge_confirm",
		OperationDesc: fmt.Sprintf("确认线下充值，充值单号: %s，金额: %d分", record.RechargeNo, record.Amount),
		RequestID:     middleware.GetRequestIDFromContext(ctx),
		IPAddress:     middleware.GetIPFromContext(ctx),
		UserAgent:     middleware.GetUserAgentFromContext(ctx),
	})

	shop, _ := s.shopStore.GetByID(ctx, record.ShopID)
	shopName := ""
	if shop != nil {
		shopName = shop.ShopName
	}

	// 更新本地对象以反映最新状态
	record.Status = constants.RechargeStatusCompleted
	record.PaidAt = &now
	record.CompletedAt = &now

	return toResponse(record, shopName), nil
}

// HandlePaymentCallback 处理支付回调
// 幂等处理：status != 待支付则直接返回成功
func (s *Service) HandlePaymentCallback(ctx context.Context, rechargeNo string, paymentMethod string, paymentTransactionID string) error {
	record, err := s.agentRechargeStore.GetByRechargeNo(ctx, rechargeNo)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "充值订单不存在")
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
	}

	// 幂等检查
	if record.Status != constants.RechargeStatusPending {
		s.logger.Info("代理充值订单已处理，跳过",
			zap.String("recharge_no", rechargeNo),
			zap.Int("status", record.Status),
		)
		return nil
	}

	wallet, err := s.agentWalletStore.GetByID(ctx, record.AgentWalletID)
	if err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "查询代理钱包失败")
	}

	now := time.Now()
	err = s.db.Transaction(func(tx *gorm.DB) error {
		// 条件更新（WHERE status = 1）
		result := tx.Model(&model.AgentRechargeRecord{}).
			Where("id = ? AND status = ?", record.ID, constants.RechargeStatusPending).
			Updates(map[string]interface{}{
				"status":                 constants.RechargeStatusCompleted,
				"payment_transaction_id": paymentTransactionID,
				"paid_at":                now,
				"completed_at":           now,
			})
		if result.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新充值记录状态失败")
		}
		if result.RowsAffected == 0 {
			return nil
		}

		// 增加钱包余额（乐观锁）
		balanceResult := tx.Model(&model.AgentWallet{}).
			Where("id = ? AND version = ?", wallet.ID, wallet.Version).
			Updates(map[string]interface{}{
				"balance": gorm.Expr("balance + ?", record.Amount),
				"version": gorm.Expr("version + 1"),
			})
		if balanceResult.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, balanceResult.Error, "更新钱包余额失败")
		}
		if balanceResult.RowsAffected == 0 {
			return errors.New(errors.CodeInternalError, "钱包余额更新冲突，请重试")
		}

		// 创建交易记录
		remark := "在线支付充值"
		refType := "topup"
		txRecord := &model.AgentWalletTransaction{
			AgentWalletID:   wallet.ID,
			ShopID:          record.ShopID,
			UserID:          record.UserID,
			TransactionType: constants.AgentTransactionTypeRecharge,
			Amount:          record.Amount,
			BalanceBefore:   wallet.Balance,
			BalanceAfter:    wallet.Balance + record.Amount,
			Status:          constants.TransactionStatusSuccess,
			ReferenceType:   &refType,
			ReferenceID:     &record.ID,
			Remark:          &remark,
			Creator:         record.UserID,
			ShopIDTag:       wallet.ShopIDTag,
			EnterpriseIDTag: wallet.EnterpriseIDTag,
		}
		if err := s.agentWalletTxStore.CreateWithTx(ctx, tx, txRecord); err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "创建钱包交易记录失败")
		}

		return nil
	})

	if err != nil {
		return err
	}

	s.logger.Info("代理充值支付回调处理成功",
		zap.String("recharge_no", rechargeNo),
		zap.Int64("amount", record.Amount),
		zap.Uint("shop_id", record.ShopID),
	)

	return nil
}

// GetByID 根据ID查询充值订单详情
// GET /api/admin/agent-recharges/:id
func (s *Service) GetByID(ctx context.Context, id uint) (*dto.AgentRechargeResponse, error) {
	record, err := s.agentRechargeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "充值记录不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录失败")
	}

	shop, _ := s.shopStore.GetByID(ctx, record.ShopID)
	shopName := ""
	if shop != nil {
		shopName = shop.ShopName
	}

	return toResponse(record, shopName), nil
}

// List 分页查询充值订单列表
// GET /api/admin/agent-recharges
func (s *Service) List(ctx context.Context, req *dto.AgentRechargeListRequest) ([]*dto.AgentRechargeResponse, int64, error) {
	page := req.Page
	pageSize := req.PageSize
	if page == 0 {
		page = 1
	}
	if pageSize == 0 {
		pageSize = constants.DefaultPageSize
	}

	query := s.db.WithContext(ctx).Model(&model.AgentRechargeRecord{})

	if req.ShopID != nil {
		query = query.Where("shop_id = ?", *req.ShopID)
	}
	if req.Status != nil {
		query = query.Where("status = ?", *req.Status)
	}
	if req.StartDate != "" {
		query = query.Where("created_at >= ?", req.StartDate+" 00:00:00")
	}
	if req.EndDate != "" {
		query = query.Where("created_at <= ?", req.EndDate+" 23:59:59")
	}

	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录总数失败")
	}

	var records []*model.AgentRechargeRecord
	offset := (page - 1) * pageSize
	if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&records).Error; err != nil {
		return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录列表失败")
	}

	// 批量查询店铺名称
	shopIDs := make([]uint, 0, len(records))
	for _, r := range records {
		shopIDs = append(shopIDs, r.ShopID)
	}
	shopMap := make(map[uint]string)
	if len(shopIDs) > 0 {
		shops, err := s.shopStore.GetByIDs(ctx, shopIDs)
		if err == nil {
			for _, sh := range shops {
				shopMap[sh.ID] = sh.ShopName
			}
		}
	}

	list := make([]*dto.AgentRechargeResponse, 0, len(records))
	for _, r := range records {
		list = append(list, toResponse(r, shopMap[r.ShopID]))
	}

	return list, total, nil
}

// generateRechargeNo 生成代理充值订单号
// 格式: ARCH + 14位时间戳 + 6位随机数
func (s *Service) generateRechargeNo() string {
	timestamp := time.Now().Format("20060102150405")
	randomNum := rand.Intn(1000000)
	return fmt.Sprintf("%s%s%06d", constants.AgentRechargeOrderPrefix, timestamp, randomNum)
}

func agentRechargeStatusName(status int) string {
	switch status {
	case constants.RechargeStatusPending:
		return "待支付"
	case constants.RechargeStatusPaid:
		return "已支付"
	case constants.RechargeStatusCompleted:
		return "已完成"
	case constants.RechargeStatusClosed:
		return "已关闭"
	case constants.RechargeStatusRefunded:
		return "已退款"
	default:
		return "未知"
	}
}

// toResponse 将模型转换为响应 DTO
func toResponse(record *model.AgentRechargeRecord, shopName string) *dto.AgentRechargeResponse {
	resp := &dto.AgentRechargeResponse{
		ID:            record.ID,
		RechargeNo:    record.RechargeNo,
		ShopID:        record.ShopID,
		ShopName:      shopName,
		AgentWalletID: record.AgentWalletID,
		Amount:        record.Amount,
		PaymentMethod: record.PaymentMethod,
		Status:        record.Status,
		StatusName:    agentRechargeStatusName(record.Status),
		CreatedAt:     record.CreatedAt.Format("2006-01-02 15:04:05"),
		UpdatedAt:     record.UpdatedAt.Format("2006-01-02 15:04:05"),
	}

	if record.PaymentChannel != nil {
		resp.PaymentChannel = *record.PaymentChannel
	}
	if record.PaymentConfigID != nil {
		resp.PaymentConfigID = record.PaymentConfigID
	}
	if record.PaymentTransactionID != nil {
		resp.PaymentTransactionID = *record.PaymentTransactionID
	}
	if record.PaidAt != nil {
		t := record.PaidAt.Format("2006-01-02 15:04:05")
		resp.PaidAt = &t
	}
	if record.CompletedAt != nil {
		t := record.CompletedAt.Format("2006-01-02 15:04:05")
		resp.CompletedAt = &t
	}

	return resp
}
