package mappers

import (
	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/models"

	"github.com/google/uuid"
)

func OrderEntityToResponse(order *entities.Order) *models.OrderResponse {
	if order == nil {
		return nil
	}

	response := &models.OrderResponse{
		ID:              order.ID,
		OrganizationID:  order.OrganizationID,
		OutletID:        order.OutletID,
		UserID:          order.UserID,
		CustomerID:      order.CustomerID,
		OrderNumber:     order.OrderNumber,
		TableNumber:     order.TableNumber,
		OrderType:       constants.OrderType(order.OrderType),
		Status:          constants.OrderStatus(order.Status),
		Subtotal:        order.Subtotal,
		TaxAmount:       order.TaxAmount,
		DiscountAmount:  order.DiscountAmount,
		TotalAmount:     order.TotalAmount,
		TotalCost:       order.TotalCost,
		RemainingAmount: order.RemainingAmount,
		PaymentStatus:   constants.PaymentStatus(order.PaymentStatus),
		RefundAmount:    order.RefundAmount,
		IsVoid:          order.IsVoid,
		IsRefund:        order.IsRefund,
		VoidReason:      order.VoidReason,
		VoidedAt:        order.VoidedAt,
		VoidedBy:        order.VoidedBy,
		RefundReason:    order.RefundReason,
		RefundedAt:      order.RefundedAt,
		RefundedBy:      order.RefundedBy,
		Metadata:        map[string]interface{}(order.Metadata),
		CreatedAt:       order.CreatedAt,
		UpdatedAt:       order.UpdatedAt,
	}

	// Calculate payment summary and determine split type
	var totalPaid float64
	var paymentCount int
	var splitType *string

	if order.Payments != nil {
		paymentCount = len(order.Payments)
		for _, payment := range order.Payments {
			if payment.Status == entities.PaymentTransactionStatusCompleted {
				totalPaid += payment.Amount
			}
			// Determine split type from the first split payment
			if splitType == nil && payment.SplitType != nil && payment.SplitTotal > 1 {
				st := string(*payment.SplitType)
				splitType = &st
			}
		}
	}

	response.TotalPaid = totalPaid
	response.PaymentCount = paymentCount
	response.SplitType = splitType

	// Map order items
	if order.OrderItems != nil {
		response.OrderItems = make([]models.OrderItemResponse, len(order.OrderItems))

		// Build map of paid quantities per order item from payments
		paidQtyByOrderItem := map[uuid.UUID]int{}
		if order.Payments != nil {
			for _, p := range order.Payments {
				for _, poi := range p.PaymentOrderItems {
					paidQtyByOrderItem[poi.OrderItemID] += poi.Quantity
				}
			}
		}

		for i, item := range order.OrderItems {
			resp := OrderItemEntityToResponse(&item)
			if resp != nil {
				resp.PaidQuantity = paidQtyByOrderItem[item.ID]
				response.OrderItems[i] = *resp
			}
		}
	}

	// Map payments
	if order.Payments != nil {
		response.Payments = make([]models.PaymentResponse, len(order.Payments))
		for i, payment := range order.Payments {
			response.Payments[i] = *PaymentEntityToResponse(&payment)
		}
	}

	return response
}

func OrderItemEntityToResponse(item *entities.OrderItem) *models.OrderItemResponse {
	if item == nil {
		return nil
	}

	response := &models.OrderItemResponse{
		ID:                  item.ID,
		OrderID:             item.OrderID,
		ProductID:           item.ProductID,
		ProductVariantID:    item.ProductVariantID,
		Quantity:            item.Quantity,
		UnitPrice:           item.UnitPrice,
		TotalPrice:          item.TotalPrice,
		UnitCost:            item.UnitCost,
		TotalCost:           item.TotalCost,
		RefundAmount:        item.RefundAmount,
		RefundQuantity:      item.RefundQuantity,
		IsPartiallyRefunded: item.IsPartiallyRefunded,
		IsFullyRefunded:     item.IsFullyRefunded,
		RefundReason:        item.RefundReason,
		RefundedAt:          item.RefundedAt,
		RefundedBy:          item.RefundedBy,
		Modifiers:           []map[string]interface{}(item.Modifiers),
		Notes:               item.Notes,
		Metadata:            map[string]interface{}(item.Metadata),
		Status:              constants.OrderItemStatus(item.Status),
		CreatedAt:           item.CreatedAt,
		UpdatedAt:           item.UpdatedAt,
		PrinterType:         item.Product.PrinterType,
	}

	if item.Product.ID != uuid.Nil {
		response.ProductName = item.Product.Name
	}

	if item.ProductVariant != nil {
		response.ProductVariantName = &item.ProductVariant.Name
	}

	return response
}

