<?php

declare(strict_types=1);

namespace Bolt\Extension;

use Bolt\Common\Str;
use Composer\Package\PackageInterface;
use ComposerPackages\Types;
use ReflectionClass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Yaml\Yaml;

class ExtensionCompilerPass implements CompilerPassInterface
{
    /** string */
    private $projectDir;

    public function process(ContainerBuilder $container): void
    {
        if ($container->has(ExtensionRegistry::class) === false) {
            return;
        }

        $registry = $container->findDefinition(ExtensionRegistry::class);
        $packages = array_keys($container->findTaggedServiceIds(ExtensionInterface::CONTAINER_TAG));
        $this->projectDir = $container->getParameter('kernel.project_dir');

        /* @see ExtensionRegistry::addCompilerPass() */
        $registry->addMethodCall('addCompilerPass', [$packages]);

        // Rebuild our own `packages/bolt.yaml` file.
        $this->buildServices($packages);
    }

    public function buildServices(array $packages): void
    {
        $filename = $this->projectDir . '/config/services_bolt.yaml';
        $oldServices = Yaml::parseFile($filename);
        $services = [
            'services' => [
                '_defaults' => [
                    'autowire' => true,
                    'autoconfigure' => true,
                    'bind' => $oldServices['services']['_defaults']['bind'] ?? [],
                ],
            ],
        ];

        $packages = $this->addComposerPackages($packages);

        foreach ($packages as $package) {
            [$name, $service] = $this->createService($package);
            if ($name) {
                $services['services'][$name] = $service;
            }
        }

        $yaml = "# This file is auto-generated by Bolt. Please change no more than binds.\n\n";
        $yaml .= Yaml::dump($services, 4);

        $filename = $this->projectDir . '/config/services_bolt.yaml';
        file_put_contents($filename, $yaml);
    }

    private function createService(string $package): array
    {
        if (! class_exists($package)) {
            return [null, null];
        }

        $reflection = new ReflectionClass($package);

        $namespace = Str::removeLast($reflection->getName(), Str::splitLast($reflection->getName(), '\\'));
        $path = $this->getRelativePath($package);

        return [
            $namespace, [
                'resource' => $path . '/*',
                'exclude' => $path . '/{Entity,Exception}',
            ],
        ];
    }

    private function addComposerPackages(array $packages): array
    {
        $composerPackages = Types::get('bolt-extension');

        /** @var PackageInterface $package */
        foreach ($composerPackages as $package) {
            $extra = $package->getExtra();

            if (array_key_exists('entrypoint', $extra)) {
                $packages[] = $extra['entrypoint'];
            }
        }

        sort($packages);

        return array_unique($packages);
    }

    private function getRelativePath(string $package): string
    {
        $reflection = new ReflectionClass($package);

        // We add the `/foo` to make the path start with `../`
        return Path::makeRelative(dirname($reflection->getFileName()), $this->projectDir . '/foo');
    }
}
