package dispute

import (
	"context"
	"time"

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

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

func (r *Repository) CancelByOrder(ctx context.Context, userID uint64, orderID uint64) (*DisputeDTO, error) {
	var row model.Dispute
	if err := r.db.WithContext(ctx).
		Where("order_id = ? AND initiator_id = ? AND status IN ?", orderID, userID, []string{"open", "processing"}).
		Order("id DESC").
		First(&row).Error; err != nil {
		return nil, err
	}
	return r.Cancel(ctx, userID, row.ID)
}

func (r *Repository) Cancel(ctx context.Context, userID uint64, id uint64) (*DisputeDTO, error) {
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var row model.Dispute
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&row, id).Error; err != nil {
			return err
		}
		if row.InitiatorID != userID {
			return ErrPermissionDenied
		}
		if row.Status != "open" && row.Status != "processing" {
			return ErrDisputeCannotCancel
		}

		var order model.RentalOrder
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, row.OrderID).Error; err != nil {
			return err
		}
		if err := restoreOrderAfterDisputeCancel(tx, &row, &order); err != nil {
			return err
		}

		now := time.Now()
		row.Status = "cancelled"
		row.ArbitrationRemark = "用户自行取消申诉"
		row.HandledAt = &now
		if err := tx.Save(&row).Error; err != nil {
			return err
		}
		if err := tx.Save(&order).Error; err != nil {
			return err
		}
		recordType := "dispute_cancelled"
		recordContent := "用户自行取消申诉，订单已恢复到申诉前流程。"
		if row.Type == "checkout_dispute" {
			recordType = "checkout_dispute_cancelled"
			recordContent = "用户自行取消结账争议，订单已恢复到申诉前流程。"
		}
		record := model.HandoffRecord{
			OrderID:    order.ID,
			FromUserID: row.InitiatorID,
			ToUserID:   row.TargetUserID,
			Type:       recordType,
			Content:    recordContent,
		}
		if err := tx.Create(&record).Error; err != nil {
			return err
		}

		disputeID := row.ID
		if err := notification.Append(tx,
			notification.Entry{
				UserID:  row.TargetUserID,
				Type:    "dispute",
				Title:   "申诉已取消",
				Content: "对方已自行取消申诉，订单已恢复到申诉前流程。",
				BizType: "dispute",
				BizID:   &disputeID,
			},
			notification.Entry{
				UserID:  row.InitiatorID,
				Type:    "dispute",
				Title:   "申诉已取消",
				Content: "你已取消申诉，订单已恢复到申诉前流程。",
				BizType: "dispute",
				BizID:   &disputeID,
			},
		); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	var row disputeRow
	if err := r.baseQuery(ctx).Where("d.id = ?", id).First(&row).Error; err != nil {
		return nil, err
	}
	dto := row.toDTO()
	return &dto, nil
}

func restoreOrderAfterDisputeCancel(tx *gorm.DB, row *model.Dispute, order *model.RentalOrder) error {
	if row.Type == "checkout_dispute" {
		return restoreCheckoutDispute(tx, row, order)
	}
	if order.Status != "disputing" {
		return ErrDisputeCannotCancel
	}
	restoreOrderStatusFromSnapshot(row, order)
	if order.Status == "" || order.Status == "disputing" {
		order.Status = inferOrderStatusForDisputeCancel(*order)
	}
	if order.Status == "" {
		return ErrDisputeCannotCancel
	}
	return nil
}

func restoreCheckoutDispute(tx *gorm.DB, row *model.Dispute, order *model.RentalOrder) error {
	if order.Status != "checkout_disputing" {
		return ErrDisputeCannotCancel
	}
	var checkout model.OrderCheckout
	query := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("order_id = ? AND status = ?", order.ID, "disputed")
	if row.CheckoutID != nil && *row.CheckoutID > 0 {
		query = query.Where("id = ?", *row.CheckoutID)
	}
	if err := query.Order("id DESC").First(&checkout).Error; err != nil {
		return err
	}
	restoreOrderStatusFromSnapshot(row, order)
	if order.Status == "" || order.Status == "checkout_disputing" {
		order.Status = "pending_checkout_confirm"
		order.HandoffStatus = "pending_owner_checkout"
		if checkout.OwnerAdjustedAt != nil || checkout.OwnerAdjustmentReason != "" {
			order.Status = "pending_checkout_accept"
			order.HandoffStatus = "pending_renter_checkout"
		}
		order.SettlementStatus = "pending"
	}
	checkout.Status = row.PreviousCheckoutStatus
	if checkout.Status == "" {
		checkout.Status = "submitted"
		if order.Status == "pending_checkout_accept" {
			checkout.Status = "countered"
		}
	}
	checkout.RenterRejectedAt = nil
	if err := tx.Save(&checkout).Error; err != nil {
		return err
	}
	return nil
}

func restoreOrderStatusFromSnapshot(row *model.Dispute, order *model.RentalOrder) {
	if row.PreviousOrderStatus != "" {
		order.Status = row.PreviousOrderStatus
	}
	if row.PreviousHandoffStatus != "" {
		order.HandoffStatus = row.PreviousHandoffStatus
	}
	if row.PreviousSettlementStatus != "" {
		order.SettlementStatus = row.PreviousSettlementStatus
	}
}

func inferOrderStatusForDisputeCancel(order model.RentalOrder) string {
	switch order.HandoffStatus {
	case "none":
		return "pending_payment"
	case "pending_owner", "pending_renter_confirm":
		return "pending_handoff"
	case "received":
		return "renting"
	case "return_overdue":
		return "overdue"
	default:
		return ""
	}
}
