<?php

namespace App\Framework\DI;

class ContainerCompiler
{
public function compile(DefaultContainer $container, array $services, string $outputFile): void
{
        $code = "<?php\n\nnamespace App\\Framework\\DI;\n\nclass CompiledContainer extends Container\n{\n";

        // get()-Methode überschreiben
        $code .= "    public function get(string \$class): object\n     {\n";
        $code .= "        // Singleton- und Instanz-Prüfung vom Basiscontainer\n";
        $code .= "        if (isset(\$this->singletons[\$class])) {\n";
        $code .= "            return \$this->singletons[\$class];\n";
        $code .= "        }\n\n";
        $code .= "        if (isset(\$this->instances[\$class])) {\n";
        $code .= "            return \$this->instances[\$class];\n";
        $code .= "        }\n\n";

        // Switch-Statement für bekannte Services
        $code .= "        // Optimierte Factory-Methoden für bekannte Services\n";
        $code .= "        switch (\$class) {\n";

        foreach ($services as $service) {
            $code .= "            case \\{$service}::class:\n";
            $code .= "                return \$this->create" . $this->getServiceMethodName($service) . "();\n";
        }

        $code .= "            default:\n";
        $code .= "                // Fallback auf Standard-Container\n";
        $code .= "                return parent::get(\$class);\n";
        $code .= "        }\n";
        $code .= "    }\n\n";

        // Factory-Methoden für jeden bekannten Service
        foreach ($services as $service) {
            $code .= $this->generateFactoryMethod($service, $container);
        }

        $code .= "}\n";

        file_put_contents($outputFile, $code);
    }

    private function getServiceMethodName(string $service): string
    {
        // Erzeugt einen methodenkompatiblen Namen aus dem Klassennamen
        $parts = explode('\\', $service);
        return end($parts);
    }

    private function generateFactoryMethod(string $service, DefaultContainer $container): string
    {
        // Analysiert den Service und seine Abhängigkeiten
        $reflection = new \ReflectionClass($service);
        $constructor = $reflection->getConstructor();

        $methodName = 'create' . $this->getServiceMethodName($service);
        $code = "    private function {$methodName}(): \\{$service}\n    {\n";

        // Dependency-Analyse und optimierter Instantiierungs-Code
        if ($constructor) {
            $params = [];
            foreach ($constructor->getParameters() as $param) {
                $paramType = $param->getType();
                if ($paramType instanceof \ReflectionNamedType && !$paramType->isBuiltin()) {
                    $typeName = $paramType->getName();
                    $params[] = "\$this->get(\\{$typeName}::class)";
                } elseif ($param->isDefaultValueAvailable()) {
                    $default = var_export($param->getDefaultValue(), true);
                    $params[] = $default;
                } elseif ($paramType instanceof \ReflectionNamedType && $paramType->allowsNull()) {
                    $params[] = 'null';
                } else {
                    // Nicht auflösbar, Fallback
                    $paramName = $param->getName();
                    $params[] = "/* Parameter '{$paramName}' konnte nicht aufgelöst werden */";
                }
            }

            $paramsStr = implode(', ', $params);
            $code .= "        return new \\{$service}({$paramsStr});\n";
        } else {
            $code .= "        return new \\{$service}();\n";
        }

        $code .= "    }\n\n";

        return $code;
    }


    // Neue Methode zur Abhängigkeitsanalyse hinzufügen
    public function analyzeDependencies(DefaultContainer $container, array $services): array
    {
        $dependencies = [];
        $analyzedServices = [];

        // Rekursive Analyse von Service-Abhängigkeiten
        foreach ($services as $service) {
            $this->analyzeServiceDependencies($service, $dependencies, $analyzedServices);
        }

        return array_unique(array_merge($services, array_keys($dependencies)));
    }

    private function analyzeServiceDependencies(
        string $service,
        array &$dependencies,
        array &$analyzedServices
    ): void {
        // Vermeidung von Zyklen
        if (isset($analyzedServices[$service])) {
            return;
        }

        $analyzedServices[$service] = true;

        try {
            $reflection = new \ReflectionClass($service);
            $constructor = $reflection->getConstructor();

            if ($constructor) {
                foreach ($constructor->getParameters() as $param) {
                    $type = $param->getType();

                    if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
                        $typeName = $type->getName();
                        $dependencies[$typeName] = true;

                        // Rekursive Analyse der Abhängigkeit
                        $this->analyzeServiceDependencies($typeName, $dependencies, $analyzedServices);
                    }
                }
            }
        } catch (\ReflectionException $e) {
            // Fehlerbehandlung
        }
    }

}
