package apple

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/hibiken/asynq"
	commonLogic "github.com/perfect-panel/server/internal/logic/common"
	iapmodel "github.com/perfect-panel/server/internal/model/iap/apple"
	ordermodel "github.com/perfect-panel/server/internal/model/order"
	"github.com/perfect-panel/server/internal/model/subscribe"
	"github.com/perfect-panel/server/internal/model/user"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/internal/types"
	"github.com/perfect-panel/server/pkg/constant"
	iapapple "github.com/perfect-panel/server/pkg/iap/apple"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/xerr"
	queueType "github.com/perfect-panel/server/queue/types"
	"github.com/pkg/errors"
	"gorm.io/gorm"
)

type AttachTransactionLogic struct {
	logger.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

const (
	orderTypeSubscribe  uint8 = 1
	orderStatusPending  uint8 = 1
	orderStatusPaid     uint8 = 2
	orderStatusClosed   uint8 = 3
	orderStatusFinished uint8 = 5
)

func NewAttachTransactionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AttachTransactionLogic {
	return &AttachTransactionLogic{
		Logger: logger.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest) (*types.AttachAppleTransactionResponse, error) {
	l.Infow("开始绑定 Apple IAP 交易", logger.Field("orderNo", req.OrderNo))
	u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
	if !ok || u == nil {
		l.Errorw("无效访问，用户信息缺失")
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "invalid access")
	}
	// 解析家庭权益：member 的订阅归属于 owner
	entitlement, entErr := commonLogic.ResolveEntitlementUser(l.ctx, l.svcCtx.DB, u.Id)
	if entErr != nil {
		l.Errorw("解析家庭权益失败", logger.Field("userId", u.Id), logger.Field("error", entErr.Error()))
		return nil, entErr
	}

	if strings.TrimSpace(req.OrderNo) == "" {
		l.Errorw("参数错误，orderNo 不能为空")
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "order_no is required")
	}
	orderInfo, orderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
	switch {
	case errors.Is(orderErr, gorm.ErrRecordNotFound):
		l.Errorw("订单不存在", logger.Field("orderNo", req.OrderNo))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.OrderNotExist), "order not exist")
	case orderErr != nil:
		l.Errorw("查询订单失败", logger.Field("orderNo", req.OrderNo), logger.Field("error", orderErr.Error()))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order error: %v", orderErr.Error())
	case orderInfo == nil || orderInfo.Id == 0:
		l.Errorw("订单不存在", logger.Field("orderNo", req.OrderNo))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.OrderNotExist), "order not exist")
	case orderInfo.UserId != u.Id:
		l.Errorw("订单与当前用户不匹配", logger.Field("orderNo", req.OrderNo), logger.Field("orderUserId", orderInfo.UserId), logger.Field("userId", u.Id))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "order owner mismatch")
	}
	commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "iap_attach_start",
		"[SubscriptionFlow] apple iap attach flow started",
		append(commonLogic.OrderTraceFields(orderInfo),
			logger.Field("request_user_id", u.Id),
			logger.Field("effective_user_id", entitlement.EffectiveUserID),
		)...,
	)
	isNewPurchaseOrder := orderInfo.Type == orderTypeSubscribe
	if isNewPurchaseOrder {
		l.Infow("首购订单将只由订单激活流程创建订阅", logger.Field("orderNo", req.OrderNo), logger.Field("orderType", orderInfo.Type))
	}

	txPayload, err := iapapple.VerifyTransactionJWS(req.SignedTransactionJWS)
	if err != nil {
		l.Errorw("JWS 验签失败", logger.Field("error", err.Error()))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "invalid jws")
	}
	l.Infow("JWS 验签成功", logger.Field("productId", txPayload.ProductId), logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("purchaseAt", txPayload.PurchaseDate))
	commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "iap_attach_verified",
		"[SubscriptionFlow] apple iap transaction verified",
		append(commonLogic.OrderTraceFields(orderInfo),
			logger.Field("product_id", txPayload.ProductId),
			logger.Field("original_transaction_tail", commonLogic.SensitiveTail(txPayload.OriginalTransactionId)),
			logger.Field("transaction_id_tail", commonLogic.SensitiveTail(txPayload.TransactionId)),
		)...,
	)
	tradeNoCandidates := l.getAppleTradeNoCandidates(txPayload)
	existingOrderNo, validateErr := l.validateOrderTradeNoBinding(orderInfo, tradeNoCandidates)
	if validateErr != nil {
		l.Errorw("Apple 交易绑定校验失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNoCandidates", tradeNoCandidates), logger.Field("error", validateErr.Error()))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "apple transaction binding error")
	}
	if existingOrderNo != "" {
		l.Errorw("Apple 交易重复绑定，返回已绑定订单", logger.Field("orderNo", req.OrderNo), logger.Field("existingOrderNo", existingOrderNo), logger.Field("tradeNoCandidates", tradeNoCandidates))
		// 关闭当前 pending 订单，避免产生孤儿订单
		if orderInfo.Status == orderStatusPending {
			if closeErr := l.svcCtx.DB.Model(&ordermodel.Order{}).
				Where("order_no = ? AND status = ?", req.OrderNo, orderStatusPending).
				Update("status", orderStatusClosed).Error; closeErr != nil {
				l.Errorw("关闭重复订单失败", logger.Field("orderNo", req.OrderNo), logger.Field("error", closeErr.Error()))
			} else {
				l.Infow("已关闭重复 pending 订单", logger.Field("orderNo", req.OrderNo), logger.Field("existingOrderNo", existingOrderNo))
			}
		}
		return &types.AttachAppleTransactionResponse{ExistingOrderNo: existingOrderNo}, nil
	}
	tradeNo := ""
	if len(tradeNoCandidates) > 0 {
		tradeNo = tradeNoCandidates[0]
	}
	orderAlreadyBound := containsString(tradeNoCandidates, orderInfo.TradeNo)
	// idempotency: check existing transaction by original id
	var existTx *iapmodel.Transaction
	existTx, existTxErr := iapmodel.NewModel(l.svcCtx.DB, l.svcCtx.Redis).FindByOriginalId(l.ctx, txPayload.OriginalTransactionId)
	if existTxErr != nil && !errors.Is(existTxErr, gorm.ErrRecordNotFound) {
		l.Errorw("查询 IAP 事务记录失败", logger.Field("error", existTxErr.Error()), logger.Field("originalTransactionId", txPayload.OriginalTransactionId))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find iap transaction error: %v", existTxErr.Error())
	}
	l.Infow("幂等等检查", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("exists", existTx != nil && existTx.Id > 0))

	// 解析 Apple 商品ID中的单位与数量：支持 dayN / monthN / yearN
	var parsedUnit string
	var parsedQuantity int64
	if parsed := iapapple.ParseProductIdDuration(txPayload.ProductId); parsed != nil {
		parsedUnit = parsed.Unit
		parsedQuantity = parsed.Quantity
	}
	l.Infow("商品映射解析", logger.Field("productId", txPayload.ProductId), logger.Field("解析单位", parsedUnit), logger.Field("解析数量", parsedQuantity))

	// 基于订阅列表的折扣配置做匹配：UnitTime=Day 且 Discount.quantity == parsedQuantity
	var duration int64
	var tier string
	var subscribeId int64
	if parsedQuantity > 0 {
		_, subs, e := l.svcCtx.SubscribeModel.FilterList(l.ctx, &subscribe.FilterParams{
			Page:            1,
			Size:            9999,
			Show:            true,
			Sell:            true,
			DefaultLanguage: true,
		})
		if e == nil && len(subs) > 0 {
			for _, item := range subs {
				if parsedUnit != "" && !strings.EqualFold(item.UnitTime, parsedUnit) {
					continue
				}
				var discounts []types.SubscribeDiscount
				if item.Discount != "" {
					_ = json.Unmarshal([]byte(item.Discount), &discounts)
				}
				for _, d := range discounts {
					if int64(d.Quantity) == parsedQuantity {
						switch parsedUnit {
						case "Day":
							duration = parsedQuantity
						case "Month":
							duration = parsedQuantity * 30
						case "Year":
							duration = parsedQuantity * 365
						default:
							duration = parsedQuantity
						}
						subscribeId = item.Id
						tier = item.Name
						l.Infow("订阅映射命中", logger.Field("subscribeId", subscribeId), logger.Field("name", tier), logger.Field("durationDays", duration))
						break
					}
				}
				if subscribeId > 0 {
					break
				}
			}
		} else {
			l.Infow("订阅列表为空或查询失败", logger.Field("error", func() string {
				if e != nil {
					return e.Error()
				}
				return ""
			}()))
		}
	}
	if subscribeId == 0 {
		// fallback from order_no if provided
		duration = orderInfo.Quantity
		subscribeId = orderInfo.SubscribeId
		l.Infow("使用订单信息回退", logger.Field("orderNo", req.OrderNo), logger.Field("durationDays", duration), logger.Field("subscribeId", subscribeId))
		// final fallback: use request fields
		if duration <= 0 {
			duration = req.DurationDays
		}
		if tier == "" {
			tier = req.Tier
		}
		if subscribeId <= 0 {
			subscribeId = req.SubscribeId
		}
		l.Infow("使用请求参数回退", logger.Field("durationDays", duration), logger.Field("tier", tier), logger.Field("subscribeId", subscribeId))
		if duration <= 0 || subscribeId <= 0 {
			l.Errorw("商品识别失败", logger.Field("durationDays", duration), logger.Field("tier", tier), logger.Field("subscribeId", subscribeId))
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "unknown product")
		}
	}
	exp := iapapple.CalcExpire(txPayload.PurchaseDate, duration)
	shouldAccumulate := duration > 0 && (strings.EqualFold(parsedUnit, "Day") || strings.EqualFold(txPayload.Type, "Consumable"))
	accumulateDuration := int64(0)
	if shouldAccumulate {
		accumulateDuration = duration
	}
	l.Infow("计算订阅到期时间", logger.Field("expireAt", exp), logger.Field("expireUnix", exp.Unix()))
	var orderLinkedSub *user.Subscribe
	if !isNewPurchaseOrder && orderInfo != nil && orderInfo.SubscribeToken != "" {
		orderSub, subErr := l.svcCtx.UserModel.FindOneSubscribeByToken(l.ctx, orderInfo.SubscribeToken)
		switch {
		case subErr == nil && orderSub != nil && orderSub.Id > 0:
			if orderSub.UserId != u.Id && orderSub.UserId != entitlement.EffectiveUserID {
				l.Errorw("订单订阅与当前用户不匹配", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("orderSubUserId", orderSub.UserId), logger.Field("userId", u.Id), logger.Field("effectiveUserId", entitlement.EffectiveUserID))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "order subscribe owner mismatch")
			}
			orderLinkedSub = orderSub
			subscribeId = orderSub.SubscribeId
			l.Infow("IAP 绑定命中订单订阅", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("userSubscribeId", orderSub.Id), logger.Field("subscribeToken", orderInfo.SubscribeToken))
		case subErr != nil && !errors.Is(subErr, gorm.ErrRecordNotFound):
			l.Errorw("查询订单订阅失败", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("subscribeToken", orderInfo.SubscribeToken), logger.Field("error", subErr.Error()))
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order subscribe error: %v", subErr.Error())
		}
	}
	var singleModeAnchorSub *user.Subscribe
	if !isNewPurchaseOrder && l.svcCtx.Config.Subscribe.SingleModel && orderLinkedSub == nil {
		anchorSub, anchorErr := findSingleModeMergeTarget(l.ctx, l.svcCtx, entitlement.EffectiveUserID, subscribeId)
		switch {
		case errors.Is(anchorErr, commonLogic.ErrSingleModePlanMismatch):
			l.Errorw("单订阅模式下 IAP 套餐不匹配", logger.Field("userId", u.Id), logger.Field("orderNo", req.OrderNo), logger.Field("iapSubscribeId", subscribeId))
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.SingleSubscribePlanMismatch), "single subscribe mode plan mismatch")
		case anchorErr == nil && anchorSub != nil && anchorSub.Id > 0:
			singleModeAnchorSub = anchorSub
			subscribeId = anchorSub.SubscribeId
			l.Infow("IAP 绑定命中单订阅锚点", logger.Field("userSubscribeId", anchorSub.Id), logger.Field("subscribeId", anchorSub.SubscribeId))
		case errors.Is(anchorErr, gorm.ErrRecordNotFound):
		case anchorErr != nil:
			l.Errorw("查询单订阅锚点失败", logger.Field("userId", u.Id), logger.Field("error", anchorErr.Error()))
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find single mode anchor subscribe error: %v", anchorErr.Error())
		}
	}

	existSub, existSubErr := l.findIAPSubscribeByOriginalTransactionID(txPayload.OriginalTransactionId)
	if existSubErr != nil && !errors.Is(existSubErr, gorm.ErrRecordNotFound) {
		l.Errorw("查询 IAP 订阅失败", logger.Field("error", existSubErr.Error()), logger.Field("originalTransactionId", txPayload.OriginalTransactionId))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find iap subscribe error: %v", existSubErr.Error())
	}

	if existTx != nil && existTx.Id > 0 {
		if orderAlreadyBound {
			expiresAt := exp.Unix()
			if existSub != nil && existSub.Id > 0 {
				expiresAt = existSub.ExpireTime.Unix()
			}
			if orderLinkedSub != nil && orderLinkedSub.Id > 0 {
				expiresAt = orderLinkedSub.ExpireTime.Unix()
			}
			if singleModeAnchorSub != nil && singleModeAnchorSub.Id > 0 {
				expiresAt = singleModeAnchorSub.ExpireTime.Unix()
			}
			if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
				l.Errorw("回填订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
			}
			if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, 0); syncErr != nil {
				l.Errorw("同订单幂等同步失败", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
			}
			l.Infow("事务已处理，同订单幂等返回", logger.Field("orderNo", req.OrderNo), logger.Field("expiresAt", expiresAt))
			return &types.AttachAppleTransactionResponse{ExpiresAt: expiresAt, Tier: tier}, nil
		}

		if isNewPurchaseOrder {
			if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
				l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
			}
			if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, 0); syncErr != nil {
				l.Errorw("事务已处理但同步订单状态失败", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
			}
			l.Infow("事务已处理，首购订单等待激活队列发放订阅", logger.Field("orderNo", req.OrderNo), logger.Field("expiresAt", exp.Unix()))
			return &types.AttachAppleTransactionResponse{ExpiresAt: exp.Unix(), Tier: tier}, nil
		}

		switch {
		case existSubErr == nil && existSub != nil && existSub.Id > 0:
			// 不在此处更新 expire_time：由激活队列统一写入，避免双重叠加天数
			if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
				l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
			}
			if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix()); syncErr != nil {
				l.Errorw("同步订单状态失败(existSub)", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
			}
			estimatedExpire := l.calcIAPRenewalExpire(existSub.ExpireTime, exp, accumulateDuration)
			l.Infow("事务已处理，订阅到期时间由队列更新", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("tier", tier), logger.Field("estimatedExpiresAt", estimatedExpire.Unix()))
			return &types.AttachAppleTransactionResponse{
				ExpiresAt: estimatedExpire.Unix(),
				Tier:      tier,
			}, nil
		}
		if orderLinkedSub != nil {
			// 不在此处更新 expire_time：由激活队列统一写入，避免双重叠加天数
			if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
				l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
			}
			if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix()); syncErr != nil {
				l.Errorw("同步订单状态失败(orderLinkedSub)", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
			}
			estimatedExpire := l.calcIAPRenewalExpire(orderLinkedSub.ExpireTime, exp, accumulateDuration)
			l.Infow("事务已处理，订单关联订阅到期时间由队列更新", logger.Field("orderNo", req.OrderNo), logger.Field("userSubscribeId", orderLinkedSub.Id), logger.Field("estimatedExpiresAt", estimatedExpire.Unix()))
			return &types.AttachAppleTransactionResponse{ExpiresAt: estimatedExpire.Unix(), Tier: tier}, nil
		}
		if singleModeAnchorSub != nil {
			// 不在此处更新 expire_time：由激活队列统一写入，避免双重叠加天数
			if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
				l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
			}
			if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix()); syncErr != nil {
				l.Errorw("同步订单状态失败(singleModeAnchorSub)", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
			}
			estimatedExpire := l.calcIAPRenewalExpire(singleModeAnchorSub.ExpireTime, exp, accumulateDuration)
			l.Infow("事务已处理，单订阅锚点到期时间由队列更新", logger.Field("userSubscribeId", singleModeAnchorSub.Id), logger.Field("estimatedExpiresAt", estimatedExpire.Unix()))
			return &types.AttachAppleTransactionResponse{ExpiresAt: estimatedExpire.Unix(), Tier: tier}, nil
		}
	}

	sum := sha256.Sum256([]byte(req.SignedTransactionJWS))
	jwsHash := hex.EncodeToString(sum[:])
	l.Infow("准备写入事务记录", logger.Field("userId", u.Id), logger.Field("transactionId", txPayload.TransactionId), logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("productId", txPayload.ProductId), logger.Field("jwsHash", jwsHash))
	iapTx := &iapmodel.Transaction{
		UserId:                u.Id,
		OriginalTransactionId: txPayload.OriginalTransactionId,
		TransactionId:         txPayload.TransactionId,
		ProductId:             txPayload.ProductId,
		PurchaseAt:            txPayload.PurchaseDate,
		RevocationAt:          txPayload.RevocationDate,
		JWSHash:               jwsHash,
	}
	err = l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
		if existTx == nil || existTx.Id == 0 {
			if e := tx.Model(&iapmodel.Transaction{}).Create(iapTx).Error; e != nil {
				l.Errorw("写入事务表失败", logger.Field("error", e.Error()))
				return e
			}
			l.Infow("写入事务表成功", logger.Field("id", iapTx.Id))
		}
		if !isNewPurchaseOrder {
			merged := false
			if orderLinkedSub != nil {
				// 不在此处更新 expire_time：由激活队列统一写入，避免双重叠加天数
				merged = true
			} else if singleModeAnchorSub != nil {
				// 同上
				merged = true
			}
			if !merged {
				userSub := user.Subscribe{
					UserId:      entitlement.EffectiveUserID,
					SubscribeId: subscribeId,
					OrderId:     orderInfo.Id,
					StartTime:   time.Now(),
					ExpireTime:  exp,
					Traffic:     0,
					Download:    0,
					Upload:      0,
					Token:       fmt.Sprintf("iap:%s", txPayload.OriginalTransactionId),
					UUID:        uuid.New().String(),
					Status:      1,
				}
				if e := l.svcCtx.UserModel.InsertSubscribe(l.ctx, &userSub, tx); e != nil {
					l.Errorw("写入用户订阅失败", logger.Field("error", e.Error()))
					return e
				}
				l.Infow("写入用户订阅成功", logger.Field("userId", u.Id), logger.Field("subscribeId", subscribeId), logger.Field("expireUnix", exp.Unix()))
				commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "subscription_created",
					"[SubscriptionFlow] apple iap attach created a subscription placeholder before queue activation",
					append(commonLogic.OrderTraceFields(orderInfo),
						commonLogic.UserSubscribeTraceFields(&userSub)...,
					)...,
				)
			}
		} else {
			l.Infow("首购订单跳过 attach 阶段订阅写入", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("orderType", orderInfo.Type))
		}
		if e := l.bindOrderTradeNo(orderInfo, tradeNo, tx); e != nil {
			l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", e.Error()))
			return e
		}
		if e := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix(), tx); e != nil {
			l.Errorw("同步订单状态失败", logger.Field("orderNo", req.OrderNo), logger.Field("error", e.Error()))
			return e
		}
		return nil
	})
	if err != nil {
		l.Errorw("绑定事务提交失败", logger.Field("error", err.Error()))
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert error: %v", err.Error())
	}

	// 事务提交后立即清除订阅缓存，避免 App 查到旧数据（激活队列异步执行，存在竞态）
	if orderLinkedSub != nil {
		_ = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, orderLinkedSub)
	} else if singleModeAnchorSub != nil {
		_ = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, singleModeAnchorSub)
	}

	// merged 路径下，exp 仅从购买日算起，需要用已有订阅到期时间 + duration 作为预估值返回前端
	responseExpire := exp
	var mergeSub *user.Subscribe
	if orderLinkedSub != nil {
		mergeSub = orderLinkedSub
	} else if singleModeAnchorSub != nil {
		mergeSub = singleModeAnchorSub
	}
	if mergeSub != nil {
		base := mergeSub.ExpireTime
		now := time.Now()
		if base.Before(now) {
			base = now
		}
		estimated := base.AddDate(0, 0, int(duration))
		if estimated.After(responseExpire) {
			responseExpire = estimated
		}
	}

	l.Infow("绑定完成", logger.Field("userId", u.Id), logger.Field("tier", tier), logger.Field("expiresAt", responseExpire.Unix()))
	return &types.AttachAppleTransactionResponse{
		ExpiresAt: responseExpire.Unix(),
		Tier:      tier,
	}, nil
}

