<?php

namespace App\Controller;

use App\Entity\ActivityLog;
use App\Entity\User;
use App\Repository\OtpCodeRepository;
use App\Repository\UserRepository;
use App\Security\Voter\UserManagementVoter;
use App\Service\ActivityLogger;
use App\Service\ConcessionsProvider;
use App\Service\UserValidationMailer;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;

/**
 * Gestion des utilisateurs (ADMIN + AUDIT) : validation des demandes d'accès, gestion des comptes actifs.
 */
#[IsGranted(UserManagementVoter::VALIDATE)]
final class UserAdminController extends AbstractController
{
    #[Route('/utilisateurs', name: 'app_users', methods: ['GET'])]
    public function index(UserRepository $users, ConcessionsProvider $provider): Response
    {
        $all = $users->findAllOrdered();
        $pending = array_filter($all, static fn (User $u) => User::STATUS_PENDING === $u->getStatus());
        $others = array_filter($all, static fn (User $u) => User::STATUS_PENDING !== $u->getStatus());

        return $this->render('page/users.html.twig', [
            'pending' => array_values($pending),
            'others' => array_values($others),
            'concessions' => $provider->all(),
            'groups' => User::GROUPS,
            'groupLabels' => User::GROUP_LABELS,
            'canManageAdmin' => $this->isGranted(UserManagementVoter::MANAGE_ADMIN),
        ]);
    }

    #[Route('/utilisateurs/{id}', name: 'app_users_action', methods: ['POST'], requirements: ['id' => '\d+'])]
    public function action(
        int $id,
        Request $request,
        UserRepository $users,
        ConcessionsProvider $provider,
        EntityManagerInterface $em,
        UserValidationMailer $mailer,
        LoggerInterface $logger,
        OtpCodeRepository $otpCodes,
        ActivityLogger $activity,
    ): Response {
        $target = $users->find($id);
        if (null === $target) {
            throw $this->createNotFoundException();
        }

        if (!$this->isCsrfTokenValid('user_' . $id, (string) $request->request->get('_token'))) {
            $this->addFlash('error', 'Jeton de sécurité invalide. Réessayez.');

            return $this->redirectToRoute('app_users');
        }

        /** @var User $actor */
        $actor = $this->getUser();

        // On ne se gère pas soi-même via cette page (évite auto-verrouillage / auto-rétrogradation).
        if ($target->getId() === $actor->getId()) {
            $this->addFlash('error', 'Vous ne pouvez pas modifier votre propre compte ici.');

            return $this->redirectToRoute('app_users');
        }

        // Agir sur un compte ADMINISTRATEUR (refuser, rétrograder, modifier) exige le droit dédié :
        // un AUDIT ne peut ni verrouiller ni rétrograder un administrateur.
        if (User::ROLE_ADMIN === $target->getGroup() && !$this->isGranted(UserManagementVoter::MANAGE_ADMIN)) {
            $this->addFlash('error', 'Seul un administrateur peut gérer un compte administrateur.');

            return $this->redirectToRoute('app_users');
        }

        $action = (string) $request->request->get('action');

        if ('delete' === $action) {
            $email = $target->getUserIdentifier();
            // Purge des codes OTP de l'utilisateur (table sans FK, liée par email).
            // Les inventaires (scan_sessions) sont volontairement conservés : ce sont des données historiques.
            $otpCodes->deleteAllForEmail($email);
            $em->remove($target);
            $em->flush();
            $activity->log(ActivityLog::USER_DELETE, null, ['compte' => $email]);
            $logger->info('Utilisateur supprimé', ['email' => $email, 'par' => $actor->getUserIdentifier()]);
            $this->addFlash('success', sprintf('Compte %s supprimé. Ses inventaires existants sont conservés.', $email));

            return $this->redirectToRoute('app_users');
        }

        if ('refuse' === $action) {
            $target->setStatus(User::STATUS_REFUSED);
            $em->flush();
            $activity->log(ActivityLog::USER_REFUSE, null, ['compte' => $target->getUserIdentifier()]);
            $this->addFlash('success', sprintf('Demande de %s refusée.', $target->getUserIdentifier()));

            return $this->redirectToRoute('app_users');
        }

        if (\in_array($action, ['validate', 'update'], true)) {
            $group = (string) $request->request->get('group', '');
            if (!\in_array($group, User::GROUPS, true)) {
                $this->addFlash('error', 'Groupe invalide.');

                return $this->redirectToRoute('app_users');
            }
            // Promouvoir/conserver un ADMIN nécessite le droit dédié.
            if (User::ROLE_ADMIN === $group && !$this->isGranted(UserManagementVoter::MANAGE_ADMIN)) {
                $this->addFlash('error', 'Seul un administrateur peut attribuer le groupe Administrateur.');

                return $this->redirectToRoute('app_users');
            }

            $concessions = $request->request->all('concessions');
            $concessions = \is_array($concessions) ? $concessions : [];
            $validConcessions = array_values(array_filter(
                array_map(static fn ($c) => \is_string($c) ? $c : null, $concessions),
                static fn (?string $c) => null !== $c && $provider->isValid($c),
            ));

            $target->setGroup($group)
                ->setConcessions($validConcessions)
                ->setRequestedRole(null);

            $justActivated = false;
            if ('validate' === $action) {
                $justActivated = User::STATUS_ACTIVE !== $target->getStatus();
                $target->setStatus(User::STATUS_ACTIVE)
                    ->setValidatedAt(new \DateTimeImmutable())
                    ->setValidatedBy($actor->getUserIdentifier());
                $this->addFlash('success', sprintf('%s validé(e) comme %s.', $target->getUserIdentifier(), User::GROUP_LABELS[$group]));
            } else {
                $this->addFlash('success', sprintf('Compte %s mis à jour.', $target->getUserIdentifier()));
            }

            $em->flush();

            $activity->log($justActivated ? ActivityLog::USER_VALIDATE : ActivityLog::USER_UPDATE, null, [
                'compte' => $target->getUserIdentifier(),
                'groupe' => $group,
                'concessions' => $validConcessions,
            ]);

            // Email de confirmation à l'utilisateur lors d'une (ré)activation. Non bloquant.
            if ($justActivated) {
                try {
                    $mailer->sendAccessGranted($target, $this->generateUrl('app_login', [], UrlGeneratorInterface::ABSOLUTE_URL));
                } catch (\Throwable $e) {
                    $logger->error('Échec envoi email de validation d\'accès', ['exception' => $e]);
                    $this->addFlash('error', sprintf(
                        "Compte validé, mais l'email de confirmation n'a pas pu être envoyé (%s). Renvoi possible : bin/console app:user:notify-access %s",
                        $e->getMessage(),
                        $target->getUserIdentifier(),
                    ));
                }
            }

            return $this->redirectToRoute('app_users');
        }

        return $this->redirectToRoute('app_users');
    }
}
