<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects;

/**
 * ISO 4217 Currency Codes with metadata
 * Immutable enum for type-safe currency handling
 */
enum Currency: string
{
    case EUR = 'EUR';
    case USD = 'USD';
    case GBP = 'GBP';
    case CHF = 'CHF';
    case JPY = 'JPY';
    case CNY = 'CNY';
    case AUD = 'AUD';
    case CAD = 'CAD';
    case SEK = 'SEK';
    case NOK = 'NOK';
    case DKK = 'DKK';
    case PLN = 'PLN';
    case CZK = 'CZK';
    case HUF = 'HUF';
    case RON = 'RON';
    case BGN = 'BGN';
    case HRK = 'HRK';
    case RUB = 'RUB';
    case TRY = 'TRY';
    case INR = 'INR';
    case BRL = 'BRL';
    case ZAR = 'ZAR';
    case KRW = 'KRW';
    case SGD = 'SGD';
    case NZD = 'NZD';
    case MXN = 'MXN';
    case BTC = 'BTC';
    case ETH = 'ETH';

    public function getSymbol(): string
    {
        return match($this) {
            self::EUR => '€',
            self::USD => '$',
            self::GBP => '£',
            self::CHF => 'CHF',
            self::JPY => '¥',
            self::CNY => '¥',
            self::AUD => 'A$',
            self::CAD => 'C$',
            self::SEK => 'kr',
            self::NOK => 'kr',
            self::DKK => 'kr',
            self::PLN => 'zł',
            self::CZK => 'Kč',
            self::HUF => 'Ft',
            self::RON => 'lei',
            self::BGN => 'лв',
            self::HRK => 'kn',
            self::RUB => '₽',
            self::TRY => '₺',
            self::INR => '₹',
            self::BRL => 'R$',
            self::ZAR => 'R',
            self::KRW => '₩',
            self::SGD => 'S$',
            self::NZD => 'NZ$',
            self::MXN => '$',
            self::BTC => '₿',
            self::ETH => 'Ξ',
        };
    }

    public function getDecimals(): int
    {
        return match($this) {
            self::JPY, self::KRW => 0,
            self::BTC => 8,
            self::ETH => 18,
            default => 2,
        };
    }

    public function getName(): string
    {
        return match($this) {
            self::EUR => 'Euro',
            self::USD => 'US Dollar',
            self::GBP => 'British Pound',
            self::CHF => 'Swiss Franc',
            self::JPY => 'Japanese Yen',
            self::CNY => 'Chinese Yuan',
            self::AUD => 'Australian Dollar',
            self::CAD => 'Canadian Dollar',
            self::SEK => 'Swedish Krona',
            self::NOK => 'Norwegian Krone',
            self::DKK => 'Danish Krone',
            self::PLN => 'Polish Zloty',
            self::CZK => 'Czech Koruna',
            self::HUF => 'Hungarian Forint',
            self::RON => 'Romanian Leu',
            self::BGN => 'Bulgarian Lev',
            self::HRK => 'Croatian Kuna',
            self::RUB => 'Russian Ruble',
            self::TRY => 'Turkish Lira',
            self::INR => 'Indian Rupee',
            self::BRL => 'Brazilian Real',
            self::ZAR => 'South African Rand',
            self::KRW => 'South Korean Won',
            self::SGD => 'Singapore Dollar',
            self::NZD => 'New Zealand Dollar',
            self::MXN => 'Mexican Peso',
            self::BTC => 'Bitcoin',
            self::ETH => 'Ethereum',
        };
    }

    public function getMinorUnit(): int
    {
        return 10 ** $this->getDecimals();
    }

    public function formatAmount(int $amountInMinorUnits): string
    {
        $decimals = $this->getDecimals();
        $amount = $amountInMinorUnits / $this->getMinorUnit();
        $formatted = number_format($amount, $decimals, '.', ',');

        return $this->getSymbol() . $formatted;
    }

    public function equals(self $other): bool
    {
        return $this === $other;
    }

    public function isNot(self $other): bool
    {
        return $this !== $other;
    }

    public function isCrypto(): bool
    {
        return match($this) {
            self::BTC, self::ETH => true,
            default => false,
        };
    }

    public function isFiat(): bool
    {
        return ! $this->isCrypto();
    }

    public function isEurozone(): bool
    {
        return $this === self::EUR;
    }

    public static function fromCode(string $code): ?self
    {
        return self::tryFrom(strtoupper($code));
    }

    public function toArray(): array
    {
        return [
            'code' => $this->value,
            'symbol' => $this->getSymbol(),
            'decimals' => $this->getDecimals(),
            'name' => $this->getName(),
        ];
    }
}
