package entity

import (
	"fmt"
	"time"

	"github.com/genex/trading-service/internal/domain/vo"
)

// OrderStatus represents the lifecycle status of an order.
type OrderStatus string

const (
	OrderPending   OrderStatus = "pending"
	OrderPartial   OrderStatus = "partial"
	OrderFilled    OrderStatus = "filled"
	OrderCancelled OrderStatus = "cancelled"
)

// Order is the core domain entity representing a trading order.
type Order struct {
	ID           string         `json:"id"`
	UserID       string         `json:"userId"`
	CouponID     string         `json:"couponId"`
	Side         vo.OrderSide   `json:"side"`
	Type         vo.OrderType   `json:"type"`
	Price        vo.Price       `json:"price"`
	Quantity     vo.Quantity    `json:"quantity"`
	FilledQty    vo.Quantity    `json:"filledQty"`
	RemainingQty vo.Quantity    `json:"remainingQty"`
	Status       OrderStatus    `json:"status"`
	CreatedAt    time.Time      `json:"createdAt"`
	UpdatedAt    time.Time      `json:"updatedAt"`
}

// NewOrder creates a new Order with validated inputs.
func NewOrder(id, userID, couponID string, side vo.OrderSide, orderType vo.OrderType, price vo.Price, quantity vo.Quantity) (*Order, error) {
	if id == "" {
		return nil, fmt.Errorf("order ID cannot be empty")
	}
	if userID == "" {
		return nil, fmt.Errorf("user ID cannot be empty")
	}
	if couponID == "" {
		return nil, fmt.Errorf("coupon ID cannot be empty")
	}
	if !side.IsValid() {
		return nil, fmt.Errorf("invalid order side: %s", side)
	}
	if !orderType.IsValid() {
		return nil, fmt.Errorf("invalid order type: %s", orderType)
	}
	if orderType == vo.Limit && !price.IsPositive() {
		return nil, fmt.Errorf("limit order must have a positive price")
	}

	now := time.Now()
	return &Order{
		ID:           id,
		UserID:       userID,
		CouponID:     couponID,
		Side:         side,
		Type:         orderType,
		Price:        price,
		Quantity:     quantity,
		FilledQty:    vo.ZeroQuantity(),
		RemainingQty: quantity,
		Status:       OrderPending,
		CreatedAt:    now,
		UpdatedAt:    now,
	}, nil
}

// Fill records a partial or full fill of the given quantity.
func (o *Order) Fill(qty vo.Quantity) {
	o.FilledQty = o.FilledQty.Add(qty)
	o.RemainingQty = o.RemainingQty.Subtract(qty)
	o.UpdatedAt = time.Now()

	if o.RemainingQty.IsZero() {
		o.Status = OrderFilled
	} else {
		o.Status = OrderPartial
	}
}

// Cancel marks the order as cancelled.
func (o *Order) Cancel() {
	o.Status = OrderCancelled
	o.UpdatedAt = time.Now()
}

// IsActive returns true if the order can still participate in matching.
func (o *Order) IsActive() bool {
	return o.Status == OrderPending || o.Status == OrderPartial
}

// IsFilled returns true if the order is fully filled.
func (o *Order) IsFilled() bool {
	return o.Status == OrderFilled
}
