package repository

import (
	"context"
	"fmt"
	"time"

	"apskel-pos-be/internal/entities"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type OrderItemRepository interface {
	Create(ctx context.Context, orderItem *entities.OrderItem) error
	GetByID(ctx context.Context, id uuid.UUID) (*entities.OrderItem, error)
	GetByOrderID(ctx context.Context, orderID uuid.UUID) ([]*entities.OrderItem, error)
	Update(ctx context.Context, orderItem *entities.OrderItem) error
	Delete(ctx context.Context, id uuid.UUID) error
	RefundOrderItem(ctx context.Context, id uuid.UUID, refundQuantity int, refundAmount float64, reason string, refundedBy uuid.UUID) error
	VoidOrderItem(ctx context.Context, id uuid.UUID, voidQuantity int, reason string, voidedBy uuid.UUID) error
	UpdateStatus(ctx context.Context, id uuid.UUID, status entities.OrderItemStatus) error
}

type OrderItemRepositoryImpl struct {
	db *gorm.DB
}

func NewOrderItemRepositoryImpl(db *gorm.DB) *OrderItemRepositoryImpl {
	return &OrderItemRepositoryImpl{
		db: db,
	}
}

func (r *OrderItemRepositoryImpl) Create(ctx context.Context, orderItem *entities.OrderItem) error {
	return r.db.WithContext(ctx).Create(orderItem).Error
}

func (r *OrderItemRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.OrderItem, error) {
	var orderItem entities.OrderItem
	err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error
	if err != nil {
		return nil, err
	}
	return &orderItem, nil
}

func (r *OrderItemRepositoryImpl) GetByOrderID(ctx context.Context, orderID uuid.UUID) ([]*entities.OrderItem, error) {
	var orderItems []*entities.OrderItem
	err := r.db.WithContext(ctx).
		Preload("Product").
		Preload("ProductVariant").
		Where("order_id = ?", orderID).
		Find(&orderItems).Error
	return orderItems, err
}

func (r *OrderItemRepositoryImpl) Update(ctx context.Context, orderItem *entities.OrderItem) error {
	return r.db.WithContext(ctx).Save(orderItem).Error
}

func (r *OrderItemRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.OrderItem{}, "id = ?", id).Error
}

func (r *OrderItemRepositoryImpl) RefundOrderItem(ctx context.Context, id uuid.UUID, refundQuantity int, refundAmount float64, reason string, refundedBy uuid.UUID) error {
	now := time.Now()

	// Get current order item
	var orderItem entities.OrderItem
	if err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error; err != nil {
		return err
	}

	// Calculate new refund quantities and amounts
	newRefundQuantity := orderItem.RefundQuantity + refundQuantity
	newRefundAmount := orderItem.RefundAmount + refundAmount

	// Determine if fully or partially refunded
	isFullyRefunded := newRefundQuantity >= orderItem.Quantity
	isPartiallyRefunded := newRefundQuantity > 0 && newRefundQuantity < orderItem.Quantity

	updates := map[string]interface{}{
		"refund_quantity":       newRefundQuantity,
		"refund_amount":         newRefundAmount,
		"is_partially_refunded": isPartiallyRefunded,
		"is_fully_refunded":     isFullyRefunded,
		"refund_reason":         reason,
		"refunded_at":           now,
		"refunded_by":           refundedBy,
	}

	return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
		Where("id = ?", id).
		Updates(updates).Error
}

func (r *OrderItemRepositoryImpl) UpdateStatus(ctx context.Context, id uuid.UUID, status entities.OrderItemStatus) error {
	return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
		Where("id = ?", id).
		Update("status", status).Error
}

func (r *OrderItemRepositoryImpl) VoidOrderItem(ctx context.Context, id uuid.UUID, voidQuantity int, reason string, voidedBy uuid.UUID) error {
	now := time.Now()

	var orderItem entities.OrderItem
	if err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error; err != nil {
		return err
	}

	if voidQuantity >= orderItem.Quantity {
		voidedAmount := float64(voidQuantity) * orderItem.UnitPrice

		updates := map[string]interface{}{
			"refund_quantity":       voidQuantity,
			"refund_amount":         voidedAmount,
			"is_partially_refunded": false,
			"is_fully_refunded":     true,
			"refund_reason":         reason,
			"refunded_at":           now,
			"refunded_by":           voidedBy,
			"status":                entities.OrderItemStatusCancelled,
		}

		return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
			Where("id = ?", id).
			Updates(updates).Error
	}

	voidedOrderItem := entities.OrderItem{
		OrderID:             orderItem.OrderID,
		ProductID:           orderItem.ProductID,
		ProductVariantID:    orderItem.ProductVariantID,
		Quantity:            voidQuantity,
		UnitPrice:           orderItem.UnitPrice,
		TotalPrice:          float64(voidQuantity) * orderItem.UnitPrice,
		UnitCost:            orderItem.UnitCost,
		TotalCost:           float64(voidQuantity) * orderItem.UnitCost,
		RefundAmount:        float64(voidQuantity) * orderItem.UnitPrice,
		RefundQuantity:      voidQuantity,
		IsPartiallyRefunded: false,
		IsFullyRefunded:     true,
		RefundReason:        &reason,
		RefundedAt:          &now,
		RefundedBy:          &voidedBy,
		Modifiers:           orderItem.Modifiers,
		Notes:               orderItem.Notes,
		Metadata:            orderItem.Metadata,
		Status:              entities.OrderItemStatusCancelled,
	}

	if err := r.db.WithContext(ctx).Create(&voidedOrderItem).Error; err != nil {
		return fmt.Errorf("failed to create voided order item: %w", err)
	}

	remainingQuantity := orderItem.Quantity - voidQuantity
	remainingTotalPrice := float64(remainingQuantity) * orderItem.UnitPrice
	remainingTotalCost := float64(remainingQuantity) * orderItem.UnitCost

	updates := map[string]interface{}{
		"quantity":    remainingQuantity,
		"total_price": remainingTotalPrice,
		"total_cost":  remainingTotalCost,
		"updated_at":  now,
	}

	return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
		Where("id = ?", id).
		Updates(updates).Error
}
