package refund

import (
	"context"
	"strings"

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

	approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

// Handle 幂等处理退款的渠道无关审批终态。
func (s *Service) Handle(ctx context.Context, event approvalapp.TerminalDecisionEvent) error {
	if s == nil || s.db == nil || event.BusinessType != constants.ApprovalBusinessTypeRefund ||
		event.BusinessID == 0 || event.InstanceID == 0 {
		return errors.New(errors.CodeInvalidParam, "退款审批终态参数无效")
	}
	switch event.Decision {
	case constants.ApprovalDecisionApproved:
		return s.applyApprovedDecision(ctx, event)
	case constants.ApprovalDecisionRejected, constants.ApprovalDecisionCancelled, constants.ApprovalDecisionDeleted:
		return s.applyClosedDecision(ctx, event)
	case constants.ApprovalDecisionRevokedAfterApproved:
		return nil
	default:
		return errors.New(errors.CodeInvalidParam, "不支持的退款审批终态")
	}
}

func (s *Service) applyApprovedDecision(ctx context.Context, event approvalapp.TerminalDecisionEvent) error {
	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var refund model.RefundRequest
		if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", event.BusinessID).First(&refund).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
			}
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款申请失败")
		}
		if refund.ApprovalInstanceID == nil || *refund.ApprovalInstanceID != event.InstanceID {
			return errors.New(errors.CodeConflict, "退款申请关联的审批实例不一致")
		}
		if refund.Status != model.RefundStatusPending && refund.Status != model.RefundStatusApproved {
			return errors.New(errors.CodeInvalidStatus, "退款申请状态不允许审批通过")
		}
		var order model.Order
		if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", refund.OrderID).First(&order).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款关联订单失败")
		}
		approvedAmount := refund.RequestedRefundAmount
		if err := validateApprovedRefundAmount(approvedAmount, refund.RequestedRefundAmount, &order); err != nil {
			return err
		}
		if refund.Status == model.RefundStatusPending {
			result := tx.WithContext(ctx).Model(&model.RefundRequest{}).
				Where("id = ? AND status = ?", refund.ID, model.RefundStatusPending).
				Updates(map[string]any{
					"status": model.RefundStatusApproved, "processed_at": event.OccurredAt,
					"approved_refund_amount": approvedAmount, "remark": "企业微信审批通过",
					"updated_at": event.OccurredAt,
				})
			if result.Error != nil {
				return errors.Wrap(errors.CodeDatabaseError, result.Error, "完成退款审批申请失败")
			}
			if result.RowsAffected != 1 {
				return errors.New(errors.CodeConflict, "退款申请状态已变化")
			}
		}
		switch order.PaymentStatus {
		case model.PaymentStatusPaid:
			result := tx.WithContext(ctx).Model(&model.Order{}).
				Where("id = ? AND payment_status = ?", order.ID, model.PaymentStatusPaid).
				Updates(map[string]any{"payment_status": model.PaymentStatusRefunded, "updated_at": event.OccurredAt})
			if result.Error != nil {
				return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新订单退款状态失败")
			}
			if result.RowsAffected != 1 {
				return errors.New(errors.CodeConflict, "订单退款状态已变化")
			}
			order.PaymentStatus = model.PaymentStatusRefunded
		case model.PaymentStatusRefunded:
		default:
			return errors.New(errors.CodeInvalidStatus, "订单状态不允许完成退款")
		}
		return s.refundWalletPayment(ctx, tx, &refund, &order, approvedAmount, event.SubmitterAccountID)
	})
	if err != nil {
		return err
	}
	return s.ensureApprovedPostProcessing(ctx, event.BusinessID)
}

func (s *Service) applyClosedDecision(ctx context.Context, event approvalapp.TerminalDecisionEvent) error {
	reason := map[string]string{
		constants.ApprovalDecisionRejected:  "企业微信审批已拒绝",
		constants.ApprovalDecisionCancelled: "企业微信审批已撤销",
		constants.ApprovalDecisionDeleted:   "企业微信审批已删除",
	}[event.Decision]
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var refund model.RefundRequest
		if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", event.BusinessID).First(&refund).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款申请失败")
		}
		if refund.ApprovalInstanceID == nil || *refund.ApprovalInstanceID != event.InstanceID {
			return errors.New(errors.CodeConflict, "退款申请关联的审批实例不一致")
		}
		if refund.Status == model.RefundStatusRejected {
			return nil
		}
		if refund.Status != model.RefundStatusPending {
			return errors.New(errors.CodeInvalidStatus, "退款申请状态不允许结束审批")
		}
		result := tx.WithContext(ctx).Model(&model.RefundRequest{}).
			Where("id = ? AND status = ?", refund.ID, model.RefundStatusPending).
			Updates(map[string]any{
				"status": model.RefundStatusRejected, "processed_at": event.OccurredAt,
				"reject_reason": strings.TrimSpace(reason), "updated_at": event.OccurredAt,
			})
		if result.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, result.Error, "结束退款审批申请失败")
		}
		if result.RowsAffected != 1 {
			return errors.New(errors.CodeConflict, "退款申请状态已变化")
		}
		return nil
	})
}

func (s *Service) ensureApprovedPostProcessing(ctx context.Context, refundID uint) error {
	s.deductAllCommission(ctx, refundID)
	s.handleRefundAssetProcessing(ctx, refundID)
	var refund model.RefundRequest
	if err := s.db.WithContext(ctx).Select("commission_deducted", "asset_reset").Where("id = ?", refundID).First(&refund).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "复核退款后处理状态失败")
	}
	if !refund.CommissionDeducted || !refund.AssetReset {
		return errors.New(errors.CodeServiceUnavailable, "退款后处理尚未全部完成，将自动重试")
	}
	return nil
}

var _ approvalapp.BusinessDecisionHandler = (*Service)(nil)