func (l *AttachTransactionLogic) syncOrderStatusAndEnqueue(orderInfo *ordermodel.Order, iapExpireAt int64, tx ...*gorm.DB) error {
	if orderInfo == nil || orderInfo.OrderNo == "" {
		return errors.New("order info is nil")
	}
	if orderInfo.Status == orderStatusPending {
		if err := l.svcCtx.OrderModel.UpdateOrderStatus(l.ctx, orderInfo.OrderNo, orderStatusPaid, tx...); err != nil {
			return err
		}
		orderInfo.Status = orderStatusPaid
		l.Infow("更新订单状态成功", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("status", orderStatusPaid))
		commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "payment_settled",
			"[SubscriptionFlow] apple iap attach marked order as paid",
			append(commonLogic.OrderTraceFields(orderInfo),
				logger.Field("iap_expire_at", iapExpireAt),
			)...,
		)
	}
	// enqueue activation regardless (idempotent handler downstream)
	payload := queueType.ForthwithActivateOrderPayload{OrderNo: orderInfo.OrderNo, IAPExpireAt: iapExpireAt}
	bytes, _ := json.Marshal(payload)
	task := asynq.NewTask(queueType.ForthwithActivateOrder, bytes)
	if _, err := l.svcCtx.Queue.EnqueueContext(l.ctx, task); err != nil {
		// non-fatal
		l.Errorw("enqueue activate task error", logger.Field("error", err.Error()))
	} else {
		l.Infow("已加入订单激活队列", logger.Field("orderNo", orderInfo.OrderNo))
		commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "activation_task_enqueued",
			"[SubscriptionFlow] apple iap attach enqueued activation task",
			append(commonLogic.OrderTraceFields(orderInfo),
				logger.Field("iap_expire_at", iapExpireAt),
			)...,
		)
	}
	return nil
}

