<?php

/**
 * Chill is a software for social workers.
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;

use Chill\MainBundle\Entity\User;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;

/**
 * Create a subscription for a user.
 */
class EventsOnUserSubscriptionCreator
{
    public function __construct(private readonly LoggerInterface $logger, private readonly MachineHttpClient $machineHttpClient, private readonly MapCalendarToUser $mapCalendarToUser, private readonly UrlGeneratorInterface $urlGenerator) {}

    /**
     * @return array{secret: string, id: string, expiration: int}
     *
     * @throws ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function createSubscriptionForUser(User $user, \DateTimeImmutable $expiration): array
    {
        if (null === $userId = $this->mapCalendarToUser->getUserId($user)) {
            throw new \LogicException('no user id');
        }

        $subscription = [
            'changeType' => 'deleted,updated',
            'notificationUrl' => $this->urlGenerator->generate(
                'chill_calendar_remote_msgraph_incoming_webhook_events',
                ['userId' => $user->getId()],
                UrlGeneratorInterface::ABSOLUTE_URL
            ),
            'resource' => "/users/{$userId}/calendar/events",
            'clientState' => $secret = base64_encode(openssl_random_pseudo_bytes(92, $cstrong)),
            'expirationDateTime' => $expiration->format(\DateTimeImmutable::ATOM),
        ];

        try {
            $subs = $this->machineHttpClient->request(
                'POST',
                '/v1.0/subscriptions',
                [
                    'json' => $subscription,
                ]
            )->toArray();
        } catch (ClientExceptionInterface $e) {
            $this->logger->error('could not create subscription for user events', [
                'body' => $e->getResponse()->getContent(false),
            ]);

            return ['secret' => '', 'id' => '', 'expiration' => 0];
        }

        return ['secret' => $secret, 'id' => $subs['id'], 'expiration' => $expiration->getTimestamp()];
    }

    /**
     * @return array{secret: string, id: string, expiration: int}
     *
     * @throws ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function renewSubscriptionForUser(User $user, \DateTimeImmutable $expiration): array
    {
        if (null === $userId = $this->mapCalendarToUser->getUserId($user)) {
            throw new \LogicException('no user id');
        }

        if (null === $subscriptionId = $this->mapCalendarToUser->getActiveSubscriptionId($user)) {
            throw new \LogicException('no user id');
        }

        $subscription = [
            'expirationDateTime' => $expiration->format(\DateTimeImmutable::ATOM),
        ];

        try {
            $subs = $this->machineHttpClient->request(
                'PATCH',
                "/v1.0/subscriptions/{$subscriptionId}",
                [
                    'json' => $subscription,
                ]
            )->toArray();
        } catch (ClientExceptionInterface $e) {
            $this->logger->error('could not patch subscription for user events', [
                'body' => $e->getResponse()->getContent(false),
            ]);

            return ['secret' => '', 'id' => '', 'expiration' => 0];
        }

        return ['secret' => $subs['clientState'], 'id' => $subs['id'], 'expiration' => $expiration->getTimestamp()];
    }
}
