<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Delivery\Util;

use InPost\International\Common\DTO\Phone;
use InPost\International\Country;
use InPost\International\PrestaShop\ObjectModel\Repository\ObjectRepositoryInterface;

if (!defined('_PS_VERSION_')) {
    exit;
}

class PhoneNumberResolver
{
    /**
     * @var ObjectRepositoryInterface<\Country>
     */
    private $countryRepository;

    /**
     * @param ObjectRepositoryInterface<\Country> $countryRepository
     */
    public function __construct(ObjectRepositoryInterface $countryRepository)
    {
        $this->countryRepository = $countryRepository;
    }

    public function resolve(\Address $address): ?Phone
    {
        foreach (['phone_mobile', 'phone'] as $field) {
            if ('' === $phone = trim((string) $address->{$field})) {
                continue;
            }

            return $this->doResolve($phone, (int) $address->id_country);
        }

        return null;
    }

    private function doResolve(string $phone, int $countryId): Phone
    {
        if (!str_starts_with($phone, '+')) {
            $prefix = $this->getPrefixByCountry($countryId);
            $number = preg_replace('/\D/', '', $phone);

            return new Phone($prefix, $number);
        }

        $phone = preg_replace('/[^\d+]/', ' ', $phone);

        if (preg_match('/^\+\d{1,3} /', $phone)) {
            [$prefix, $phone] = explode(' ', $phone, 2);
            $number = preg_replace('/\D/', '', $phone);

            return new Phone($prefix, $number);
        }

        $phone = str_replace(' ', '', $phone);

        foreach (self::getKnownPrefixes() as $prefix) {
            if (!str_starts_with($phone, $prefix)) {
                continue;
            }

            return new Phone($prefix, substr($phone, strlen($prefix)));
        }

        $prefix = substr($phone, 0, 3);
        $number = substr($phone, 3);

        return new Phone($prefix, $number);
    }

    private function getPrefixByCountry(int $countryId): ?string
    {
        $model = $this->countryRepository->find($countryId);

        if (null === $model) {
            return null;
        }

        if (null !== $country = Country::tryFrom((string) $model->iso_code)) {
            return $country->getPhonePrefix();
        }

        if (!isset($model->call_prefix)) {
            return null;
        }

        return '+' . $model->call_prefix;
    }

    /**
     * @return string[]
     */
    private static function getKnownPrefixes(): array
    {
        static $knownPrefixes;

        if (isset($knownPrefixes)) {
            return $knownPrefixes;
        }

        $knownPrefixes = array_map(static function (Country $country): string {
            return $country->getPhonePrefix();
        }, Country::cases());

        // sort by descending length to match the longest first
        usort($knownPrefixes, static function (string $a, string $b): int {
            return strlen($b) - strlen($a);
        });

        return $knownPrefixes;
    }
}
