<?php

declare(strict_types=1);

namespace App\Command;

use App\Entity\DartThrow;
use App\Entity\Game;
use App\Entity\GameEventLog;
use App\Entity\GamePlayer;
use App\Entity\RefreshToken;
use App\Entity\Turn;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
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;

#[AsCommand(
    name: 'app:user:delete',
    description: 'Delete a user and all related records (refresh tokens, games, game players, and game data).',
)]
class DeleteUserCommand extends Command
{
    public function __construct(
        private readonly UserRepository $userRepository,
        private readonly EntityManagerInterface $entityManager,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addArgument('email', InputArgument::REQUIRED, 'Email of the user to delete')
            ->addOption('yes', 'y', InputOption::VALUE_NONE, 'Skip confirmation prompt');
    }

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

        $email = mb_strtolower(trim((string) $input->getArgument('email')));
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $io->error('Please provide a valid email address.');

            return Command::FAILURE;
        }

        $user = $this->userRepository->findOneByEmail($email);
        if (!$user instanceof User) {
            $io->error(sprintf('User "%s" was not found.', $email));

            return Command::FAILURE;
        }

        $counts = $this->collectCounts($user);

        $io->warning([
            sprintf('User: %s (%s)', $user->getEmail(), $user->getId()),
            sprintf('Refresh tokens: %d', $counts['refreshTokens']),
            sprintf('Games: %d', $counts['games']),
            sprintf('Game players (for those games): %d', $counts['gamePlayers']),
            sprintf('Turns (for those games): %d', $counts['turns']),
            sprintf('Dart throws (for those games): %d', $counts['dartThrows']),
            sprintf('Game event logs (for those games): %d', $counts['gameEventLogs']),
        ]);

        $force = (bool) $input->getOption('yes');
        if (!$force) {
            if (!$input->isInteractive()) {
                $io->error('Non-interactive mode requires --yes.');

                return Command::FAILURE;
            }

            $confirmed = $io->confirm('Proceed with permanent deletion?', false);
            if (!$confirmed) {
                $io->text('Aborted.');

                return Command::SUCCESS;
            }
        }

        $connection = $this->entityManager->getConnection();

        try {
            $connection->beginTransaction();

            $ownedGamesSubquery = $this->entityManager->createQueryBuilder()
                ->select('g_sub')
                ->from(Game::class, 'g_sub')
                ->where('g_sub.owner = :user')
                ->getDQL();

            $deletedDartThrows = $this->entityManager->createQueryBuilder()
                ->delete(DartThrow::class, 'dt')
                ->where(sprintf('dt.game IN (%s)', $ownedGamesSubquery))
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $deletedTurns = $this->entityManager->createQueryBuilder()
                ->delete(Turn::class, 't')
                ->where(sprintf('t.game IN (%s)', $ownedGamesSubquery))
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $deletedGameEventLogs = $this->entityManager->createQueryBuilder()
                ->delete(GameEventLog::class, 'gel')
                ->where(sprintf('gel.game IN (%s)', $ownedGamesSubquery))
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $deletedGamePlayers = $this->entityManager->createQueryBuilder()
                ->delete(GamePlayer::class, 'gp')
                ->where(sprintf('gp.game IN (%s)', $ownedGamesSubquery))
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $deletedGames = $this->entityManager->createQueryBuilder()
                ->delete(Game::class, 'g')
                ->where('g.owner = :user')
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $deletedRefreshTokens = $this->entityManager->createQueryBuilder()
                ->delete(RefreshToken::class, 'rt')
                ->where('rt.user = :user')
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $deletedUsers = $this->entityManager->createQueryBuilder()
                ->delete(User::class, 'u')
                ->where('u = :user')
                ->setParameter('user', $user)
                ->getQuery()
                ->execute();

            $connection->commit();
        } catch (\Throwable $exception) {
            if ($connection->isTransactionActive()) {
                $connection->rollBack();
            }

            $io->error(sprintf('Deletion failed: %s', $exception->getMessage()));

            return Command::FAILURE;
        }

        $io->success([
            sprintf('Deleted user "%s".', $email),
            sprintf('refresh_tokens: %d', $deletedRefreshTokens),
            sprintf('games: %d', $deletedGames),
            sprintf('game_players: %d', $deletedGamePlayers),
            sprintf('turns: %d', $deletedTurns),
            sprintf('dart_throws: %d', $deletedDartThrows),
            sprintf('game_event_logs: %d', $deletedGameEventLogs),
            sprintf('users: %d', $deletedUsers),
        ]);

        return Command::SUCCESS;
    }

    /**
     * @return array{refreshTokens:int,games:int,gamePlayers:int,turns:int,dartThrows:int,gameEventLogs:int}
     */
    private function collectCounts(User $user): array
    {
        $refreshTokens = (int) $this->entityManager->createQueryBuilder()
            ->select('COUNT(rt.id)')
            ->from(RefreshToken::class, 'rt')
            ->where('rt.user = :user')
            ->setParameter('user', $user)
            ->getQuery()
            ->getSingleScalarResult();

        $games = (int) $this->entityManager->createQueryBuilder()
            ->select('COUNT(g.id)')
            ->from(Game::class, 'g')
            ->where('g.owner = :user')
            ->setParameter('user', $user)
            ->getQuery()
            ->getSingleScalarResult();

        $gamePlayers = (int) $this->entityManager->createQueryBuilder()
            ->select('COUNT(gp.id)')
            ->from(GamePlayer::class, 'gp')
            ->innerJoin('gp.game', 'g')
            ->where('g.owner = :user')
            ->setParameter('user', $user)
            ->getQuery()
            ->getSingleScalarResult();

        $turns = (int) $this->entityManager->createQueryBuilder()
            ->select('COUNT(t.id)')
            ->from(Turn::class, 't')
            ->innerJoin('t.game', 'g')
            ->where('g.owner = :user')
            ->setParameter('user', $user)
            ->getQuery()
            ->getSingleScalarResult();

        $dartThrows = (int) $this->entityManager->createQueryBuilder()
            ->select('COUNT(dt.id)')
            ->from(DartThrow::class, 'dt')
            ->innerJoin('dt.game', 'g')
            ->where('g.owner = :user')
            ->setParameter('user', $user)
            ->getQuery()
            ->getSingleScalarResult();

        $gameEventLogs = (int) $this->entityManager->createQueryBuilder()
            ->select('COUNT(gel.id)')
            ->from(GameEventLog::class, 'gel')
            ->innerJoin('gel.game', 'g')
            ->where('g.owner = :user')
            ->setParameter('user', $user)
            ->getQuery()
            ->getSingleScalarResult();

        return [
            'refreshTokens' => $refreshTokens,
            'games' => $games,
            'gamePlayers' => $gamePlayers,
            'turns' => $turns,
            'dartThrows' => $dartThrows,
            'gameEventLogs' => $gameEventLogs,
        ];
    }
}
