<?php

declare(strict_types=1);

namespace App\Domain\Common\ValueObject;

final readonly class PhoneNumber
{
    /**
     * @param string $value Phone number in various formats
     */
    public function __construct(
        public string $value
    ) {
        if (empty($value)) {
            throw new \InvalidArgumentException('Phone number cannot be empty.');
        }

        $this->validate($value);
    }

    /**
     * Create from string value
     */
    public static function from(string $value): self
    {
        return new self($value);
    }

    /**
     * Parse from various formats
     */
    public static function parse(string $value): self
    {
        $normalized = self::normalize($value);

        return new self($normalized);
    }

    /**
     * Validate if string is a valid phone number
     */
    public static function isValid(string $value): bool
    {
        try {
            new self($value);

            return true;
        } catch (\InvalidArgumentException) {
            return false;
        }
    }

    /**
     * Get the normalized phone number value
     */
    public function getValue(): string
    {
        return $this->value;
    }

    /**
     * Get country code if present
     */
    public function getCountryCode(): ?string
    {
        if (str_starts_with($this->value, '+')) {
            // Extract country code (1-4 digits after +)
            if (preg_match('/^\+(\d{1,4})/', $this->value, $matches)) {
                return $matches[1];
            }
        }

        return null;
    }

    /**
     * Get national number (without country code)
     */
    public function getNationalNumber(): string
    {
        $countryCode = $this->getCountryCode();
        if ($countryCode !== null) {
            return substr($this->value, strlen($countryCode) + 1);
        }

        return $this->value;
    }

    /**
     * Check if it's a mobile number (basic heuristic)
     */
    public function isMobile(): bool
    {
        $countryCode = $this->getCountryCode();
        $nationalNumber = $this->getNationalNumber();

        // German mobile numbers typically start with 15, 16, 17
        if ($countryCode === '49') {
            return preg_match('/^(15|16|17)\d/', str_replace([' ', '-', '.'], '', $nationalNumber)) === 1;
        }

        // US mobile numbers (basic check for common prefixes)
        if ($countryCode === '1') {
            $cleaned = str_replace([' ', '-', '.', '(', ')'], '', $nationalNumber);

            return strlen($cleaned) === 10;
        }

        // General heuristic: numbers with 10+ digits are likely mobile
        $digitsOnly = preg_replace('/\D/', '', $nationalNumber);

        return strlen($digitsOnly) >= 10;
    }

    /**
     * Format as E.164 international format
     */
    public function toE164(): string
    {
        if (str_starts_with($this->value, '+')) {
            return '+' . preg_replace('/\D/', '', $this->value);
        }

        // If no country code, assume German (+49) for demo
        $digitsOnly = preg_replace('/\D/', '', $this->value);
        if (str_starts_with($digitsOnly, '0')) {
            return '+49' . substr($digitsOnly, 1);
        }

        return '+49' . $digitsOnly;
    }

    /**
     * Format as international display format
     */
    public function toInternational(): string
    {
        $e164 = $this->toE164();
        $countryCode = $this->getCountryCode() ?? '49';
        $nationalNumber = substr($e164, strlen($countryCode) + 1);

        // Format German numbers: +49 123 456789
        if ($countryCode === '49') {
            return "+49 " . chunk_split($nationalNumber, 3, ' ');
        }

        // Format US numbers: +1 (123) 456-7890
        if ($countryCode === '1' && strlen($nationalNumber) === 10) {
            return "+1 (" . substr($nationalNumber, 0, 3) . ") " .
                   substr($nationalNumber, 3, 3) . "-" . substr($nationalNumber, 6);
        }

        // Default formatting
        return "+{$countryCode} " . chunk_split($nationalNumber, 3, ' ');
    }

    /**
     * Format as national display format
     */
    public function toNational(): string
    {
        $nationalNumber = $this->getNationalNumber();
        $digitsOnly = preg_replace('/\D/', '', $nationalNumber);

        // German format: 0123 456789
        if ($this->getCountryCode() === '49') {
            return '0' . chunk_split($digitsOnly, 3, ' ');
        }

        // US format: (123) 456-7890
        if ($this->getCountryCode() === '1' && strlen($digitsOnly) === 10) {
            return "(" . substr($digitsOnly, 0, 3) . ") " .
                   substr($digitsOnly, 3, 3) . "-" . substr($digitsOnly, 6);
        }

        // Default: add spaces every 3 digits
        return chunk_split($digitsOnly, 3, ' ');
    }

    /**
     * Check equality with another phone number
     */
    public function equals(self $other): bool
    {
        // Compare normalized E.164 format for accurate comparison
        return $this->toE164() === $other->toE164();
    }

    /**
     * Check if from same country
     */
    public function isSameCountry(self $other): bool
    {
        return $this->getCountryCode() === $other->getCountryCode();
    }

    public function __toString(): string
    {
        return $this->value;
    }

    /**
     * Validate phone number format
     */
    private function validate(string $value): void
    {
        // Must contain at least some digits
        if (! preg_match('/\d/', $value)) {
            throw new \InvalidArgumentException("Phone number must contain digits: {$value}");
        }

        // Must be reasonable length (5-20 characters after normalization)
        $normalized = self::normalize($value);
        $digitsOnly = preg_replace('/\D/', '', $normalized);
        $digitCount = strlen($digitsOnly);

        if ($digitCount < 5) {
            throw new \InvalidArgumentException("Phone number too short: {$value}");
        }

        if ($digitCount > 20) {
            throw new \InvalidArgumentException("Phone number too long: {$value}");
        }

        // Basic format validation
        if (! preg_match('/^[\+]?[\d\s\-\.\(\)]+$/', $value)) {
            throw new \InvalidArgumentException("Invalid phone number format: {$value}");
        }
    }

    /**
     * Normalize phone number (trim, clean basic formatting)
     */
    private static function normalize(string $value): string
    {
        $value = trim($value);

        // Replace common separators with spaces
        $value = str_replace(['-', '.', '(', ')'], ' ', $value);

        // Clean up multiple spaces
        $value = preg_replace('/\s+/', ' ', $value);

        return trim($value);
    }
}
