<?php

namespace App\ValueObjects;

use InvalidArgumentException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Stringable;
use Throwable;

class PhoneNumber implements Stringable
{
    private string $e164;
    private string $region;

    public function __construct(string $rawNumber, string $defaultRegion = 'GB')
    {
        $rawNumber = trim($rawNumber);

        if ($rawNumber === '') {
            throw new InvalidArgumentException('Phone number is empty.');
        }

        $phoneUtil = PhoneNumberUtil::getInstance();

        try {
            $number = $phoneUtil->parse($rawNumber, $defaultRegion);
        } catch (Throwable $e) {
            throw new InvalidArgumentException('Invalid phone number.');
        }

        if (! $phoneUtil->isValidNumber($number)) {
            throw new InvalidArgumentException('Invalid phone number.');
        }

        $this->e164 = $phoneUtil->format($number, PhoneNumberFormat::E164);
        $this->region = $phoneUtil->getRegionCodeForNumber($number) ?: $defaultRegion;
    }

    public static function tryFrom(?string $rawNumber, string $defaultRegion = 'GB'): ?self
    {
        if (blank($rawNumber)) {
            return null;
        }

        try {
            return new self($rawNumber, $defaultRegion);
        } catch (Throwable) {
            return null;
        }
    }

    public static function fromNullable(?string $rawNumber): ?self
    {
        return self::tryFrom($rawNumber);
    }

    public function toE164(): string
    {
        return $this->e164;
    }

    public function toNational(): string
    {
        $phoneUtil = PhoneNumberUtil::getInstance();
        $number = $phoneUtil->parse($this->e164, $this->region);

        return $phoneUtil->format($number, PhoneNumberFormat::NATIONAL);
    }

    public function toDisplay(): string
    {
        $phoneUtil = PhoneNumberUtil::getInstance();
        $number = $phoneUtil->parse($this->e164, $this->region);

        return $phoneUtil->format($number, PhoneNumberFormat::INTERNATIONAL);
    }

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