package vo

import (
	"fmt"
	"math"
)

// Price is a value object representing a positive price with decimal precision.
type Price struct {
	value float64
}

// NewPrice creates a validated Price. Value must be positive.
func NewPrice(v float64) (Price, error) {
	if v < 0 {
		return Price{}, fmt.Errorf("price must be non-negative, got %f", v)
	}
	if math.IsNaN(v) || math.IsInf(v, 0) {
		return Price{}, fmt.Errorf("price must be a finite number, got %f", v)
	}
	return Price{value: v}, nil
}

// MustNewPrice creates a Price, panicking on invalid input. Use only in tests.
func MustNewPrice(v float64) Price {
	p, err := NewPrice(v)
	if err != nil {
		panic(err)
	}
	return p
}

// Zero returns a zero price.
func ZeroPrice() Price {
	return Price{value: 0}
}

// Float64 returns the underlying float64 value.
func (p Price) Float64() float64 {
	return p.value
}

// IsZero returns true if the price is zero.
func (p Price) IsZero() bool {
	return p.value == 0
}

// IsPositive returns true if the price is strictly greater than zero.
func (p Price) IsPositive() bool {
	return p.value > 0
}

// GreaterThan returns true if this price is greater than other.
func (p Price) GreaterThan(other Price) bool {
	return p.value > other.value
}

// LessThan returns true if this price is less than other.
func (p Price) LessThan(other Price) bool {
	return p.value < other.value
}

// Equal returns true if both prices are equal.
func (p Price) Equal(other Price) bool {
	return p.value == other.value
}

// Add returns a new Price that is the sum of two prices.
func (p Price) Add(other Price) Price {
	return Price{value: p.value + other.value}
}

// Multiply returns price * quantity as a float64 (for notional calculation).
func (p Price) Multiply(qty int) float64 {
	return p.value * float64(qty)
}

// MidPrice returns the midpoint between two prices.
func MidPrice(a, b Price) Price {
	return Price{value: (a.value + b.value) / 2}
}

// Spread returns (ask - bid) / bid as a float64 ratio.
func Spread(bid, ask Price) float64 {
	if bid.IsZero() {
		return 0
	}
	return (ask.value - bid.value) / bid.value
}

// RoundTo rounds the price to the given number of decimal places.
func (p Price) RoundTo(decimals int) Price {
	ratio := math.Pow(10, float64(decimals))
	return Price{value: math.Round(p.value*ratio) / ratio}
}

// String returns a human-readable representation.
func (p Price) String() string {
	return fmt.Sprintf("%.4f", p.value)
}
