<?php

namespace App\Service\UDS;

use AllowDynamicProperties;
use App\Entity\OrganizationalUnit;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[AllowDynamicProperties]
class UDSClient
{
    private string $token;

    private string $scrambler;

    public function __construct(
        private HttpClientInterface             $httpClient,
        private readonly EntityManagerInterface $entityManager,
        #[Autowire(env: 'REMOTE_PC_URL')]
        private readonly string                 $udsAPIurl,
        #[Autowire(env: 'REMOTE_PC_AUTH_LOGIN')]
        private readonly string                 $udsAuthLogin,
        #[Autowire(env: 'REMOTE_PC_AUTH_USERNAME')]
        private readonly string                 $udsAuthUsername,
        #[Autowire(env: 'REMOTE_PC_AUTH_PASSWORD')]
        private readonly string                 $udsAuthPassword,
    )
    {
    }

    /**
     * @throws TransportExceptionInterface
     */
    public function __invoke(): array
    {
        $this->login();
        $servicePools = $this->getServicePools();

        foreach ($servicePools as $servicePool) {
            $servicePoolInfo = $this->getServicePool($servicePool['provider_id'], $servicePool['service_id']);

            if (!$servicePoolInfo) {
                continue;
            }

            $classroom = $servicePoolInfo['lab'];
            $maxAvailableSeats = $this->getMaxAvailableSeats($classroom);
            $servicePool['max_srvs'] = $maxAvailableSeats;
            $servicePool['osmanager_id'] = null;

            $response = $this->httpClient->request('PUT', $this->udsAPIurl . 'servicespools/' . $servicePool['id'], [
                'verify_host' => false,
                'headers' => [
                    'X-Auth-Token' => $this->token,
                    'Content-Type' => 'application/json',
                    'Scrambler' => $this->scrambler
                ],
                'body' => json_encode($servicePool)
            ]);

            return json_decode($response->getContent(false), true);
        }
    }

    /**
     * @throws TransportExceptionInterface
     */
    public function login(): void
    {
        try {
            $response = $this->httpClient->request('POST', $this->udsAPIurl . 'auth/login', [
                'verify_host' => false,
                'headers' => [
                    'Content-Type' => 'application/json'
                ],
                'json' => [
                    'auth' => $this->udsAuthLogin,
                    'username' => $this->udsAuthUsername,
                    'password' => $this->udsAuthPassword
                ]
            ]);

            $data = json_decode($response->getContent(false), true);

            $this->token = $data['token'];
            $this->scrambler = $data['scrambler'];

        } catch (TransportExceptionInterface $e) {
            throw new TransportException('Error while logging in to UDS');
        } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) {
        }
    }

    /**
     * @throws TransportExceptionInterface
     */
    public function getServicePools(): array
    {
        try {
            $response = $this->httpClient->request('GET', $this->udsAPIurl . '/servicespools/overview', [
                'verify_host' => false,
                'headers' => [
                    'X-Auth-Token' => $this->token,
                    'Content-Type' => 'application/json',
                    'Scrambler' => $this->scrambler
                ]
            ]);
            return json_decode($response->getContent(), true);
        } catch (TransportExceptionInterface $e) {
            throw new TransportException('Error while fetching service pools');
        } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) {
        }
    }

    /**
     * @throws TransportExceptionInterface
     */
    public function getServicePool(string $providerId, string $serviceId): ?array
    {
        try {
            $response = $this->httpClient->request('GET', $this->udsAPIurl . 'providers/' . $providerId .'/services/'. $serviceId, [
                'verify_host' => false,
                'headers' => [
                    'X-Auth-Token' => $this->token,
                    'Content-Type' => 'application/json',
                    'Scrambler' => $this->scrambler
                ]
            ]);

            return json_decode($response->getContent(), true);
        } catch (TransportExceptionInterface $e) {
            throw new TransportException('Error while fetching service pool');
        } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) {
        }

        return null;
    }

    public function getMaxAvailableSeats(int $organizationalUnitId): int
    {
        $organizationalUnit = $this->entityManager->getRepository(OrganizationalUnit::class)->findOneBy(['id' => $organizationalUnitId]);

        if (!$organizationalUnit) {
            return 0;
        }

        $remoteCalendar = $organizationalUnit->getRemoteCalendar();
        if (!$remoteCalendar || !$remoteCalendar->isAvailable()) {
            return 0;
        }

        $totalAvailableClients = 0;

        foreach ($organizationalUnit->getClients() as $client) {
            if ($client->isMaintenance()) {
                continue;
            }
            $status = $client->getStatus();

            $clientAvailable = in_array($status, ['active', 'windows', 'linux']);
            $totalAvailableClients += $clientAvailable ? 1 : 0;
        }

        return $totalAvailableClients;
    }
}