<?php
/**
 * 2026 Paysera
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author Paysera <plugins@paysera.com>
 * @copyright 2026 Paysera
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 * International Registered Trademark & Property of Paysera
 */

declare(strict_types=1);

namespace Paysera\PrestashopPayments\Provider;

use Country;
use Paysera\CheckoutSdk\Entity\PaymentCountry;
use Paysera\CheckoutSdk\SdkFacade;
use PrestaShop\PrestaShop\Adapter\LegacyContext;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

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

class PaymentCountryProvider implements PaymentCountryProviderInterface
{
    private const MIN_COUNTRIES_FOR_ALL_OPTION = 2;
    private const TRANSLATION_DOMAIN = 'Modules.Payserapayments.Global';

    /**
     * ISO 3166-1 alpha-2 codes that PrestaShop does not ship in its country table.
     * Names are English source strings resolved through the translator at runtime.
     */
    private const FALLBACK_COUNTRY_NAMES = [
        'XK' => 'Kosovo',
        'BQ' => 'Bonaire, Sint Eustatius and Saba',
        'CW' => 'Curaçao',
        'SX' => 'Sint Maarten',
        'SS' => 'South Sudan',
        'SH' => 'Saint Helena',
        'BV' => 'Bouvet Island',
        'HM' => 'Heard Island and McDonald Islands',
        'UM' => 'United States Minor Outlying Islands',
    ];

    private SdkFacade $sdkFacade;
    private LoggerInterface $logger;
    private TranslatorInterface $translator;
    private int $languageId;

    public function __construct(
        SdkFacade $sdkFacade,
        LoggerInterface $logger,
        TranslatorInterface $translator,
        LegacyContext $legacyContext
    ) {
        $this->sdkFacade = $sdkFacade;
        $this->logger = $logger;
        $this->translator = $translator;
        $this->languageId = (int) $legacyContext->getContext()->language->id;
    }

    /**
     * @return array<string, string> Country codes mapped to country names
     */
    public function getAvailableCountries(): array
    {
        try {
            $paymentMethods = $this->sdkFacade
                ->getPaymentsFacade()
                ->getPaymentMethods()
            ;

            $allCountries = $this->extractCountriesFromPaymentMethods($paymentMethods);

            $countries = $this->buildCountryList($allCountries);

            $this->logger->debug('Fetched payment countries', [
                'count' => count($countries),
            ]);

            return $countries;
        } catch (\Exception $exception) {
            $this->logger->error('Failed to fetch payment countries', [
                'error' => $exception->getMessage(),
                'code' => $exception->getCode(),
            ]);

            return [];
        }
    }

    /**
     * @param iterable<mixed> $paymentMethods
     *
     * @return array<string, string>
     */
    private function extractCountriesFromPaymentMethods(iterable $paymentMethods): array
    {
        $countries = [];

        foreach ($paymentMethods as $method) {
            foreach ($method->getAvailableCountries() as $country) {
                $code = $country->getCode();
                $countries[$code] = $this->resolveCountryName($code, $country);
            }
        }

        return $countries;
    }

    private function resolveCountryName(string $code, PaymentCountry $country): string
    {
        $sdkName = $country->getName();
        if ($sdkName === $code) {
            $sdkName = null;
        }

        return $this->getNonEmptyString($this->getCountryNameByIsoCode($code))
            ?? $this->getNonEmptyString($this->getFallbackCountryName($code))
            ?? $this->getNonEmptyString($sdkName)
            ?? $code;
    }

    private function getFallbackCountryName(string $code): ?string
    {
        if (!isset(self::FALLBACK_COUNTRY_NAMES[$code])) {
            return null;
        }

        return $this->translator->trans(
            self::FALLBACK_COUNTRY_NAMES[$code],
            [],
            self::TRANSLATION_DOMAIN
        );
    }

    private function getNonEmptyString(?string $value): ?string
    {
        return ($value !== null && $value !== '') ? $value : null;
    }

    /**
     * @param array<string, string> $allCountries
     *
     * @return array<string, string>
     */
    private function buildCountryList(array $allCountries): array
    {
        $realCountries = array_diff_key(
            $allCountries,
            array_flip([
                PaymentCountry::ALL_COUNTRIES_CODE,
                PaymentCountry::OTHER_COUNTRIES_CODE,
            ])
        );

        ksort($realCountries);

        $result = [];

        if (count($realCountries) >= self::MIN_COUNTRIES_FOR_ALL_OPTION) {
            $result[PaymentCountry::ALL_COUNTRIES_CODE] = $this->translator->trans(
                'All countries',
                [],
                self::TRANSLATION_DOMAIN
            );
        }

        $result = array_merge($result, $realCountries);

        $result[PaymentCountry::OTHER_COUNTRIES_CODE] = $this->translator->trans(
            'Other countries',
            [],
            self::TRANSLATION_DOMAIN
        );

        return $result;
    }

    private function getCountryNameByIsoCode(string $isoCode): ?string
    {
        $idCountry = \Country::getByIso($isoCode);
        if (!$idCountry) {
            return null;
        }

        $country = new \Country($idCountry);
        if (!\Validate::isLoadedObject($country)) {
            return null;
        }

        $idLang = $this->languageId;

        return $country->name[$idLang] ?? null;
    }
}
