// Package refundapproval 收口退款申请与渠道无关审批的事务边界。
package refundapproval

import (
	"context"
	"fmt"
	"strings"

	"github.com/bytedance/sonic"
	"gorm.io/gorm"

	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"
)

// CreateCommand 描述已通过订单与金额校验的退款审批申请。
type CreateCommand struct {
	Refund             *model.RefundRequest
	SubmitterAccountID uint
}

// CreateResult 返回原子保存后的退款申请和初始审批状态。
type CreateResult struct {
	Refund         *model.RefundRequest
	SubmitterName  string
	ApprovalStatus int
}

// CreationService 原子创建退款申请、通用审批实例、企微上下文和提交 Outbox。
type CreationService struct {
	db       *gorm.DB
	approval approvalapp.Port
}

// NewCreationService 创建退款审批申请用例。
func NewCreationService(db *gorm.DB, approval approvalapp.Port) *CreationService {
	return &CreationService{db: db, approval: approval}
}

// Execute 在业务写入前校验审批渠道，并在同一事务冻结退款事实和审批事实。
func (s *CreationService) Execute(ctx context.Context, command CreateCommand) (*CreateResult, error) {
	if s == nil || s.db == nil || s.approval == nil {
		return nil, errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")
	}
	if command.Refund == nil || command.Refund.OrderID == 0 || command.SubmitterAccountID == 0 ||
		command.Refund.Creator != command.SubmitterAccountID || strings.TrimSpace(command.Refund.RefundNo) == "" {
		return nil, errors.New(errors.CodeInvalidParam)
	}
	account, err := s.loadSubmitter(ctx, command.SubmitterAccountID)
	if err != nil {
		return nil, err
	}
	preparation, err := s.approval.Prepare(ctx, approvalapp.PrepareRequest{
		BusinessType: constants.ApprovalBusinessTypeRefund, SubmitterAccountID: command.SubmitterAccountID,
		CorrelationID: command.Refund.RefundNo,
	})
	if err != nil {
		return nil, err
	}
	submitterSnapshot, requestSnapshot, err := refundSnapshots(command.Refund, account)
	if err != nil {
		return nil, err
	}
	var approvalStatus int
	err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Exec("SELECT pg_advisory_xact_lock(?)", int64(command.Refund.OrderID)).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款订单申请边界失败")
		}
		var activeCount int64
		if err := tx.WithContext(ctx).Model(&model.RefundRequest{}).
			Where("order_id = ? AND status IN ?", command.Refund.OrderID, []int{model.RefundStatusPending, model.RefundStatusApproved}).
			Count(&activeCount).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "复核订单活跃退款申请失败")
		}
		if activeCount > 0 {
			return errors.New(errors.CodeConflict, "该订单已存在退款申请")
		}
		if err := tx.WithContext(ctx).Create(command.Refund).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "创建退款申请失败")
		}
		reference, err := s.approval.CreateInTx(ctx, tx, approvalapp.CreateRequest{
			Preparation: preparation, BusinessType: constants.ApprovalBusinessTypeRefund,
			BusinessID: command.Refund.ID, SubmitterAccountID: command.SubmitterAccountID,
			SubmitterSnapshot: submitterSnapshot, RequestSnapshot: requestSnapshot,
			CorrelationID: command.Refund.RefundNo,
		})
		if err != nil {
			return err
		}
		result := tx.WithContext(ctx).Model(&model.RefundRequest{}).
			Where("id = ? AND approval_instance_id IS NULL", command.Refund.ID).
			Update("approval_instance_id", reference.InstanceID)
		if result.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, result.Error, "关联退款审批实例失败")
		}
		if result.RowsAffected != 1 {
			return errors.New(errors.CodeConflict, "退款审批实例关联已变化")
		}
		command.Refund.ApprovalInstanceID = &reference.InstanceID
		approvalStatus = reference.Status
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &CreateResult{Refund: command.Refund, SubmitterName: account.Username, ApprovalStatus: approvalStatus}, nil
}

func (s *CreationService) loadSubmitter(ctx context.Context, accountID uint) (*model.Account, error) {
	var account model.Account
	if err := s.db.WithContext(ctx).Where("id = ? AND status = ?", accountID, constants.StatusEnabled).First(&account).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeForbidden, "退款提交人账号不可用")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款提交人失败")
	}
	return &account, nil
}

func refundSnapshots(refund *model.RefundRequest, account *model.Account) ([]byte, []byte, error) {
	submitterSnapshot, err := sonic.Marshal(map[string]any{
		"account_id": account.ID, "account_name": account.Username, "user_type": account.UserType,
	})
	if err != nil {
		return nil, nil, errors.Wrap(errors.CodeInternalError, err, "编码退款提交人快照失败")
	}
	requestSnapshot, err := sonic.Marshal(map[string]any{
		constants.ApprovalFieldRefundNo:              refund.RefundNo,
		constants.ApprovalFieldOrderID:               refund.OrderID,
		constants.ApprovalFieldOrderNo:               refund.OrderNo,
		constants.ApprovalFieldAssetIdentifier:       refund.AssetIdentifier,
		constants.ApprovalFieldAssetType:             refund.OrderType,
		constants.ApprovalFieldActualReceivedAmount:  formatCentAmount(refund.ActualReceivedAmount),
		constants.ApprovalFieldRequestedRefundAmount: formatCentAmount(refund.RequestedRefundAmount),
		constants.ApprovalFieldRefundVoucherKey:      []string(refund.RefundVoucherKey),
		constants.ApprovalFieldRefundReason:          refund.RefundReason,
		constants.ApprovalFieldPackageUsageID:        refund.PackageUsageID,
		constants.ApprovalFieldSubmitterID:           account.ID,
		constants.ApprovalFieldSubmitterName:         account.Username,
	})
	if err != nil {
		return nil, nil, errors.Wrap(errors.CodeInternalError, err, "编码退款审批业务快照失败")
	}
	return submitterSnapshot, requestSnapshot, nil
}

func formatCentAmount(amount int64) string {
	return fmt.Sprintf("%d.%02d", amount/100, amount%100)
}
