<?php

declare(strict_types=1);

namespace App\Module\Core\Infrastructure\Console;

use App\Module\Core\Application\Rbac\RbacSeeder;
use App\Module\Core\Domain\Exception\RbacSeedException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * Seed RBAC metier idempotent et NON destructif (cf. ERP-74 / spec-back M1
 * § 2.7). Contrairement aux fixtures Doctrine (`require-dev`, absentes du build
 * prod `--no-dev`), cette commande applicative est presente dans l'image prod :
 * elle est donc rejouable en recette/staging/prod.
 *
 * Etape de release : a lancer APRES `doctrine:migrations:migrate` et
 * `app:sync-permissions`.
 *  - En prod : `app:seed-rbac` (roles + matrice § 2.7, sans comptes demo).
 *  - En recette : `app:seed-rbac --with-demo-users --password=<...>` pour
 *    disposer de logins de test.
 *
 * Toute la logique (litteraux des roles, matrice, comptes demo) vit dans
 * RbacSeeder — cette commande n'en est que l'enveloppe CLI.
 */
#[AsCommand(
    name: 'app:seed-rbac',
    description: 'Seede les roles metier RBAC + la matrice § 2.7 (idempotent, non destructif).',
)]
final class SeedRbacCommand extends Command
{
    /** Variable d'environnement de repli pour le mot de passe des comptes demo. */
    private const string PASSWORD_ENV = 'RBAC_DEMO_PASSWORD';

    public function __construct(private readonly RbacSeeder $seeder)
    {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addOption(
                'with-demo-users',
                null,
                InputOption::VALUE_NONE,
                'Cree aussi un compte demo par role metier (recette/dev — JAMAIS en prod).',
            )
            ->addOption(
                'password',
                null,
                InputOption::VALUE_REQUIRED,
                'Mot de passe des comptes demo (defaut : variable d\'env '.self::PASSWORD_ENV.'). Requis avec --with-demo-users.',
            )
        ;
    }

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

        // 1. Roles metier + matrice § 2.7. attachMatrix() exige que les
        // permissions soient en base : sinon RbacSeedException porteuse de
        // l'invite a lancer `app:sync-permissions`.
        try {
            $createdRoles = $this->seeder->ensureRoles();
            $addedLinks   = $this->seeder->attachMatrix();
        } catch (RbacSeedException $e) {
            $io->error($e->getMessage());

            return Command::FAILURE;
        }

        $io->text(sprintf(
            'Roles metier : %d cree(s), matrice § 2.7 : %d lien(s) ajoute(s).',
            count($createdRoles),
            $addedLinks,
        ));

        // 2. Comptes demo (optionnel, jamais en prod).
        if ((bool) $input->getOption('with-demo-users')) {
            $password = $this->resolveDemoPassword($input);
            if (null === $password) {
                $io->error(sprintf(
                    '--with-demo-users exige un mot de passe : passe --password=<...> ou definis la variable d\'env %s. '
                    .'(Aucun mot de passe en dur cote serveur.)',
                    self::PASSWORD_ENV,
                ));

                return Command::FAILURE;
            }

            try {
                $createdUsers = $this->seeder->ensureDemoUsers($password);
            } catch (RbacSeedException $e) {
                $io->error($e->getMessage());

                return Command::FAILURE;
            }

            $io->text(sprintf(
                'Comptes demo : %d cree(s)%s.',
                count($createdUsers),
                [] === $createdUsers ? '' : ' ['.implode(', ', $createdUsers).']',
            ));
        }

        $io->success('Seed RBAC metier termine (idempotent).');

        return Command::SUCCESS;
    }

    /**
     * Resout le mot de passe demo : option `--password` prioritaire, sinon
     * variable d'environnement. Renvoie null si aucun n'est fourni (la commande
     * refuse alors --with-demo-users plutot que d'inventer un mot de passe).
     */
    private function resolveDemoPassword(InputInterface $input): ?string
    {
        /** @var null|string $option */
        $option = $input->getOption('password');
        if (null !== $option && '' !== $option) {
            return $option;
        }

        $env = $_SERVER[self::PASSWORD_ENV] ?? $_ENV[self::PASSWORD_ENV] ?? getenv(self::PASSWORD_ENV);
        if (is_string($env) && '' !== $env) {
            return $env;
        }

        return null;
    }
}
