<?php

declare(strict_types=1);

namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;

use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\MethodDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\PropertyDefinition;

use function array_map;
use function assert;
use function implode;
use function iterator_to_array;
use function var_export;

/** @internal */
final class ClassDefinitionCompiler
{
    private TypeCompiler $typeCompiler;

    private AttributesCompiler $attributesCompiler;

    private MethodDefinitionCompiler $methodCompiler;

    private PropertyDefinitionCompiler $propertyCompiler;

    public function __construct()
    {
        $this->typeCompiler = new TypeCompiler();
        $this->attributesCompiler = new AttributesCompiler($this);

        $this->methodCompiler = new MethodDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
        $this->propertyCompiler = new PropertyDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
    }

    public function compile(mixed $value): string
    {
        assert($value instanceof ClassDefinition);

        $name = var_export($value->name, true);
        $type = $this->typeCompiler->compile($value->type);

        $properties = array_map(
            fn (PropertyDefinition $property) => $this->propertyCompiler->compile($property),
            iterator_to_array($value->properties)
        );

        $properties = implode(', ', $properties);

        $methods = array_map(
            fn (MethodDefinition $method) => $this->methodCompiler->compile($method),
            iterator_to_array($value->methods)
        );

        $methods = implode(', ', $methods);
        $attributes = $this->attributesCompiler->compile($value->attributes);

        $isFinal = var_export($value->isFinal, true);
        $isAbstract = var_export($value->isAbstract, true);

        return <<<PHP
        new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition(
            $name,
            $type,
            $attributes,
            new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Properties($properties),
            new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Methods($methods),
            $isFinal,
            $isAbstract,
        )
        PHP;
    }
}
