<?php

declare (strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Normalizer\Transformer\Compiler\TypeFormatter;

use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\AnonymousClassNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ComplianceNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use OCA\Talk\Vendor\CuyZ\Valinor\Normalizer\Transformer\Compiler\TransformerDefinitionBuilder;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ArrayType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\MixedType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ShapedArrayType;
use WeakMap;
use function hash;
/** @internal */
final class ShapedArrayFormatter implements TypeFormatter
{
    public function __construct(private ShapedArrayType $type)
    {
    }
    public function formatValueNode(ComplianceNode $valueNode): Node
    {
        return Node::this()->callMethod(method: $this->methodName(), arguments: [$valueNode, Node::variable('references')]);
    }
    public function manipulateTransformerClass(AnonymousClassNode $class, TransformerDefinitionBuilder $definitionBuilder): AnonymousClassNode
    {
        $methodName = $this->methodName();
        if ($class->hasMethod($methodName)) {
            return $class;
        }
        if ($this->type->isUnsealed && $this->type->unsealedType() instanceof ArrayType) {
            $defaultDefinition = $definitionBuilder->for($this->type->unsealedType()->subType());
        } else {
            $defaultDefinition = $definitionBuilder->for(MixedType::get());
        }
        $class = $defaultDefinition->typeFormatter()->manipulateTransformerClass($class, $definitionBuilder);
        $elementsDefinitions = [];
        foreach ($this->type->elements as $key => $element) {
            $elementDefinition = $definitionBuilder->for($element->type());
            $class = $elementDefinition->typeFormatter()->manipulateTransformerClass($class, $definitionBuilder);
            $elementsDefinitions[$key] = $elementDefinition;
        }
        return $class->withMethods(Node::method($methodName)->witParameters(Node::parameterDeclaration('value', 'array'), Node::parameterDeclaration('references', WeakMap::class))->withReturnType('array')->withBody(Node::variable('result')->assign(Node::array())->asExpression(), Node::forEach(value: Node::variable('value'), key: 'key', item: 'item', body: Node::variable('result')->key(Node::variable('key'))->assign((function () use ($defaultDefinition, $elementsDefinitions) {
            $match = Node::match(Node::variable('key'));
            foreach ($elementsDefinitions as $name => $definition) {
                $match = $match->withCase(condition: Node::value($name), body: $definition->typeFormatter()->formatValueNode(Node::variable('item')));
            }
            return $match->withDefaultCase($defaultDefinition->typeFormatter()->formatValueNode(Node::variable('item')));
        })())->asExpression()), Node::return(Node::variable('result'))));
    }
    /**
     * @return non-empty-string
     */
    private function methodName(): string
    {
        return 'transform_shaped_array_' . hash('crc32', $this->type->toString());
    }
}