<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Console;

use App\Administration\Application\Port\PasswordHasher;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\StatutCompte;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\UserRepository;
use App\Shared\Domain\Clock;
use App\Shared\Infrastructure\Tenant\TenantNotFoundException;
use App\Shared\Infrastructure\Tenant\TenantRegistry;

use function sprintf;

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;

/**
 * Creates an already-activated test user for E2E login tests.
 *
 * Unlike the activation token command, this creates a user that can
 * immediately log in with the provided password.
 */
#[AsCommand(
    name: 'app:dev:create-test-user',
    description: 'Creates an already-activated test user for E2E login tests',
)]
final class CreateTestUserCommand extends Command
{
    public function __construct(
        private readonly UserRepository $userRepository,
        private readonly PasswordHasher $passwordHasher,
        private readonly TenantRegistry $tenantRegistry,
        private readonly Clock $clock,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addOption('email', null, InputOption::VALUE_OPTIONAL, 'Email address', 'e2e-login@example.com')
            ->addOption('password', null, InputOption::VALUE_OPTIONAL, 'Password (plain text)', 'TestPassword123')
            ->addOption('role', null, InputOption::VALUE_OPTIONAL, 'User role (PARENT, ELEVE, PROF, ADMIN)', 'PARENT')
            ->addOption('school', null, InputOption::VALUE_OPTIONAL, 'School name', 'École de Test')
            ->addOption('tenant', null, InputOption::VALUE_OPTIONAL, 'Tenant subdomain (ecole-alpha, ecole-beta)', 'ecole-alpha');
    }

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

        /** @var string $email */
        $email = $input->getOption('email');
        /** @var string $password */
        $password = $input->getOption('password');
        /** @var string $roleOption */
        $roleOption = $input->getOption('role');
        $roleInput = strtoupper($roleOption);
        /** @var string $schoolName */
        $schoolName = $input->getOption('school');
        /** @var string $tenantSubdomain */
        $tenantSubdomain = $input->getOption('tenant');

        // Convert short role name to full Symfony role format
        $roleName = str_starts_with($roleInput, 'ROLE_') ? $roleInput : 'ROLE_' . $roleInput;

        $role = Role::tryFrom($roleName);
        if ($role === null) {
            $validRoles = array_map(static fn (Role $r) => str_replace('ROLE_', '', $r->value), Role::cases());
            $io->error(sprintf(
                'Invalid role "%s". Valid roles: %s',
                $roleInput,
                implode(', ', $validRoles)
            ));

            return Command::FAILURE;
        }

        // Resolve tenant from subdomain
        try {
            $tenantConfig = $this->tenantRegistry->getBySubdomain($tenantSubdomain);
            $tenantId = $tenantConfig->tenantId;
        } catch (TenantNotFoundException) {
            $availableTenants = array_map(
                static fn ($config) => $config->subdomain,
                $this->tenantRegistry->getAllConfigs()
            );
            $io->error(sprintf(
                'Tenant "%s" not found. Available tenants: %s',
                $tenantSubdomain,
                implode(', ', $availableTenants)
            ));

            return Command::FAILURE;
        }

        $now = $this->clock->now();

        // Check if user already exists
        $existingUser = $this->userRepository->findByEmail(new Email($email), $tenantId);
        if ($existingUser !== null) {
            $io->warning(sprintf('User with email "%s" already exists. Returning existing user.', $email));

            $io->table(
                ['Property', 'Value'],
                [
                    ['User ID', (string) $existingUser->id],
                    ['Email', $email],
                    ['Password', $password],
                    ['Role', $existingUser->role->value],
                    ['Status', $existingUser->statut->value],
                ]
            );

            return Command::SUCCESS;
        }

        // Create activated user using reconstitute to bypass domain validation
        $hashedPassword = $this->passwordHasher->hash($password);

        $user = User::reconstitute(
            id: UserId::generate(),
            email: new Email($email),
            roles: [$role],
            tenantId: $tenantId,
            schoolName: $schoolName,
            statut: StatutCompte::ACTIF,
            dateNaissance: null,
            createdAt: $now,
            hashedPassword: $hashedPassword,
            activatedAt: $now,
            consentementParental: null,
        );

        $this->userRepository->save($user);

        $io->success('Test user created successfully!');

        $io->table(
            ['Property', 'Value'],
            [
                ['User ID', (string) $user->id],
                ['Email', $email],
                ['Password', $password],
                ['Role', $role->value],
                ['Tenant', $tenantSubdomain],
                ['School', $schoolName],
                ['Status', StatutCompte::ACTIF->value],
            ]
        );

        return Command::SUCCESS;
    }
}
