<?php

declare(strict_types=1);

namespace Plugin\tnt_international_phone\src\Service;

use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberType;
use libphonenumber\PhoneNumberUtil;

/**
 * Server-side phone number validation / E.164 normalization via giggsey/libphonenumber-for-php.
 */
final class PhoneValidator
{
    public const REASON_MISSING_PREFIX = 'missing_prefix';
    public const REASON_UNPARSEABLE    = 'unparseable';
    public const REASON_INVALID        = 'invalid';
    public const REASON_NOT_MOBILE     = 'not_mobile';

    public function __construct(private readonly bool $requirePrefix = false)
    {
    }

    /**
     * @param bool $mustBeMobile reject valid numbers that are not mobile-capable
     *                           (used to keep landlines out of the mobile field)
     * @return array{valid: bool, e164: string|null, country: string|null, reason: string|null}
     *         `country` is the ISO 3166-1 alpha-2 region libphonenumber detected from the
     *         parsed number, falling back to the assumed region hint when detection is not
     *         possible (e.g. unparseable input).
     */
    public function check(string $raw, ?string $region, bool $mustBeMobile = false): array
    {
        $number = trim($raw);
        if (str_starts_with($number, '00')) {
            $number = '+' . substr($number, 2);
        }
        $region = $region !== null && $region !== '' ? strtoupper($region) : null;
        if ($this->requirePrefix && !str_starts_with($number, '+')) {
            return ['valid' => false, 'e164' => null, 'country' => $region, 'reason' => self::REASON_MISSING_PREFIX];
        }
        try {
            $util   = PhoneNumberUtil::getInstance();
            $parsed = $util->parse($number, $region);
        } catch (NumberParseException) {
            return ['valid' => false, 'e164' => null, 'country' => $region, 'reason' => self::REASON_UNPARSEABLE];
        }
        $country = $util->getRegionCodeForNumber($parsed) ?? $region;
        if (!$util->isValidNumber($parsed)) {
            return ['valid' => false, 'e164' => null, 'country' => $country, 'reason' => self::REASON_INVALID];
        }
        if ($mustBeMobile && !$this->isMobileCapable($util->getNumberType($parsed))) {
            return ['valid' => false, 'e164' => null, 'country' => $country, 'reason' => self::REASON_NOT_MOBILE];
        }

        return [
            'valid'   => true,
            'e164'    => $util->format($parsed, PhoneNumberFormat::E164),
            'country' => $country,
            'reason'  => null,
        ];
    }

    /**
     * A number counts as mobile-capable when libphonenumber classifies it as MOBILE or,
     * for regions that share a numbering plan, FIXED_LINE_OR_MOBILE.
     */
    private function isMobileCapable(PhoneNumberType $type): bool
    {
        return $type === PhoneNumberType::MOBILE || $type === PhoneNumberType::FIXED_LINE_OR_MOBILE;
    }
}
