<?php
/**
 * Money Value Object - Domain Safety
 * 
 * Immutable money representation
 * Prevents invalid money operations
 * Type-safe arithmetic
 * 
 * @package Odjoo
 * @category Finance
 * @version 3.0.0
 */
class Money
{
    /**
     * Amount in minor units (kuruş)
     * @var int
     */
    private $amount;
    
    /**
     * Currency code (ISO 4217)
     * @var string
     */
    private $currency;
    
    /**
     * Private constructor - use factory methods
     * 
     * @param int $amount Amount in minor units
     * @param string $currency Currency code
     */
    private function __construct(int $amount, string $currency = 'TRY')
    {
        $this->amount = $amount;
        $this->currency = $currency;
    }
    
    /**
     * Create from TL (major units)
     * 
     * @param string|float $tl TL amount
     * @param string $currency Currency code
     * @return self
     * 
     * @example
     * Money::fromTL("123.45")
     * Money::fromTL(123.45)
     */
    public static function fromTL($tl, string $currency = 'TRY'): self
    {
        require_once __DIR__ . '/MoneyHelper.php';
        $amount = MoneyHelper::toKurus($tl);
        return new self($amount, $currency);
    }
    
    /**
     * Create from kuruş (minor units)
     * 
     * @param int $kurus Amount in kuruş
     * @param string $currency Currency code
     * @return self
     * 
     * @example
     * Money::fromKurus(12345)
     */
    public static function fromKurus(int $kurus, string $currency = 'TRY'): self
    {
        return new self($kurus, $currency);
    }
    
    /**
     * Create zero money
     * 
     * @param string $currency Currency code
     * @return self
     */
    public static function zero(string $currency = 'TRY'): self
    {
        return new self(0, $currency);
    }
    
    /**
     * Get amount in kuruş
     * 
     * @return int
     */
    public function getAmount(): int
    {
        return $this->amount;
    }
    
    /**
     * Get currency code
     * 
     * @return string
     */
    public function getCurrency(): string
    {
        return $this->currency;
    }
    
    /**
     * Add money (immutable)
     * 
     * @param Money $other
     * @return Money
     * @throws InvalidArgumentException
     */
    public function add(Money $other): Money
    {
        $this->assertSameCurrency($other);
        return new self($this->amount + $other->amount, $this->currency);
    }
    
    /**
     * Subtract money (immutable)
     * 
     * @param Money $other
     * @return Money
     * @throws InvalidArgumentException
     */
    public function subtract(Money $other): Money
    {
        $this->assertSameCurrency($other);
        return new self($this->amount - $other->amount, $this->currency);
    }
    
    /**
     * Multiply by integer (immutable)
     * 
     * @param int $multiplier
     * @return Money
     */
    public function multiply(int $multiplier): Money
    {
        return new self($this->amount * $multiplier, $this->currency);
    }
    
    /**
     * Divide by integer (immutable)
     * 
     * @param int $divisor
     * @return Money
     * @throws InvalidArgumentException
     */
    public function divide(int $divisor): Money
    {
        if ($divisor === 0) {
            throw new InvalidArgumentException("Cannot divide by zero");
        }
        return new self(intdiv($this->amount, $divisor), $this->currency);
    }
    
    /**
     * Calculate percentage (immutable)
     * 
     * @param int $basisPoints Basis points (e.g., 2000 = 20%)
     * @return Money
     */
    public function percentage(int $basisPoints): Money
    {
        $result = intdiv($this->amount * $basisPoints, 10000);
        return new self($result, $this->currency);
    }
    
    /**
     * Is zero?
     * 
     * @return bool
     */
    public function isZero(): bool
    {
        return $this->amount === 0;
    }
    
    /**
     * Is positive?
     * 
     * @return bool
     */
    public function isPositive(): bool
    {
        return $this->amount > 0;
    }
    
    /**
     * Is negative?
     * 
     * @return bool
     */
    public function isNegative(): bool
    {
        return $this->amount < 0;
    }
    
    /**
     * Compare with another money
     * 
     * @param Money $other
     * @return int -1 if less, 0 if equal, 1 if greater
     */
    public function compare(Money $other): int
    {
        $this->assertSameCurrency($other);
        
        if ($this->amount < $other->amount) {
            return -1;
        }
        if ($this->amount > $other->amount) {
            return 1;
        }
        return 0;
    }
    
    /**
     * Equals?
     * 
     * @param Money $other
     * @return bool
     */
    public function equals(Money $other): bool
    {
        return $this->currency === $other->currency 
            && $this->amount === $other->amount;
    }
    
    /**
     * Greater than?
     * 
     * @param Money $other
     * @return bool
     */
    public function greaterThan(Money $other): bool
    {
        return $this->compare($other) > 0;
    }
    
    /**
     * Less than?
     * 
     * @param Money $other
     * @return bool
     */
    public function lessThan(Money $other): bool
    {
        return $this->compare($other) < 0;
    }
    
    /**
     * Format for display
     * 
     * @param bool $showCurrency Show currency code?
     * @return string
     */
    public function format(bool $showCurrency = true): string
    {
        require_once __DIR__ . '/MoneyHelper.php';
        return MoneyHelper::format($this->amount, $this->currency, $showCurrency);
    }
    
    /**
     * Convert to TL (for display only)
     * 
     * @return float
     */
    public function toTL(): float
    {
        return $this->amount / 100.0;
    }
    
    /**
     * Assert same currency
     * 
     * @param Money $other
     * @throws InvalidArgumentException
     */
    private function assertSameCurrency(Money $other): void
    {
        if ($this->currency !== $other->currency) {
            throw new InvalidArgumentException(
                "Currency mismatch: {$this->currency} vs {$other->currency}"
            );
        }
    }
    
    /**
     * String representation
     * 
     * @return string
     */
    public function __toString(): string
    {
        return $this->format();
    }
    
    /**
     * JSON serialization
     * 
     * @return array
     */
    public function jsonSerialize(): array
    {
        return [
            'amount' => $this->amount,
            'currency' => $this->currency
        ];
    }
}
