<?php
/**
 * For the full copyright and license information, please view the
 * docs/licenses/LICENSE.txt file that was distributed with this source code.
 */

namespace PrestaShop\PrestaShop\Core\Localization\Currency;

use PrestaShop\PrestaShop\Core\Exception\InvalidArgumentException;

/**
 * Transform a currency pattern by moving the symbol position, with or without
 * a separation space (no-break space).
 */
class PatternTransformer
{
    public const NO_BREAK_SPACE = "\u{00A0}";
    public const RTL_CHARACTER = "\u{200F}";
    public const REGULAR_SPACE = ' ';
    public const CURRENCY_SYMBOL = '¤';

    public const TYPE_LEFT_SYMBOL_WITH_SPACE = 'leftWithSpace';
    public const TYPE_LEFT_SYMBOL_WITHOUT_SPACE = 'leftWithoutSpace';
    public const TYPE_RIGHT_SYMBOL_WITH_SPACE = 'rightWithSpace';
    public const TYPE_RIGHT_SYMBOL_WITHOUT_SPACE = 'rightWithoutSpace';

    public const ALLOWED_TRANSFORMATIONS = [
        self::TYPE_LEFT_SYMBOL_WITH_SPACE,
        self::TYPE_LEFT_SYMBOL_WITHOUT_SPACE,
        self::TYPE_RIGHT_SYMBOL_WITH_SPACE,
        self::TYPE_RIGHT_SYMBOL_WITHOUT_SPACE,
    ];

    public const CHARACTERS_TO_TRIM =
        self::CURRENCY_SYMBOL .
        self::NO_BREAK_SPACE .
        self::REGULAR_SPACE .
        self::RTL_CHARACTER
    ;

    public const TRANSFORM_DICTIONARY = [
        self::TYPE_LEFT_SYMBOL_WITH_SPACE => '$rtl$currencySymbol$nbsp$pattern',
        self::TYPE_LEFT_SYMBOL_WITHOUT_SPACE => '$rtl$currencySymbol$pattern',
        self::TYPE_RIGHT_SYMBOL_WITH_SPACE => '$rtl$pattern$nbsp$currencySymbol',
        self::TYPE_RIGHT_SYMBOL_WITHOUT_SPACE => '$rtl$pattern$currencySymbol',
    ];

    /**
     * @param string $currencyPattern
     * @param string $transformationType
     *
     * @return string
     *
     * @throws InvalidArgumentException
     */
    public function transform(string $currencyPattern, string $transformationType): string
    {
        if (!in_array($transformationType, self::ALLOWED_TRANSFORMATIONS)) {
            throw new InvalidArgumentException(sprintf('Invalid transformation type "%s", allowed transformations are: %s', $transformationType, implode(',', self::ALLOWED_TRANSFORMATIONS)));
        }

        $transformedPatterns = [];
        $currencyPatterns = explode(';', $currencyPattern);
        foreach ($currencyPatterns as $pattern) {
            $transformedPatterns[] = $this->transformPattern($pattern, $transformationType);
        }

        return implode(';', $transformedPatterns);
    }

    /**
     * @param string $currencyPattern
     *
     * @return string
     */
    public function getTransformationType(string $currencyPattern)
    {
        $patterns = explode(';', $currencyPattern);
        $pattern = str_replace(self::RTL_CHARACTER, '', $patterns[0]);

        $regexpList = [
            self::TYPE_LEFT_SYMBOL_WITH_SPACE => '/^¤[ ' . self::NO_BREAK_SPACE . ']+.+/',
            self::TYPE_LEFT_SYMBOL_WITHOUT_SPACE => '/^¤[^ ' . self::NO_BREAK_SPACE . ']+/',
            self::TYPE_RIGHT_SYMBOL_WITH_SPACE => '/.+[ ' . self::NO_BREAK_SPACE . ']+¤$/',
            self::TYPE_RIGHT_SYMBOL_WITHOUT_SPACE => '/[^ ' . self::NO_BREAK_SPACE . ']+¤$/',
        ];
        foreach ($regexpList as $type => $regexp) {
            if (preg_match($regexp, $pattern)) {
                return $type;
            }
        }

        return '';
    }

    /**
     * @param string $basePattern
     * @param string $transformationType
     *
     * @return string
     */
    private function transformPattern(string $basePattern, string $transformationType)
    {
        $rtlCharacter = $this->getRtlCharacter($basePattern);
        $trimmedPattern = trim($basePattern, self::CHARACTERS_TO_TRIM);

        return strtr(
            self::TRANSFORM_DICTIONARY[$transformationType],
            [
                '$rtl' => $rtlCharacter,
                '$currencySymbol' => self::CURRENCY_SYMBOL,
                '$nbsp' => self::NO_BREAK_SPACE,
                '$pattern' => $trimmedPattern,
            ]
        );
    }

    /**
     * @param string $currencyPattern
     *
     * @return string
     */
    private function getRtlCharacter(string $currencyPattern): string
    {
        return (str_contains($currencyPattern, self::RTL_CHARACTER)) ? self::RTL_CHARACTER : '';
    }
}
