<?php

declare(strict_types=1);

namespace App\Module\Core\Infrastructure\Console;

use App\Module\Core\Domain\Entity\Permission;
use App\Module\Core\Domain\Repository\PermissionRepositoryInterface;
use App\Shared\Domain\Module\ModuleRegistry;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

use function count;

#[AsCommand(name: 'app:sync-permissions', description: 'Synchronise le catalogue des permissions depuis les modules actifs.')]
final class SyncPermissionsCommand extends Command
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly PermissionRepositoryInterface $permissions,
        #[Autowire('%kernel.project_dir%')]
        private readonly string $projectDir,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        /** @var list<class-string> $moduleClasses */
        $moduleClasses = require $this->projectDir.'/config/modules.php';

        // Phase 1 : permissions désirées (code => {code,label,module}).
        $desired = [];
        foreach (ModuleRegistry::permissions($moduleClasses) as $perm) {
            $desired[$perm['code']] = $perm;
        }

        // Phase 2 : upsert.
        $existing = [];
        foreach ($this->permissions->findAll() as $permission) {
            $existing[$permission->getCode()] = $permission;
        }

        $added = $updated = $revived = 0;
        foreach ($desired as $code => $perm) {
            $entity = $existing[$code] ?? null;
            if (null === $entity) {
                $this->permissions->save(new Permission($perm['code'], $perm['label'], $perm['module']));
                ++$added;

                continue;
            }
            if ($entity->isOrphan()) {
                $entity->revive($perm['label'], $perm['module']);
                ++$revived;
            } elseif ($entity->getLabel() !== $perm['label'] || $entity->getModule() !== $perm['module']) {
                $entity->updateMetadata($perm['label'], $perm['module']);
                ++$updated;
            }
        }

        // Phase 3 : orphelines (existantes absentes des désirées).
        $orphaned = 0;
        foreach ($existing as $code => $entity) {
            if (!isset($desired[$code]) && !$entity->isOrphan()) {
                $entity->markOrphan();
                ++$orphaned;
            }
        }

        $this->em->flush();

        $io->success(sprintf('Permissions synchronisées : %d ajoutées, %d mises à jour, %d réactivées, %d orphelines. Total désirées : %d.', $added, $updated, $revived, $orphaned, count($desired)));

        return Command::SUCCESS;
    }
}
