<?php

namespace App\ValueObjects;

use InvalidArgumentException;
use Stringable;
use Throwable;

class PhoneNumber implements Stringable {
    private string $subscriberNumber;

    public function __construct(string $rawNumber) {
        $cleaned = $this->cleanNumber($rawNumber);

        if (strlen($cleaned) !== 10) {
            throw new InvalidArgumentException(
                "Invalid UK phone number provided. Expected 10-digit subscriber number after cleaning."
            );
        }

        $this->subscriberNumber = $cleaned;
    }

    private function cleanNumber(string $rawNumber): string {
        $cleaned = preg_replace('/[^0-9]/', '', $rawNumber);

        if (str_starts_with($cleaned, '0044')) {
            $cleaned = substr($cleaned, 4);
        } elseif (str_starts_with($cleaned, '44')) {
            $cleaned = substr($cleaned, 2);
        }

        if (strlen($cleaned) === 11 && str_starts_with($cleaned, '0')) {
            $cleaned = substr($cleaned, 1);
        }

        return $cleaned;
    }

    /**
     * Explicit nullable constructor for clarity.
     */
    public static function fromNullable(?string $rawNumber): ?self {
        return self::tryFrom($rawNumber);
    }

    /**
     * SAFE factory: returns null instead of throwing.
     * ⭐ This is what casts should use.
     */
    public static function tryFrom(?string $rawNumber): ?self {
        if (blank($rawNumber)) {
            return null;
        }

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

    public function isValid(): bool {
        return strlen($this->subscriberNumber) === 10;
    }

    public function toDisplay(): string {
        $sub = $this->subscriberNumber;

        if (in_array(substr($sub, 0, 2), ['20', '23', '24', '28'])) {
            return sprintf(
                '0%s %s %s',
                substr($sub, 0, 2),
                substr($sub, 2, 4),
                substr($sub, 6)
            );
        }

        return sprintf(
            '0%s %s',
            substr($sub, 0, 4),
            substr($sub, 4)
        );
    }

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

    public function toNational(): string {
        return '0' . $this->subscriberNumber;
    }

    public function toE164(): string {
        return '+44' . $this->subscriberNumber;
    }

    public function isMobile(): bool {
        return str_starts_with($this->subscriberNumber, '7');
    }

    public function isLandline(): bool {
        return in_array(substr($this->subscriberNumber, 0, 1), ['1', '2']);
    }

    public function is03Number(): bool {
        return str_starts_with($this->subscriberNumber, '3');
    }

    public function isServiceNumber(): bool {
        return in_array(substr($this->subscriberNumber, 0, 1), ['3', '8', '9']);
    }

    public function getSubscriberNumber(): string {
        return $this->subscriberNumber;
    }
}