func (l *AttachTransactionLogic) findIAPSubscribeByOriginalTransactionID(originalTransactionID string) (*user.Subscribe, error) {
	if originalTransactionID == "" {
		return nil, gorm.ErrRecordNotFound
	}
	candidates := []string{fmt.Sprintf("iap:%s", originalTransactionID), originalTransactionID}
	for _, token := range candidates {
		sub, err := l.svcCtx.UserModel.FindOneSubscribeByToken(l.ctx, token)
		if err == nil && sub != nil && sub.Id > 0 {
			return sub, nil
		}
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
	}
	return nil, gorm.ErrRecordNotFound
}

func (l *AttachTransactionLogic) extendSubscribeForIAP(userSub *user.Subscribe, exp time.Time, durationDays int64, subscribeId int64, tx ...*gorm.DB) (time.Time, error) {
	if userSub == nil {
		return time.Time{}, errors.New("user subscribe is nil")
	}
	newExpire := l.calcIAPRenewalExpire(userSub.ExpireTime, exp, durationDays)
	userSub.ExpireTime = newExpire
	if subscribeId > 0 && userSub.SubscribeId != subscribeId {
		userSub.SubscribeId = subscribeId
	}
	userSub.Status = 1
	userSub.FinishedAt = nil
	if err := l.svcCtx.UserModel.UpdateSubscribe(l.ctx, userSub, tx...); err != nil {
		return time.Time{}, err
	}
	return newExpire, nil
}

