package order

import (
	"context"
	"strconv"
	"time"

	"hfb_sys/backend/internal/model"

	"gorm.io/gorm"
)

func (r *Repository) ListForUser(ctx context.Context, userID uint64) ([]OrderDTO, error) {
	var rows []orderRow
	db := r.db.WithContext(ctx)
	err := r.baseQuery(ctx).
		Where("o.renter_id = ? OR o.owner_id = ?", userID, userID).
		Order("o.id DESC").
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}
	items := make([]OrderDTO, 0, len(rows))
	paymentTimeoutMinutes := pendingPaymentTimeoutMinutes(db)
	for _, row := range rows {
		dto := row.toDTOForUser(userID)
		applyPaymentDeadline(&dto, row.RentalOrder, paymentTimeoutMinutes)
		if shouldAttachCheckout(row.Status) {
			dto.Checkout = r.latestCheckoutDTOForUser(ctx, row.ID, userID, row.RentalOrder)
		}
		items = append(items, dto)
	}
	return items, nil
}

func (r *Repository) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) {
	var total int64
	db := r.db.WithContext(ctx)
	if err := r.adminQuery(ctx).Count(&total).Error; err != nil {
		return nil, err
	}

	offset := (page - 1) * pageSize
	var rows []orderRow
	err := r.adminQuery(ctx).
		Order("o.id DESC").
		Limit(pageSize).
		Offset(offset).
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}

	items := make([]OrderDTO, 0, len(rows))
	paymentTimeoutMinutes := pendingPaymentTimeoutMinutes(db)
	for _, row := range rows {
		dto := row.toAdminDTO()
		applyPaymentDeadline(&dto, row.RentalOrder, paymentTimeoutMinutes)
		items = append(items, dto)
	}

	return &PaginatedResult{
		Items:    items,
		Total:    total,
		Page:     page,
		PageSize: pageSize,
	}, nil
}

func (r *Repository) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, error) {
	var row orderRow
	db := r.db.WithContext(ctx)
	if err := r.adminQuery(ctx).Where("o.id = ?", orderID).First(&row).Error; err != nil {
		return nil, err
	}
	dto := row.toAdminDTO()
	applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(db))
	dto.ActiveDispute = r.activeDisputeDTO(ctx, orderID)
	dto.Checkout = r.latestCheckoutAdminDTO(ctx, orderID)
	return &dto, nil
}

func (r *Repository) FindForUser(ctx context.Context, userID uint64, orderID uint64) (*OrderDTO, error) {
	var row orderRow
	db := r.db.WithContext(ctx)
	if err := r.baseQuery(ctx).
		Where("o.id = ? AND (o.renter_id = ? OR o.owner_id = ?)", orderID, userID, userID).
		First(&row).Error; err != nil {
		return nil, err
	}
	dto := row.toDTOForUser(userID)
	applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(db))
	dto.ActiveDispute = r.activeDisputeDTO(ctx, orderID)
	dto.Checkout = r.latestCheckoutDTOForUser(ctx, orderID, userID, row.RentalOrder)
	return &dto, nil
}

func pendingPaymentTimeoutMinutes(tx *gorm.DB) int {
	var row model.SystemConfig
	err := tx.Where("`key` = ?", "order.pending_payment_timeout_minutes").First(&row).Error
	if err != nil {
		return defaultPendingPaymentTimeoutMinutes
	}
	value, err := strconv.Atoi(row.Value)
	if err != nil || value < 0 {
		return defaultPendingPaymentTimeoutMinutes
	}
	return value
}

func applyPaymentDeadline(dto *OrderDTO, order model.RentalOrder, timeoutMinutes int) {
	if dto == nil || order.Status != orderStatusPendingPayment || timeoutMinutes <= 0 {
		return
	}
	deadline := order.CreatedAt.Add(time.Duration(timeoutMinutes) * time.Minute)
	dto.PaymentDeadlineAt = &deadline
}

func (r *Repository) latestCheckoutAdminDTO(ctx context.Context, orderID uint64) *CheckoutDTO {
	var checkout model.OrderCheckout
	if err := r.db.WithContext(ctx).Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
		return nil
	}
	dto := toCheckoutAdminDTO(checkout)
	return &dto
}

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

func shouldAttachCheckout(status string) bool {
	switch status {
	case orderStatusPendingCheckoutConfirm, orderStatusPendingCheckoutAccept, orderStatusCheckoutDisputing, orderStatusCompleted:
		return true
	default:
		return false
	}
}

func (r *Repository) latestCheckoutDTOForUser(ctx context.Context, orderID uint64, userID uint64, order model.RentalOrder) *CheckoutDTO {
	var checkout model.OrderCheckout
	if err := r.db.WithContext(ctx).Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
		return nil
	}
	dto := toCheckoutDTOForUser(checkout, userID, order)
	return &dto
}

func (r *Repository) findCheckout(ctx context.Context, id uint64) (*model.OrderCheckout, error) {
	var checkout model.OrderCheckout
	if err := r.db.WithContext(ctx).First(&checkout, id).Error; err != nil {
		return nil, err
	}
	return &checkout, nil
}

func (r *Repository) baseQuery(ctx context.Context) *gorm.DB {
	return r.db.WithContext(ctx).Table("rental_orders AS o").
		Select("o.*, l.listing_no, a.title, a.server_region, a.login_platform").
		Joins("JOIN rental_listings AS l ON l.id = o.listing_id").
		Joins("JOIN game_accounts AS a ON a.id = o.account_id")
}

func (r *Repository) adminQuery(ctx context.Context) *gorm.DB {
	return r.db.WithContext(ctx).Table("rental_orders AS o").
		Select("o.*, l.listing_no, a.title, a.server_region, a.login_platform, owner.phone AS owner_phone, renter.phone AS renter_phone").
		Joins("JOIN rental_listings AS l ON l.id = o.listing_id").
		Joins("JOIN game_accounts AS a ON a.id = o.account_id").
		Joins("JOIN users AS owner ON owner.id = o.owner_id").
		Joins("JOIN users AS renter ON renter.id = o.renter_id")
}

type orderRow struct {
	model.RentalOrder
	Title         string
	ListingNo     string
	ServerRegion  string
	LoginPlatform string
	OwnerPhone    string
	RenterPhone   string
}