func PaymentEntityToResponse(payment *entities.Payment) *models.PaymentResponse {
	if payment == nil {
		return nil
	}

	response := &models.PaymentResponse{
		ID:               payment.ID,
		OrderID:          payment.OrderID,
		PaymentMethodID:  payment.PaymentMethodID,
		Amount:           payment.Amount,
		Status:           constants.PaymentTransactionStatus(payment.Status),
		TransactionID:    payment.TransactionID,
		SplitNumber:      payment.SplitNumber,
		SplitTotal:       payment.SplitTotal,
		SplitType:        (*string)(payment.SplitType),
		SplitDescription: payment.SplitDescription,
		RefundAmount:     payment.RefundAmount,
		RefundReason:     payment.RefundReason,
		RefundedAt:       payment.RefundedAt,
		RefundedBy:       payment.RefundedBy,
		Metadata:         map[string]interface{}(payment.Metadata),
		CreatedAt:        payment.CreatedAt,
		UpdatedAt:        payment.UpdatedAt,
	}

	// Add payment method information if available
	if payment.PaymentMethod.ID != uuid.Nil {
		response.PaymentMethodName = payment.PaymentMethod.Name
		response.PaymentMethodType = constants.PaymentMethodType(payment.PaymentMethod.Type)
	}

	// Map payment order items
	if payment.PaymentOrderItems != nil {
		response.PaymentOrderItems = make([]models.PaymentOrderItemResponse, len(payment.PaymentOrderItems))
		for i, item := range payment.PaymentOrderItems {
			response.PaymentOrderItems[i] = *PaymentOrderItemEntityToResponse(&item)
		}
	}

	return response
}

func PaymentOrderItemEntityToResponse(item *entities.PaymentOrderItem) *models.PaymentOrderItemResponse {
	if item == nil {
		return nil
	}

	return &models.PaymentOrderItemResponse{
		ID:          item.ID,
		PaymentID:   item.PaymentID,
		OrderItemID: item.OrderItemID,
		Amount:      item.Amount,
		CreatedAt:   item.CreatedAt,
		UpdatedAt:   item.UpdatedAt,
	}
}

// Request to Entity mappers
func CreateOrderRequestToEntity(req *models.CreateOrderRequest, organizationID uuid.UUID) *entities.Order {
	if req == nil {
		return nil
	}

	order := &entities.Order{
		OrganizationID: organizationID,
		OutletID:       req.OutletID,
		UserID:         req.UserID,
		CustomerID:     req.CustomerID,
		TableNumber:    req.TableNumber,
		OrderType:      entities.OrderType(req.OrderType),
		Status:         entities.OrderStatusPending,
		PaymentStatus:  entities.PaymentStatusPending,
		IsVoid:         false,
		IsRefund:       false,
		Metadata:       entities.Metadata(req.Metadata),
	}

	return order
}

func CreateOrderItemRequestToEntity(req *models.CreateOrderItemRequest) *entities.OrderItem {
	if req == nil {
		return nil
	}

	// UnitPrice will be set from database product price, not from request
	return &entities.OrderItem{
		ProductID:        req.ProductID,
		ProductVariantID: req.ProductVariantID,
		Quantity:         req.Quantity,
		UnitPrice:        0, // Will be set from database product price
		Modifiers:        entities.Modifiers(req.Modifiers),
		Notes:            req.Notes,
		Metadata:         entities.Metadata(req.Metadata),
		Status:           entities.OrderItemStatusPending,
	}
}

func CreatePaymentRequestToEntity(req *models.CreatePaymentRequest) *entities.Payment {
	if req == nil {
		return nil
	}

	var splitType *entities.SplitType
	if req.SplitType != nil {
		st := entities.SplitType(*req.SplitType)
		splitType = &st
	}

	payment := &entities.Payment{
		OrderID:          req.OrderID,
		PaymentMethodID:  req.PaymentMethodID,
		Amount:           req.Amount,
		Status:           entities.PaymentTransactionStatusPending,
		TransactionID:    req.TransactionID,
		SplitNumber:      req.SplitNumber,
		SplitTotal:       req.SplitTotal,
		SplitType:        splitType,
		SplitDescription: req.SplitDescription,
		Metadata:         entities.Metadata(req.Metadata),
	}

	return payment
}

func CreatePaymentOrderItemRequestToEntity(req *models.CreatePaymentOrderItemRequest) *entities.PaymentOrderItem {
	if req == nil {
		return nil
	}

	return &entities.PaymentOrderItem{
		OrderItemID: req.OrderItemID,
		Amount:      req.Amount,
	}
}

// Update request to entity updates
func UpdateOrderRequestToEntityUpdates(req *models.UpdateOrderRequest) map[string]interface{} {
	updates := make(map[string]interface{})

	if req.TableNumber != nil {
		updates["table_number"] = *req.TableNumber
	}
	if req.Status != nil {
		updates["status"] = entities.OrderStatus(*req.Status)
	}
	if req.DiscountAmount != nil {
		updates["discount_amount"] = *req.DiscountAmount
	}
	if req.Metadata != nil {
		updates["metadata"] = entities.Metadata(req.Metadata)
	}

	return updates
}

// Helper functions for list responses
func OrderEntitiesToResponses(orders []*entities.Order) []models.OrderResponse {
	if orders == nil {
		return nil
	}

	responses := make([]models.OrderResponse, len(orders))
	for i, order := range orders {
		response := OrderEntityToResponse(order)
		if response != nil {
			responses[i] = *response
		}
	}

	return responses
}

func OrderItemEntitiesToResponses(items []*entities.OrderItem) []models.OrderItemResponse {
	if items == nil {
		return nil
	}

	responses := make([]models.OrderItemResponse, len(items))
	for i, item := range items {
		response := OrderItemEntityToResponse(item)
		if response != nil {
			responses[i] = *response
		}
	}

	return responses
}

func PaymentEntitiesToResponses(payments []*entities.Payment) []models.PaymentResponse {
	if payments == nil {
		return nil
	}

	responses := make([]models.PaymentResponse, len(payments))
	for i, payment := range payments {
		response := PaymentEntityToResponse(payment)
		if response != nil {
			responses[i] = *response
		}
	}

	return responses
}