func (l *AttachTransactionLogic) calcIAPRenewalExpire(currentExpire time.Time, fallbackExpire time.Time, durationDays int64) time.Time {
	if durationDays > 0 {
		base := currentExpire
		now := time.Now()
		if base.Before(now) {
			base = now
		}
		return base.Add(time.Duration(durationDays) * 24 * time.Hour)
	}
	if fallbackExpire.After(currentExpire) {
		return fallbackExpire
	}
	return currentExpire
}

func (l *AttachTransactionLogic) getAppleTradeNoCandidates(txPayload *iapapple.TransactionPayload) []string {
	if txPayload == nil {
		return nil
	}
	candidates := make([]string, 0, 1)
	if txPayload.TransactionId != "" {
		candidates = append(candidates, txPayload.TransactionId)
	}
	return candidates
}

// validateOrderTradeNoBinding returns (existingOrderNo, error).
// existingOrderNo is non-empty when the Apple transaction is already bound to a paid/finished order.
func (l *AttachTransactionLogic) validateOrderTradeNoBinding(orderInfo *ordermodel.Order, tradeNoCandidates []string) (string, error) {
	if orderInfo == nil || len(tradeNoCandidates) == 0 {
		return "", nil
	}
	if orderInfo.TradeNo != "" && !containsString(tradeNoCandidates, orderInfo.TradeNo) {
		return "", errors.New("order already bound to another apple transaction")
	}

	var boundOrder ordermodel.Order
	err := l.svcCtx.DB.WithContext(l.ctx).
		Model(&ordermodel.Order{}).
		Where("trade_no IN ? AND order_no <> ? AND status IN ?", tradeNoCandidates, orderInfo.OrderNo, []int64{int64(orderStatusPaid), int64(orderStatusFinished)}).
		Order("id DESC").
		First(&boundOrder).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return "", nil
	}
	if err != nil {
		return "", err
	}
	return boundOrder.OrderNo, nil
}

func (l *AttachTransactionLogic) bindOrderTradeNo(orderInfo *ordermodel.Order, tradeNo string, tx ...*gorm.DB) error {
	if orderInfo == nil || tradeNo == "" {
		return nil
	}
	if orderInfo.TradeNo == tradeNo {
		return nil
	}
	if orderInfo.TradeNo != "" && orderInfo.TradeNo != tradeNo {
		return errors.New("order already bound to another apple transaction")
	}

	orderInfo.TradeNo = tradeNo
	if err := l.svcCtx.OrderModel.Update(l.ctx, orderInfo, tx...); err != nil {
		return err
	}
	return nil
}

func containsString(candidates []string, target string) bool {
	if target == "" {
		return false
	}
	for _, item := range candidates {
		if item == target {
			return true
		}
	}
	return false
}
