package order

import (
	"context"
	"errors"
	"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
			// 押金暂扣：若订单已暂扣，拦截退款中的押金部分挂起不退，其余照常原路退。
			refundAmountCent := order.ApplyDepositHold(totalCent, order.DepositAmountCent)
			action, err := r.prepareRefund(order, refundAmountCent, 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
	RecalculateEnd   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 target.RecalculateEnd {
			durationHours := estimateOrderDurationHours(order.AccountSnapshot)
			if durationHours > 0 {
				order.EstimatedDurationHours = durationHours
			}
			if order.RentedAt == nil {
				order.RentedAt = &now
			}
			if order.RentedAt != nil {
				estimatedEndAt := order.RentedAt.Add(time.Duration(order.EstimatedDurationHours) * time.Hour)
				if !estimatedEndAt.After(now) {
					order.RentedAt = &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
	}
	if order.Status == orderStatusOverdue && order.HandoffStatus == handoffStatusReturnOverdue {
		return adminResetTarget{
			OrderStatus:    orderStatusRenting,
			HandoffStatus:  handoffStatusReceived,
			Label:          "重置归还逾期",
			OwnerTitle:     "订单归还逾期已重置",
			OwnerContent:   "客服已重置归还逾期状态，订单恢复为使用中。",
			RenterTitle:    "请及时发起结账",
			RenterContent:  "客服已重置归还逾期状态，请尽快发起结账。",
			AuditAction:    "order.reset_return_overdue",
			RecalculateEnd: true,
		}, true
	}
	return adminResetTarget{}, false
}

// AdminRefund 触发后台人工退款，退款由 payment 模块走渠道原路退回。
func (r *Repository) AdminRefund(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
	db := r.db.WithContext(ctx)
	var refund *refundAction
	err := db.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
		}
		if order.RefundStatus == refundStatusRefunded {
			return nil
		}
		totalCent := order.RentAmountCent + order.DepositAmountCent
		if totalCent <= 0 {
			return ErrInvalidCheckoutAmount
		}
		// 押金暂扣：人工退款也必须尊重暂扣状态，避免绕过押金挂起直接全额退款。
		refundAmountCent := order.ApplyDepositHold(totalCent, order.DepositAmountCent)
		if refundAmountCent <= 0 {
			order.RefundStatus = "none"
			order.RefundAmountCent = 0
			return tx.Save(&order).Error
		}
		if r.refundStarter == nil {
			return ErrDependencyUnavailable
		}
		action, err := r.prepareRefund(&order, refundAmountCent, refundBizAdmin, "后台人工退款")
		if err != nil {
			return err
		}
		refund = action
		return tx.Save(&order).Error
	})
	if err != nil {
		return nil, err
	}
	status := ""
	if refund != nil {
		var err error
		status, err = r.refundStarter.StartRefund(ctx, refund.OrderID, refund.RefundAmountCent, refund.BizType, refund.Remark)
		if err != nil {
			return nil, err
		}
	}
	// 重新读取订单，拿到 payment 模块更新后的退款字段。
	var order model.RentalOrder
	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,
	}
}

// AdminApproveRefund 客服审核通过退款申请，触发渠道原路退款。
func (r *Repository) AdminApproveRefund(ctx context.Context, orderID uint64) error {
	var refund *refundAction
	err := 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
		}
		if order.RefundStatus != refundStatusPendingReview {
			return errors.New("订单不处于待审核退款状态")
		}
		if order.RefundAmountCent <= 0 {
			return errors.New("退款金额无效")
		}
		// 押金暂扣：若订单已暂扣，拦截退款中的押金部分挂起不退，其余照常原路退。
		refundAmountCent := order.ApplyDepositHold(order.RefundAmountCent, order.DepositAmountCent)
		if refundAmountCent <= 0 {
			// 全部押金被暂扣且无其他可退金额，直接标记为已处理，等待后续手动退押金。
			order.RefundStatus = "none"
			order.RefundAmountCent = 0
			return tx.Save(&order).Error
		}
		action, err := r.prepareRefund(&order, refundAmountCent, refundBizCancel, "取消订单原路退款（客服审核通过）")
		if err != nil {
			return err
		}
		refund = action
		return tx.Save(&order).Error
	})
	if err != nil {
		return err
	}
	r.startRefundBestEffort(ctx, refund)
	return nil
}

// AdminRejectRefund 客服驳回退款申请。
// action: "restore" 恢复订单至待交接继续流转；"close" 关闭订单并归档商品。
func (r *Repository) AdminRejectRefund(ctx context.Context, orderID uint64, action string) 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 order.RefundStatus != refundStatusPendingReview {
			return errors.New("订单不处于待审核退款状态")
		}

		order.RefundStatus = "none"
		order.RefundAmountCent = 0

		switch action {
		case "restore":
			if order.Status != orderStatusCancelled {
				return errors.New("仅已取消的订单可以恢复")
			}
			order.Status = orderStatusPendingHandoff
			if order.HandoffStatus == handoffStatusCancelled || order.HandoffStatus == handoffStatusRenterConfirmTimeout {
				order.HandoffStatus = handoffStatusPendingOwner
			}
			order.SettlementStatus = settlementStatusUnsettled
			order.SettledAt = nil
			reserveListingForOrder(listing)
			markAssetsRented(listing, account)

		case "close":
			now := time.Now()
			order.Status = orderStatusClosed
			order.HandoffStatus = handoffStatusAdminClosed
			order.SettlementStatus = settlementStatusClosed
			order.SettledAt = &now
			archiveAssets(listing, account)

		default:
			return errors.New("无效的驳回操作，可选 restore 或 close")
		}

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

// AdminSeal 客服封存订单：关闭订单并全额原路退款，将关联商品/账号置为终态 sealed
// （号主不可再编辑、提审、上架），同时在同一事务内解散关联群聊。
// 适用于号主失联、无法继续交接，需要终止并永久封存该商品的场景。
func (r *Repository) AdminSeal(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
		sealAssets(listing, account)
		if beforeOrderStatus != orderStatusPendingPayment {
			totalCent := order.RentAmountCent + order.DepositAmountCent
			// 押金暂扣：若订单已暂扣，拦截退款中的押金部分挂起不退，其余照常原路退。
			refundAmountCent := order.ApplyDepositHold(totalCent, order.DepositAmountCent)
			action, err := r.prepareRefund(order, refundAmountCent, refundBizAdminSeal, "客服封存订单原路退款")
			if err != nil {
				return err
			}
			refund = action
		}
		if r.chatArchiver != nil {
			if err := r.chatArchiver.ArchiveListingConversation(tx, listing.ID, req.Reason); err != nil {
				return err
			}
		}
		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_seal", "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 isTerminalStatus(status string) bool {
	return status == orderStatusCompleted || status == orderStatusCancelled || status == orderStatusClosed
}
