<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Tenant\Command;

use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantRegistry;

use function count;

use Override;

use function sprintf;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;
use Throwable;

#[AsCommand(
    name: 'tenant:migrate',
    description: 'Run migrations for a specific tenant or all tenants'
)]
final class TenantMigrateCommand extends Command
{
    public function __construct(
        private readonly TenantRegistry $registry,
        private readonly string $projectDir,
    ) {
        parent::__construct();
    }

    #[Override]
    protected function configure(): void
    {
        $this
            ->addArgument('subdomain', InputArgument::OPTIONAL, 'The subdomain of the tenant to migrate (or "all" for all tenants)')
            ->addOption('all', 'a', InputOption::VALUE_NONE, 'Run migrations for all tenants')
        ;
    }

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

        /** @var string|null $subdomain */
        $subdomain = $input->getArgument('subdomain');
        $all = $input->getOption('all');

        if ($all || $subdomain === 'all') {
            return $this->migrateAllTenants($io);
        }

        if ($subdomain === null) {
            $io->error('Please provide a subdomain or use --all to migrate all tenants.');

            return Command::FAILURE;
        }

        return $this->migrateTenant($subdomain, $io);
    }

    private function migrateTenant(string $subdomain, SymfonyStyle $io): int
    {
        try {
            $config = $this->registry->getBySubdomain($subdomain);

            return $this->runMigrationForConfig($config, $io);
        } catch (Throwable $e) {
            $io->error([
                "Failed to migrate tenant '{$subdomain}'",
                $e->getMessage(),
            ]);

            return Command::FAILURE;
        }
    }

    private function migrateAllTenants(SymfonyStyle $io): int
    {
        $io->title('Running migrations for all tenants');

        $configs = $this->registry->getAllConfigs();

        if ($configs === []) {
            $io->warning('No tenants found in registry.');

            return Command::SUCCESS;
        }

        $io->info(sprintf('Found %d tenant(s) to migrate.', count($configs)));

        $failed = 0;
        foreach ($configs as $config) {
            $result = $this->runMigrationForConfig($config, $io);
            if ($result !== Command::SUCCESS) {
                ++$failed;
            }
        }

        if ($failed > 0) {
            $io->error(sprintf('%d tenant(s) failed to migrate.', $failed));

            return Command::FAILURE;
        }

        $io->success('All tenants migrated successfully.');

        return Command::SUCCESS;
    }

    private function runMigrationForConfig(TenantConfig $config, SymfonyStyle $io): int
    {
        $io->section("Migrating tenant: {$config->subdomain}");

        // Spawn a new process with DATABASE_URL set BEFORE the kernel boots.
        // This ensures Doctrine uses the tenant's database connection.
        $process = new Process(
            command: ['php', 'bin/console', 'doctrine:migrations:migrate', '--no-interaction'],
            cwd: $this->projectDir,
            env: [
                ...getenv(),
                'DATABASE_URL' => $config->databaseUrl,
            ],
            timeout: 300,
        );

        $process->run(static function (string $type, string $buffer) use ($io): void {
            $io->write($buffer);
        });

        if ($process->isSuccessful()) {
            $io->success("Tenant '{$config->subdomain}' migrated successfully.");

            return Command::SUCCESS;
        }

        $io->error("Migration failed for tenant '{$config->subdomain}'");

        return Command::FAILURE;
    }
}
