package order

import (
	"context"
	"time"

	"hfb_sys/backend/internal/model"
	"hfb_sys/backend/internal/modules/notification"

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

func (r *Repository) AdminClose(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
	var refund *refundAction
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		assets, err := r.lockOrderAssets(tx, orderID)
		if err != nil {
			return err
		}
		order := assets.Order
		listing := assets.Listing
		account := assets.Account
		if isTerminalStatus(order.Status) {
			return ErrOrderCannotComplete
		}
		beforeOrderStatus := order.Status
		beforeHandoffStatus := order.HandoffStatus
		beforeSettlementStatus := order.SettlementStatus
		beforeListingStatus := listing.Status
		beforeAccountStatus := account.Status
		now := time.Now()
		order.Status = orderStatusClosed
		order.HandoffStatus = handoffStatusAdminClosed
		order.SettlementStatus = settlementStatusClosed
		order.SettledAt = &now
		archiveAssets(listing, account)
		if beforeOrderStatus != orderStatusPendingPayment {
			totalCent := order.RentAmountCent + order.DepositAmountCent
			action, err := r.prepareRefund(order, totalCent, refundBizAdminClose, "客服关闭订单原路退款")
			if err != nil {
				return err
			}
			refund = action
		}
		if err := notification.Append(tx,
			notification.Entry{
				UserID:  order.RenterID,
				Type:    "order_admin",
				Title:   "订单已由客服关闭",
				Content: "客服已关闭订单，退款将原路退回您的支付账户。原因：" + req.Reason,
				BizType: "order",
				BizID:   &order.ID,
			},
			notification.Entry{
				UserID:  order.OwnerID,
				Type:    "order_admin",
				Title:   "订单已由客服关闭",
				Content: "客服已关闭订单，关联商品已下架。原因：" + req.Reason,
				BizType: "order",
				BizID:   &order.ID,
			},
		); err != nil {
			return err
		}
		if err := appendAuditLog(tx, adminID, "order.admin_close", "order", order.ID, meta, map[string]any{
			"order_id":                 order.ID,
			"order_no":                 order.OrderNo,
			"listing_id":               order.ListingID,
			"account_id":               order.AccountID,
			"reason":                   req.Reason,
			"before_order_status":      beforeOrderStatus,
			"after_order_status":       order.Status,
			"before_handoff_status":    beforeHandoffStatus,
			"after_handoff_status":     order.HandoffStatus,
			"before_settlement_status": beforeSettlementStatus,
			"after_settlement_status":  order.SettlementStatus,
			"before_listing_status":    beforeListingStatus,
			"after_listing_status":     listing.Status,
			"before_account_status":    beforeAccountStatus,
			"after_account_status":     account.Status,
		}); err != nil {
			return err
		}
		if err := tx.Save(order).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		return tx.Save(account).Error
	})
	if err != nil {
		return err
	}
	r.startRefundBestEffort(ctx, refund)
	return nil
}

func (r *Repository) AdminMarkAbnormal(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		assets, err := r.lockOrderAssets(tx, orderID)
		if err != nil {
			return err
		}
		order := assets.Order
		listing := assets.Listing
		account := assets.Account
		if isTerminalStatus(order.Status) {
			return ErrOrderCannotComplete
		}
		beforeOrderStatus := order.Status
		beforeHandoffStatus := order.HandoffStatus
		beforeListingStatus := listing.Status
		beforeAccountStatus := account.Status
		order.Status = orderStatusAbnormal
		order.HandoffStatus = handoffStatusAdminAbnormal
		markAssetsAbnormal(listing, account)
		if err := notification.Append(tx,
			notification.Entry{
				UserID:  order.RenterID,
				Type:    "order_admin",
				Title:   "订单已被标记异常",
				Content: "客服已将订单标记为异常，请等待进一步处理。原因：" + req.Reason,
				BizType: "order",
				BizID:   &order.ID,
			},
			notification.Entry{
				UserID:  order.OwnerID,
				Type:    "order_admin",
				Title:   "订单已被标记异常",
				Content: "客服已将订单标记为异常，关联商品暂不可出租。原因：" + req.Reason,
				BizType: "order",
				BizID:   &order.ID,
			},
		); err != nil {
			return err
		}
		if err := appendAuditLog(tx, adminID, "order.mark_abnormal", "order", order.ID, meta, map[string]any{
			"order_id":              order.ID,
			"order_no":              order.OrderNo,
			"listing_id":            order.ListingID,
			"account_id":            order.AccountID,
			"reason":                req.Reason,
			"before_order_status":   beforeOrderStatus,
			"after_order_status":    order.Status,
			"before_handoff_status": beforeHandoffStatus,
			"after_handoff_status":  order.HandoffStatus,
			"before_listing_status": beforeListingStatus,
			"after_listing_status":  listing.Status,
			"before_account_status": beforeAccountStatus,
			"after_account_status":  account.Status,
		}); err != nil {
			return err
		}
		if err := tx.Save(order).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		return tx.Save(account).Error
	})
}

type adminResetTarget struct {
	OrderStatus      string
	HandoffStatus    string
	Label            string
	OwnerTitle       string
	OwnerContent     string
	RenterTitle      string
	RenterContent    string
	AuditAction      string
	RefreshStageTime bool
}

