<?php

declare (strict_types=1);
namespace Rector\PhpParser;

use PhpParser\BuilderHelpers;
use PhpParser\Node\Arg;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Expression;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Util\StringUtils;
use Rector\ValueObject\SprintfStringAndArgs;
/**
 * @api used in phpunit
 */
final class NodeTransformer
{
    /**
     * @see https://regex101.com/r/XFc3qA/1
     * @var string
     */
    private const PERCENT_TEXT_REGEX = '#^%\w$#';
    /**
     * @api used in phpunit symfony
     *
     * From:
     * - sprintf("Hi %s", $name));
     *
     * to:
     * - ["Hi %s", $name]
     */
    public function transformSprintfToArray(FuncCall $sprintfFuncCall): ?Array_
    {
        $sprintfStringAndArgs = $this->splitMessageAndArgs($sprintfFuncCall);
        if (!$sprintfStringAndArgs instanceof SprintfStringAndArgs) {
            return null;
        }
        $arrayItems = $sprintfStringAndArgs->getArrayItems();
        $stringValue = $sprintfStringAndArgs->getStringValue();
        $messageParts = $this->splitBySpace($stringValue);
        $arrayMessageParts = [];
        foreach ($messageParts as $messagePart) {
            if (StringUtils::isMatch($messagePart, self::PERCENT_TEXT_REGEX)) {
                /** @var Expr $messagePartNode */
                $messagePartNode = array_shift($arrayItems);
            } else {
                $messagePartNode = new String_($messagePart);
            }
            $arrayMessageParts[] = new ArrayItem($messagePartNode);
        }
        return new Array_($arrayMessageParts);
    }
    /**
     * @return Expression[]
     */
    public function transformArrayToYields(Array_ $array): array
    {
        $yields = [];
        foreach ($array->items as $arrayItem) {
            $yield = new Yield_($arrayItem->value, $arrayItem->key);
            $expression = new Expression($yield);
            $arrayItemComments = $arrayItem->getComments();
            if ($arrayItemComments !== []) {
                $expression->setAttribute(AttributeKey::COMMENTS, $arrayItemComments);
            }
            $yields[] = $expression;
        }
        return $yields;
    }
    /**
     * @api symfony
     */
    public function transformConcatToStringArray(Concat $concat): Array_
    {
        $arrayItems = $this->transformConcatToItems($concat);
        $expr = BuilderHelpers::normalizeValue($arrayItems);
        if (!$expr instanceof Array_) {
            throw new ShouldNotHappenException();
        }
        return $expr;
    }
    private function splitMessageAndArgs(FuncCall $sprintfFuncCall): ?SprintfStringAndArgs
    {
        $stringArgument = null;
        $arrayItems = [];
        foreach ($sprintfFuncCall->args as $i => $arg) {
            if (!$arg instanceof Arg) {
                continue;
            }
            if ($i === 0) {
                $stringArgument = $arg->value;
            } else {
                $arrayItems[] = $arg->value;
            }
        }
        if (!$stringArgument instanceof String_) {
            return null;
        }
        if ($arrayItems === []) {
            return null;
        }
        return new SprintfStringAndArgs($stringArgument, $arrayItems);
    }
    /**
     * @return string[]
     */
    private function splitBySpace(string $value): array
    {
        $value = str_getcsv($value, ' ', '"', '\\');
        return array_filter($value);
    }
    /**
     * @return mixed[]
     */
    private function transformConcatToItems(Concat $concat): array
    {
        $arrayItems = $this->transformConcatItemToArrayItems($concat->left);
        return array_merge($arrayItems, $this->transformConcatItemToArrayItems($concat->right));
    }
    /**
     * @return mixed[]|Expr[]|String_[]
     */
    private function transformConcatItemToArrayItems(Expr $expr): array
    {
        if ($expr instanceof Concat) {
            return $this->transformConcatToItems($expr);
        }
        if (!$expr instanceof String_) {
            return [$expr];
        }
        $arrayItems = [];
        $parts = $this->splitBySpace($expr->value);
        foreach ($parts as $part) {
            if (trim($part) !== '') {
                $arrayItems[] = new String_($part);
            }
        }
        return $arrayItems;
    }
}
