package event

import (
	"time"

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

// DomainEvent is the base interface for all domain events.
type DomainEvent interface {
	EventName() string
	OccurredAt() time.Time
}

// EventPublisher defines the interface for publishing domain events.
type EventPublisher interface {
	Publish(event DomainEvent) error
}

// --- Concrete Domain Events ---

// OrderPlacedEvent is emitted when a new order is successfully placed.
type OrderPlacedEvent struct {
	OrderID    string       `json:"orderId"`
	UserID     string       `json:"userId"`
	CouponID   string       `json:"couponId"`
	Side       vo.OrderSide `json:"side"`
	Type       vo.OrderType `json:"type"`
	Price      float64      `json:"price"`
	Quantity   int          `json:"quantity"`
	OccurredOn time.Time    `json:"occurredAt"`
}

func NewOrderPlacedEvent(orderID, userID, couponID string, side vo.OrderSide, orderType vo.OrderType, price float64, quantity int) OrderPlacedEvent {
	return OrderPlacedEvent{
		OrderID:    orderID,
		UserID:     userID,
		CouponID:   couponID,
		Side:       side,
		Type:       orderType,
		Price:      price,
		Quantity:   quantity,
		OccurredOn: time.Now(),
	}
}

func (e OrderPlacedEvent) EventName() string    { return "order.placed" }
func (e OrderPlacedEvent) OccurredAt() time.Time { return e.OccurredOn }

// OrderMatchedEvent is emitted when an order is partially or fully matched.
type OrderMatchedEvent struct {
	OrderID    string  `json:"orderId"`
	CouponID   string  `json:"couponId"`
	FilledQty  int     `json:"filledQty"`
	FilledPrice float64 `json:"filledPrice"`
	IsFull     bool    `json:"isFull"`
	OccurredOn time.Time `json:"occurredAt"`
}

func NewOrderMatchedEvent(orderID, couponID string, filledQty int, filledPrice float64, isFull bool) OrderMatchedEvent {
	return OrderMatchedEvent{
		OrderID:     orderID,
		CouponID:    couponID,
		FilledQty:   filledQty,
		FilledPrice: filledPrice,
		IsFull:      isFull,
		OccurredOn:  time.Now(),
	}
}

func (e OrderMatchedEvent) EventName() string    { return "order.matched" }
func (e OrderMatchedEvent) OccurredAt() time.Time { return e.OccurredOn }

// OrderCancelledEvent is emitted when an order is cancelled.
type OrderCancelledEvent struct {
	OrderID    string    `json:"orderId"`
	UserID     string    `json:"userId"`
	CouponID   string    `json:"couponId"`
	OccurredOn time.Time `json:"occurredAt"`
}

func NewOrderCancelledEvent(orderID, userID, couponID string) OrderCancelledEvent {
	return OrderCancelledEvent{
		OrderID:    orderID,
		UserID:     userID,
		CouponID:   couponID,
		OccurredOn: time.Now(),
	}
}

func (e OrderCancelledEvent) EventName() string    { return "order.cancelled" }
func (e OrderCancelledEvent) OccurredAt() time.Time { return e.OccurredOn }

// TradeExecutedEvent is emitted when a trade is completed.
type TradeExecutedEvent struct {
	TradeID     string  `json:"tradeId"`
	CouponID    string  `json:"couponId"`
	BuyOrderID  string  `json:"buyOrderId"`
	SellOrderID string  `json:"sellOrderId"`
	BuyerID     string  `json:"buyerId"`
	SellerID    string  `json:"sellerId"`
	Price       float64 `json:"price"`
	Quantity    int     `json:"quantity"`
	OccurredOn  time.Time `json:"occurredAt"`
}

func NewTradeExecutedEvent(tradeID, couponID, buyOrderID, sellOrderID, buyerID, sellerID string, price float64, quantity int) TradeExecutedEvent {
	return TradeExecutedEvent{
		TradeID:     tradeID,
		CouponID:    couponID,
		BuyOrderID:  buyOrderID,
		SellOrderID: sellOrderID,
		BuyerID:     buyerID,
		SellerID:    sellerID,
		Price:       price,
		Quantity:    quantity,
		OccurredOn:  time.Now(),
	}
}

func (e TradeExecutedEvent) EventName() string    { return "trade.executed" }
func (e TradeExecutedEvent) OccurredAt() time.Time { return e.OccurredOn }

// NoopEventPublisher is a no-op publisher used as a default/fallback.
type NoopEventPublisher struct{}

func (p *NoopEventPublisher) Publish(event DomainEvent) error {
	return nil
}