// AdminResetHandoff 将超时卡住的订单恢复到对应待办阶段，并刷新阶段计时，
// 让客服可以给责任方再次处理机会，避免订单卡死在异常状态。
func (r *Repository) AdminResetHandoff(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var order model.RentalOrder
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
			return err
		}
		target, ok := resetTargetForOrder(order)
		if !ok {
			return ErrOrderCannotResetHandoff
		}
		beforeOrderStatus := order.Status
		beforeHandoffStatus := order.HandoffStatus
		now := time.Now()
		order.Status = target.OrderStatus
		order.HandoffStatus = target.HandoffStatus
		if target.RefreshStageTime {
			order.HandoffStartedAt = &now
		}
		if err := notification.Append(tx,
			notification.Entry{
				UserID:  order.OwnerID,
				Type:    "order_admin",
				Title:   target.OwnerTitle,
				Content: target.OwnerContent + "原因：" + req.Reason,
				BizType: "order",
				BizID:   &order.ID,
			},
			notification.Entry{
				UserID:  order.RenterID,
				Type:    "order_admin",
				Title:   target.RenterTitle,
				Content: target.RenterContent + "原因：" + req.Reason,
				BizType: "order",
				BizID:   &order.ID,
			},
		); err != nil {
			return err
		}
		if err := appendAuditLog(tx, adminID, target.AuditAction, "order", order.ID, meta, map[string]any{
			"order_id":              order.ID,
			"order_no":              order.OrderNo,
			"reason":                req.Reason,
			"before_order_status":   beforeOrderStatus,
			"after_order_status":    order.Status,
			"before_handoff_status": beforeHandoffStatus,
			"after_handoff_status":  order.HandoffStatus,
		}); err != nil {
			return err
		}
		return tx.Save(&order).Error
	})
}

func resetTargetForOrder(order model.RentalOrder) (adminResetTarget, bool) {
	if order.Status == orderStatusPendingHandoff && order.HandoffStatus == handoffStatusOwnerTimeout {
		return adminResetTarget{
			OrderStatus:      orderStatusPendingHandoff,
			HandoffStatus:    handoffStatusPendingOwner,
			Label:            "重置交接",
			OwnerTitle:       "请重新提交交接说明",
			OwnerContent:     "客服已重置交接状态，请尽快提交交接说明，避免再次超时。",
			RenterTitle:      "订单交接已重置",
			RenterContent:    "客服已重置交接状态，正在等待号主重新提交交接说明。",
			AuditAction:      "order.reset_handoff",
			RefreshStageTime: true,
		}, true
	}
	if order.Status == orderStatusAbnormal && order.HandoffStatus == handoffStatusRenterConfirmTimeout {
		return adminResetTarget{
			OrderStatus:      orderStatusPendingHandoff,
			HandoffStatus:    handoffStatusPendingRenterConfirm,
			Label:            "重置确认收号",
			OwnerTitle:       "订单确认收号已重置",
			OwnerContent:     "客服已重置确认收号状态，正在等待租客重新确认收号。",
			RenterTitle:      "请重新确认收号",
			RenterContent:    "客服已重置确认收号状态，请尽快检查账号并确认收号，避免再次超时。",
			AuditAction:      "order.reset_renter_confirm",
			RefreshStageTime: true,
		}, true
	}
	if order.Status == orderStatusAbnormal &&
		(order.HandoffStatus == handoffStatusOwnerCheckoutConfirmTimeout ||
			order.HandoffStatus == handoffStatusOwnerReturnConfirmTimeout) {
		return adminResetTarget{
			OrderStatus:      orderStatusPendingCheckoutConfirm,
			HandoffStatus:    handoffStatusPendingOwnerCheckout,
			Label:            "重置结账确认",
			OwnerTitle:       "请重新确认结账",
			OwnerContent:     "客服已重置结账确认状态，请尽快核对账号和结账金额，避免再次超时。",
			RenterTitle:      "结账确认已重置",
			RenterContent:    "客服已重置结账确认状态，正在等待号主重新确认结账。",
			AuditAction:      "order.reset_checkout_confirm",
			RefreshStageTime: true,
		}, true
	}
	return adminResetTarget{}, false
}

// AdminRefund 触发后台人工退款，退款由 payment 模块走渠道原路退回。
func (r *Repository) AdminRefund(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
	var order model.RentalOrder
	db := r.db.WithContext(ctx)
	if err := db.First(&order, orderID).Error; err != nil {
		return nil, err
	}
	if order.RefundStatus == refundStatusRefunded {
		return r.buildRefundStatusDTO(&order), nil
	}
	if r.refundStarter == nil {
		return nil, ErrDependencyUnavailable
	}
	totalCent := order.RentAmountCent + order.DepositAmountCent
	if totalCent <= 0 {
		return nil, ErrInvalidCheckoutAmount
	}
	status, err := r.refundStarter.StartRefund(ctx, orderID, totalCent, refundBizAdmin, "后台人工退款")
	if err != nil {
		return nil, err
	}
	// 重新读取订单，拿到 payment 模块更新后的退款字段。
	if err := db.First(&order, orderID).Error; err != nil {
		return nil, err
	}
	dto := r.buildRefundStatusDTO(&order)
	if status != "" {
		dto.RefundStatus = status
	}
	return dto, nil
}

// AdminRefundStatus 查询订单退款状态。
func (r *Repository) AdminRefundStatus(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
	var order model.RentalOrder
	if err := r.db.WithContext(ctx).First(&order, orderID).Error; err != nil {
		return nil, err
	}
	return r.buildRefundStatusDTO(&order), nil
}

func (r *Repository) buildRefundStatusDTO(order *model.RentalOrder) *RefundStatusDTO {
	return &RefundStatusDTO{
		OrderID:          order.ID,
		OrderNo:          order.OrderNo,
		RefundStatus:     order.RefundStatus,
		RefundAmountCent: order.RefundAmountCent,
		RefundedAt:       order.RefundedAt,
		TotalAmountCent:  order.RentAmountCent + order.DepositAmountCent,
	}
}

func isTerminalStatus(status string) bool {
	return status == orderStatusCompleted || status == orderStatusCancelled || status == orderStatusClosed
}
