<?php

declare(strict_types=1);

namespace App\Command;

use App\Entity\Client;
use App\Entity\Trace;
use App\Model\ClientStatus;
use App\Model\TraceStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;

#[AsCommand(name: 'opengnsys:check-client-availability', description: 'Check client availability')]
class CheckClientAvailability extends Command
{
    const int THRESHOLD_MINUTES = 1;

    public function __construct(
        private readonly HubInterface $hub,
        private readonly EntityManagerInterface $entityManager
    )
    {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $threshold = (new \DateTimeImmutable('UTC'))->modify(' - '.self::THRESHOLD_MINUTES . ' minutes');
        $startQueryTime = microtime(true);

        $validStatuses = [ClientStatus::OG_LIVE, ClientStatus::WINDOWS, ClientStatus::LINUX, ClientStatus::MACOS, ClientStatus::INITIALIZING, ClientStatus::LINUX_SESSION, ClientStatus::WINDOWS_SESSION];

        $query = $this->entityManager->createQuery(
            'UPDATE App\Entity\Client c
                 SET c.status = :status
                 WHERE c.status IN (:currentStatuses)
                   AND c.updatedAt < :threshold'
        );
        $query->setParameter('status', ClientStatus::DISCONNECTED);
        $query->setParameter('currentStatuses', $validStatuses);
        $query->setParameter('threshold', $threshold);
        $updatedCount = $query->execute();

        $queryTime = microtime(true) - $startQueryTime;

        $startMercureTime = microtime(true);

        $clients = $this->entityManager->createQueryBuilder()
            ->select('c')
            ->from(Client::class, 'c')
            ->where('c.status = :status')
            ->andWhere('c.updatedAt < :threshold')
            ->setParameter('status', ClientStatus::DISCONNECTED)
            ->setParameter('threshold', $threshold)
            ->getQuery()
            ->getResult();

        $this->dispatchMercureEvent($clients);

        $mercureTime = microtime(true) - $startMercureTime;

        $io->success("Updated $updatedCount clients to DISCONNECTED status.");
        $io->note("Query time: " . round($queryTime, 3) . "s");
        $io->note("Mercure dispatch time: " . round($mercureTime, 3) . "s");

        return Command::SUCCESS;
    }

    private function dispatchMercureEvent(array $clients, int $chunkSize = 10000): void
    {
        $chunks = array_chunk($clients, $chunkSize);

        foreach ($chunks as $chunk) {
            $data = [];

            foreach ($chunk as $client) {
                $data[] = [
                    '@id' => '/clients/' . $client->getUuid(),
                    'status' => $client->getStatus(),
                ];
            }

            $update = new Update(
                'clients',
                json_encode($data)
            );

            $this->hub->publish($update);
        }
    }

}
