<?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\Diagnostic\Service\Checker;

use Paysera\PrestashopPayments\Diagnostic\Config\DiagnosticConfig;
use Paysera\PrestashopPayments\Diagnostic\Dto\CheckResult;
use Paysera\PrestashopPayments\Diagnostic\Dto\DiagnosticSection;
use Paysera\PrestashopPayments\Diagnostic\Service\PhpEnvironment;
use Paysera\PrestashopPayments\Diagnostic\Util\SizeUnitParser;

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

class PhpChecker implements CheckerInterface
{
    public const SECTION_KEY = 'php';
    public const SECTION_LABEL = 'PHP information';

    public function __construct(
        private PhpEnvironment $phpEnvironment,
        private SizeUnitParser $sizeUnitParser
    ) {
    }

    public function check(): DiagnosticSection
    {
        return new DiagnosticSection(self::SECTION_KEY, self::SECTION_LABEL, [
            $this->checkPhpVersion(),
            $this->checkExtension('bcmath'),
            $this->checkExtension('openssl'),
            $this->checkSizeIni('memory_limit', DiagnosticConfig::MEMORY_LIMIT),
            $this->checkIntIni('max_input_vars', DiagnosticConfig::MAX_INPUT_VARS),
            $this->checkSizeIni('post_max_size', DiagnosticConfig::POST_MAX_SIZE),
            $this->checkCharsetIni('default_charset', DiagnosticConfig::DEFAULT_CHARSET),
            $this->checkAllowUrlFopen(),
        ]);
    }

    private function checkPhpVersion(): CheckResult
    {
        $current = $this->phpEnvironment->getVersion();
        $required = DiagnosticConfig::MINIMUM_PHP_VERSION;

        return version_compare($current, $required, '>=')
            ? CheckResult::pass('PHP version', sprintf('PHP version %s is compatible', $current))
            : CheckResult::fail('PHP version', sprintf('PHP version %s is not compatible. Please use PHP %s or higher', $current, $required));
    }

    private function checkExtension(string $extension): CheckResult
    {
        $name = sprintf('Check %s extension enabled', $extension);

        if (!$this->phpEnvironment->isExtensionLoaded($extension)) {
            return CheckResult::fail($name, sprintf('%s extension is disabled. Please enable %s', $extension, $extension));
        }

        $version = $this->phpEnvironment->getExtensionVersion($extension) ?? 'unknown';

        return CheckResult::pass($name, sprintf('%s extension is enabled (version %s)', $extension, $version));
    }

    private function checkSizeIni(string $iniKey, string $recommended): CheckResult
    {
        $current = (string) $this->phpEnvironment->getIni($iniKey);
        $isOk = $this->sizeUnitParser->parseToBytes($current) >= $this->sizeUnitParser->parseToBytes($recommended);

        return $this->buildIniResult(
            $iniKey,
            $current,
            $isOk,
            sprintf("php.ini variable '%s' is set to '%s'. The recommended value is at least '%s'. Please increase it in your php.ini configuration.", $iniKey, $current, $recommended)
        );
    }

    private function checkIntIni(string $iniKey, int $recommended): CheckResult
    {
        $current = (string) $this->phpEnvironment->getIni($iniKey);
        $isOk = (int) $current >= $recommended;

        return $this->buildIniResult(
            $iniKey,
            $current,
            $isOk,
            sprintf("php.ini variable '%s' is set to '%s'. The recommended value is at least '%d'. Please increase it in your php.ini configuration.", $iniKey, $current, $recommended)
        );
    }

    private function checkCharsetIni(string $iniKey, string $recommended): CheckResult
    {
        $current = (string) $this->phpEnvironment->getIni($iniKey);
        $isOk = strcasecmp($current, $recommended) === 0;

        return $this->buildIniResult(
            $iniKey,
            $current,
            $isOk,
            sprintf("php.ini variable '%s' is set to '%s'. The recommended value is '%s'.", $iniKey, $current, $recommended)
        );
    }

    private function checkAllowUrlFopen(): CheckResult
    {
        $current = $this->phpEnvironment->getIni('allow_url_fopen');
        $isOk = $current !== null
            && strtolower($current) !== 'off'
            && (bool) $current === true;

        return $isOk
            ? CheckResult::pass('PHP.ini: allow_url_fopen', "php.ini variable 'allow_url_fopen' is enabled, which meets the requirement.")
            : CheckResult::fail('PHP.ini: allow_url_fopen', "php.ini variable 'allow_url_fopen' is disabled. Please enable it in your php.ini configuration.");
    }

    private function buildIniResult(string $iniKey, string $current, bool $isOk, string $failureMessage): CheckResult
    {
        $name = sprintf('PHP.ini: %s', $iniKey);

        return $isOk
            ? CheckResult::pass($name, sprintf("php.ini variable '%s' is set to '%s', which meets the requirement.", $iniKey, $current))
            : CheckResult::fail($name, $failureMessage);
    }
}
