<?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\Tests\RemoteCalendar\Connector\MSGraph;

use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\CalendarBundle\Entity\Invite;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteToLocalSync\CalendarSyncer;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

/**
 * @internal
 *
 * @coversNothing
 */
final class CalendarSyncerTest extends TestCase
{
    use ProphecyTrait;

    private const NOTIF_DELETE = <<<'JSON'
        {
            "value": [
                {
                    "subscriptionId": "077e8d19-68b3-4d8e-9b1e-8b4ba6733799",
                    "subscriptionExpirationDateTime": "2022-06-09T06:22:02-07:00",
                    "changeType": "deleted",
                    "resource": "Users/4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4/Events/AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA=",
                    "resourceData": {
                        "@odata.type": "#Microsoft.Graph.Event",
                        "@odata.id": "Users/4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4/Events/AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA=",
                        "@odata.etag": "W/\"CQAAAA==\"",
                        "id": "AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA="
                    },
                    "clientState": "uds18apRCByqWIodFCHKeM0kJqhfr+qXL/rJWYn7xmtdQ4t03W2OHEOdGJ0Ceo52NAzOYVDpbfRM3TdrZDUiE09OxZkPX/vkpdcnipoiVnPPMFBQn05p8KhklOM=",
                    "tenantId": "421bf216-3f48-47bd-a7cf-8b1995cb24bd"
                }
            ]
        }
        JSON;

    private const NOTIF_UPDATE = <<<'JSON'
        {
            "value": [
                {
                    "subscriptionId": "739703eb-80c4-4c03-b15a-ca370f19624b",
                    "subscriptionExpirationDateTime": "2022-06-09T02:40:28-07:00",
                    "changeType": "updated",
                    "resource": "Users/4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4/Events/AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA=",
                    "resourceData": {
                        "@odata.type": "#Microsoft.Graph.Event",
                        "@odata.id": "Users/4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4/Events/AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA=",
                        "@odata.etag": "W/\"DwAAABYAAAAHduaxajFfTpv0kchk+m1FAAAlyzAU\"",
                        "id": "AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA="
                    },
                    "clientState": "2k05qlr3ds2KzvUP3Ps4A+642fYaI8ThxHGIGbNr2p0MnNkmzxLTNEMxpMc/UEuDkBHfID7OYWj4DQc94vlEkPBdsh9sGTTkHxIE68hqkKkDKhwvfvdj6lS6Dus=",
                    "tenantId": "421bf216-3f48-47bd-a7cf-8b1995cb24bd"
                }
            ]
        }
        JSON;

    private const REMOTE_CALENDAR_NO_ATTENDEES = <<<'JSON'
        {
            "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4')/events/$entity",
            "@odata.etag": "W/\"B3bmsWoxX06b9JHIZPptRQAAJcswFA==\"",
            "id": "AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BupAAA=",
            "createdDateTime": "2022-06-08T15:22:24.0096697Z",
            "lastModifiedDateTime": "2022-06-09T09:27:09.9223729Z",
            "changeKey": "B3bmsWoxX06b9JHIZPptRQAAJcswFA==",
            "categories": [],
            "transactionId": "90c23105-a6b1-b594-1811-e4ffa612092a",
            "originalStartTimeZone": "Romance Standard Time",
            "originalEndTimeZone": "Romance Standard Time",
            "iCalUId": "040000008200E00074C5B7101A82E00800000000A971DA8D4B7BD801000000000000000010000000BE3F4A21C9008E4FB35A4DE1F80E0118",
            "reminderMinutesBeforeStart": 15,
            "isReminderOn": true,
            "hasAttachments": false,
            "subject": "test notif",
            "bodyPreview": "",
            "importance": "normal",
            "sensitivity": "normal",
            "isAllDay": false,
            "isCancelled": false,
            "isOrganizer": true,
            "responseRequested": true,
            "seriesMasterId": null,
            "showAs": "busy",
            "type": "singleInstance",
            "webLink": "https://outlook.office365.com/owa/?itemid=AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk%2Bm1FAAAAAAENAAAHduaxajFfTpv0kchk%2Bm1FAAAl1BupAAA%3D&exvsurl=1&path=/calendar/item",
            "onlineMeetingUrl": null,
            "isOnlineMeeting": false,
            "onlineMeetingProvider": "unknown",
            "allowNewTimeProposals": true,
            "occurrenceId": null,
            "isDraft": false,
            "hideAttendees": false,
            "responseStatus": {
                "response": "organizer",
                "time": "0001-01-01T00:00:00Z"
            },
            "body": {
                "contentType": "html",
                "content": ""
            },
            "start": {
                "dateTime": "2022-06-10T13:30:00.0000000",
                "timeZone": "UTC"
            },
            "end": {
                "dateTime": "2022-06-10T15:30:00.0000000",
                "timeZone": "UTC"
            },
            "location": {
                "displayName": "",
                "locationType": "default",
                "uniqueIdType": "unknown",
                "address": {},
                "coordinates": {}
            },
            "locations": [],
            "recurrence": null,
            "attendees": [],
            "organizer": {
                "emailAddress": {
                    "name": "Diego Siciliani",
                    "address": "DiegoS@2zy74l.onmicrosoft.com"
                }
            },
            "onlineMeeting": null,
            "calendar@odata.associationLink": "https://graph.microsoft.com/v1.0/Users('4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4')/calendar/$ref",
            "calendar@odata.navigationLink": "https://graph.microsoft.com/v1.0/Users('4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4')/calendar"
        }
        JSON;

    private const REMOTE_CALENDAR_NOT_ORGANIZER = <<<'JSON'
            {
              "@odata.etag": "W/\"B3bmsWoxX06b9JHIZPptRQAAJcrv7A==\"",
              "id": "AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BuqAAA=",
              "createdDateTime": "2022-06-08T16:19:18.997293Z",
              "lastModifiedDateTime": "2022-06-08T16:22:18.7276485Z",
              "changeKey": "B3bmsWoxX06b9JHIZPptRQAAJcrv7A==",
              "categories": [],
              "transactionId": "42ac0b77-313e-20ca-2cac-1a3f58b3452d",
              "originalStartTimeZone": "Romance Standard Time",
              "originalEndTimeZone": "Romance Standard Time",
              "iCalUId": "040000008200E00074C5B7101A82E00800000000A8955A81537BD801000000000000000010000000007EB443987CD641B6794C4BA48EE356",
              "reminderMinutesBeforeStart": 15,
              "isReminderOn": true,
              "hasAttachments": false,
              "subject": "test 2",
              "bodyPreview": "________________________________________________________________________________\r\nRéunion Microsoft Teams\r\nRejoindre sur votre ordinateur ou application mobile\r\nCliquez ici pour participer à la réunion\r\nPour en savoir plus | Options de réunion\r\n________",
              "importance": "normal",
              "sensitivity": "normal",
              "isAllDay": false,
              "isCancelled": false,
              "isOrganizer": false,
              "responseRequested": true,
              "seriesMasterId": null,
              "showAs": "busy",
              "type": "singleInstance",
              "webLink": "https://outlook.office365.com/owa/?itemid=AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk%2Bm1FAAAAAAENAAAHduaxajFfTpv0kchk%2Bm1FAAAl1BuqAAA%3D&exvsurl=1&path=/calendar/item",
              "onlineMeetingUrl": null,
              "isOnlineMeeting": true,
              "onlineMeetingProvider": "teamsForBusiness",
              "allowNewTimeProposals": true,
              "occurrenceId": null,
              "isDraft": false,
              "hideAttendees": false,
              "responseStatus": {
                "response": "organizer",
                "time": "0001-01-01T00:00:00Z"
              },
              "body": {
                "contentType": "html",
                "content": "<html>\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n</head>\r\n<body>\r\n<div></div>\r\n<br>\r\n<div style=\"width:100%; height:20px\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span>\r\n</div>\r\n<div class=\"me-email-text\" lang=\"fr-FR\" style=\"color:#252424; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\n<div style=\"margin-top:24px; margin-bottom:20px\"><span style=\"font-size:24px; color:#252424\">Réunion Microsoft Teams</span>\r\n</div>\r\n<div style=\"margin-bottom:20px\">\r\n<div style=\"margin-top:0px; margin-bottom:0px; font-weight:bold\"><span style=\"font-size:14px; color:#252424\">Rejoindre sur votre ordinateur ou application mobile</span>\r\n</div>\r\n<a href=\"https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2%40thread.v2/0?context=%7b%22Tid%22%3a%22421bf216-3f48-47bd-a7cf-8b1995cb24bd%22%2c%22Oid%22%3a%224feb0ae3-7ffb-48dd-891e-c86b2cdeefd4%22%7d\" class=\"me-email-headline\" style=\"font-size:14px; font-family:'Segoe UI Semibold','Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif; text-decoration:underline; color:#6264a7\">Cliquez\r\n ici pour participer à la réunion</a> </div>\r\n<div style=\"margin-bottom:24px; margin-top:20px\"><a href=\"https://aka.ms/JoinTeamsMeeting\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Pour en savoir\r\n plus</a> | <a href=\"https://teams.microsoft.com/meetingOptions/?organizerId=4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4&amp;tenantId=421bf216-3f48-47bd-a7cf-8b1995cb24bd&amp;threadId=19_meeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2@thread.v2&amp;messageId=0&amp;language=fr-FR\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\nOptions de réunion</a> </div>\r\n</div>\r\n<div style=\"font-size:14px; margin-bottom:4px; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\n</div>\r\n<div style=\"font-size:12px\"></div>\r\n<div></div>\r\n<div style=\"width:100%; height:20px\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span>\r\n</div>\r\n</body>\r\n</html>\r\n"
              },
              "start": {
                "dateTime": "2022-06-11T12:30:00.0000000",
                "timeZone": "UTC"
              },
              "end": {
                "dateTime": "2022-06-11T13:30:00.0000000",
                "timeZone": "UTC"
              },
              "location": {
                "displayName": "",
                "locationType": "default",
                "uniqueIdType": "unknown",
                "address": {},
                "coordinates": {}
              },
              "locations": [],
              "recurrence": null,
              "attendees": [
                {
                  "type": "required",
                  "status": {
                    "response": "accepted",
                    "time": "2022-06-08T16:22:15.1392583Z"
                  },
                  "emailAddress": {
                    "name": "Alex Wilber",
                    "address": "AlexW@2zy74l.onmicrosoft.com"
                  }
                },
                {
                  "type": "required",
                  "status": {
                    "response": "declined",
                    "time": "2022-06-08T16:22:15.1392583Z"
                  },
                  "emailAddress": {
                    "name": "Alfred Nobel",
                    "address": "alfredN@2zy74l.onmicrosoft.com"
                  }
                }
              ],
              "organizer": {
                "emailAddress": {
                  "name": "Diego Siciliani",
                  "address": "DiegoS@2zy74l.onmicrosoft.com"
                }
              },
              "onlineMeeting": {
                "joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2%40thread.v2/0?context=%7b%22Tid%22%3a%22421bf216-3f48-47bd-a7cf-8b1995cb24bd%22%2c%22Oid%22%3a%224feb0ae3-7ffb-48dd-891e-c86b2cdeefd4%22%7d"
              }
            }
        JSON;

    private const REMOTE_CALENDAR_WITH_ATTENDEES = <<<'JSON'
            {
              "@odata.etag": "W/\"B3bmsWoxX06b9JHIZPptRQAAJcrv7A==\"",
              "id": "AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BuqAAA=",
              "createdDateTime": "2022-06-08T16:19:18.997293Z",
              "lastModifiedDateTime": "2022-06-08T16:22:18.7276485Z",
              "changeKey": "B3bmsWoxX06b9JHIZPptRQAAJcrv7A==",
              "categories": [],
              "transactionId": "42ac0b77-313e-20ca-2cac-1a3f58b3452d",
              "originalStartTimeZone": "Romance Standard Time",
              "originalEndTimeZone": "Romance Standard Time",
              "iCalUId": "040000008200E00074C5B7101A82E00800000000A8955A81537BD801000000000000000010000000007EB443987CD641B6794C4BA48EE356",
              "reminderMinutesBeforeStart": 15,
              "isReminderOn": true,
              "hasAttachments": false,
              "subject": "test 2",
              "bodyPreview": "________________________________________________________________________________\r\nRéunion Microsoft Teams\r\nRejoindre sur votre ordinateur ou application mobile\r\nCliquez ici pour participer à la réunion\r\nPour en savoir plus | Options de réunion\r\n________",
              "importance": "normal",
              "sensitivity": "normal",
              "isAllDay": false,
              "isCancelled": false,
              "isOrganizer": true,
              "responseRequested": true,
              "seriesMasterId": null,
              "showAs": "busy",
              "type": "singleInstance",
              "webLink": "https://outlook.office365.com/owa/?itemid=AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk%2Bm1FAAAAAAENAAAHduaxajFfTpv0kchk%2Bm1FAAAl1BuqAAA%3D&exvsurl=1&path=/calendar/item",
              "onlineMeetingUrl": null,
              "isOnlineMeeting": true,
              "onlineMeetingProvider": "teamsForBusiness",
              "allowNewTimeProposals": true,
              "occurrenceId": null,
              "isDraft": false,
              "hideAttendees": false,
              "responseStatus": {
                "response": "organizer",
                "time": "0001-01-01T00:00:00Z"
              },
              "body": {
                "contentType": "html",
                "content": "<html>\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n</head>\r\n<body>\r\n<div></div>\r\n<br>\r\n<div style=\"width:100%; height:20px\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span>\r\n</div>\r\n<div class=\"me-email-text\" lang=\"fr-FR\" style=\"color:#252424; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\n<div style=\"margin-top:24px; margin-bottom:20px\"><span style=\"font-size:24px; color:#252424\">Réunion Microsoft Teams</span>\r\n</div>\r\n<div style=\"margin-bottom:20px\">\r\n<div style=\"margin-top:0px; margin-bottom:0px; font-weight:bold\"><span style=\"font-size:14px; color:#252424\">Rejoindre sur votre ordinateur ou application mobile</span>\r\n</div>\r\n<a href=\"https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2%40thread.v2/0?context=%7b%22Tid%22%3a%22421bf216-3f48-47bd-a7cf-8b1995cb24bd%22%2c%22Oid%22%3a%224feb0ae3-7ffb-48dd-891e-c86b2cdeefd4%22%7d\" class=\"me-email-headline\" style=\"font-size:14px; font-family:'Segoe UI Semibold','Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif; text-decoration:underline; color:#6264a7\">Cliquez\r\n ici pour participer à la réunion</a> </div>\r\n<div style=\"margin-bottom:24px; margin-top:20px\"><a href=\"https://aka.ms/JoinTeamsMeeting\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Pour en savoir\r\n plus</a> | <a href=\"https://teams.microsoft.com/meetingOptions/?organizerId=4feb0ae3-7ffb-48dd-891e-c86b2cdeefd4&amp;tenantId=421bf216-3f48-47bd-a7cf-8b1995cb24bd&amp;threadId=19_meeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2@thread.v2&amp;messageId=0&amp;language=fr-FR\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\nOptions de réunion</a> </div>\r\n</div>\r\n<div style=\"font-size:14px; margin-bottom:4px; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\n</div>\r\n<div style=\"font-size:12px\"></div>\r\n<div></div>\r\n<div style=\"width:100%; height:20px\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span>\r\n</div>\r\n</body>\r\n</html>\r\n"
              },
              "start": {
                "dateTime": "2022-06-11T12:30:00.0000000",
                "timeZone": "UTC"
              },
              "end": {
                "dateTime": "2022-06-11T13:30:00.0000000",
                "timeZone": "UTC"
              },
              "location": {
                "displayName": "",
                "locationType": "default",
                "uniqueIdType": "unknown",
                "address": {},
                "coordinates": {}
              },
              "locations": [],
              "recurrence": null,
              "attendees": [
                {
                  "type": "required",
                  "status": {
                    "response": "accepted",
                    "time": "2022-06-08T16:22:15.1392583Z"
                  },
                  "emailAddress": {
                    "name": "Alex Wilber",
                    "address": "AlexW@2zy74l.onmicrosoft.com"
                  }
                },
                {
                  "type": "required",
                  "status": {
                    "response": "accepted",
                    "time": "2022-06-08T16:22:15.1392583Z"
                  },
                  "emailAddress": {
                    "name": "External User",
                    "address": "external@example.com"
                  }
                },
                {
                  "type": "required",
                  "status": {
                    "response": "declined",
                    "time": "2022-06-08T16:22:15.1392583Z"
                  },
                  "emailAddress": {
                    "name": "Alfred Nobel",
                    "address": "alfredN@2zy74l.onmicrosoft.com"
                  }
                }
              ],
              "organizer": {
                "emailAddress": {
                  "name": "Diego Siciliani",
                  "address": "DiegoS@2zy74l.onmicrosoft.com"
                }
              },
              "onlineMeeting": {
                "joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2%40thread.v2/0?context=%7b%22Tid%22%3a%22421bf216-3f48-47bd-a7cf-8b1995cb24bd%22%2c%22Oid%22%3a%224feb0ae3-7ffb-48dd-891e-c86b2cdeefd4%22%7d"
              }
            }
        JSON;

    public function testHandleAttendeesConfirmingCalendar(): void
    {
        $machineHttpClient = new MockHttpClient([
            new MockResponse(self::REMOTE_CALENDAR_WITH_ATTENDEES, ['http_code' => 200]),
        ]);

        $userA = (new User())->setEmail('alexw@2zy74l.onmicrosoft.com')
            ->setEmailCanonical('alexw@2zy74l.onmicrosoft.com');
        $userB = (new User())->setEmail('zzzzz@2zy74l.onmicrosoft.com')
            ->setEmailCanonical('zzzzz@2zy74l.onmicrosoft.com');
        $userC = (new User())->setEmail('alfredN@2zy74l.onmicrosoft.com')
            ->setEmailCanonical('alfredn@2zy74l.onmicrosoft.com');

        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $userRepository->findOneByUsernameOrEmail(Argument::exact('AlexW@2zy74l.onmicrosoft.com'))
            ->willReturn($userA);
        $userRepository->findOneByUsernameOrEmail(Argument::exact('zzzzz@2zy74l.onmicrosoft.com'))
            ->willReturn($userB);
        $userRepository->findOneByUsernameOrEmail(Argument::exact('alfredN@2zy74l.onmicrosoft.com'))
            ->willReturn($userC);
        $userRepository->findOneByUsernameOrEmail(Argument::exact('external@example.com'))
            ->willReturn(null);

        $calendarSyncer = new CalendarSyncer(
            new NullLogger(),
            $machineHttpClient,
            $userRepository->reveal()
        );

        $calendar = new Calendar();
        $calendar
            ->setMainUser($user = new User())
            ->setStartDate(new \DateTimeImmutable('2022-06-11 14:30:00'))
            ->setEndDate(new \DateTimeImmutable('2022-06-11 15:30:00'))
            ->addUser($userA)
            ->addUser($userB)
            ->setCalendarRange(new CalendarRange())
            ->addRemoteAttributes([
                'lastModifiedDateTime' => 0,
                'changeKey' => 'abcd',
            ]);

        $notification = json_decode(self::NOTIF_UPDATE, true);

        $calendarSyncer->handleCalendarSync(
            $calendar,
            $notification['value'][0],
            $user
        );

        $this->assertTrue($calendar->preventEnqueueChanges);
        $this->assertEquals(Calendar::STATUS_VALID, $calendar->getStatus());
        // user A is invited, and accepted
        $this->assertTrue($calendar->isInvited($userA));
        $this->assertEquals(Invite::ACCEPTED, $calendar->getInviteForUser($userA)->getStatus());
        $this->assertFalse($calendar->getInviteForUser($userA)->preventEnqueueChanges);
        // user B is no more invited
        $this->assertFalse($calendar->isInvited($userB));
        // user C is invited, but declined
        $this->assertFalse($calendar->getInviteForUser($userC)->preventEnqueueChanges);
        $this->assertTrue($calendar->isInvited($userC));
        $this->assertEquals(Invite::DECLINED, $calendar->getInviteForUser($userC)->getStatus());
    }

    public function testHandleDeleteCalendar(): void
    {
        $machineHttpClient = new MockHttpClient([]);
        $userRepository = $this->prophesize(UserRepositoryInterface::class);

        $calendarSyncer = new CalendarSyncer(
            new NullLogger(),
            $machineHttpClient,
            $userRepository->reveal()
        );

        $calendar = new Calendar();
        $calendar
            ->setMainUser($user = new User())
            ->setCalendarRange($calendarRange = new CalendarRange());

        $notification = json_decode(self::NOTIF_DELETE, true);

        $calendarSyncer->handleCalendarSync(
            $calendar,
            $notification['value'][0],
            $user
        );

        $this->assertEquals(Calendar::STATUS_CANCELED, $calendar->getStatus());
        $this->assertNull($calendar->getCalendarRange());
        $this->assertTrue($calendar->preventEnqueueChanges);
    }

    public function testHandleMoveCalendar(): void
    {
        $machineHttpClient = new MockHttpClient([
            new MockResponse(self::REMOTE_CALENDAR_NO_ATTENDEES, ['http_code' => 200]),
        ]);
        $userRepository = $this->prophesize(UserRepositoryInterface::class);

        $calendarSyncer = new CalendarSyncer(
            new NullLogger(),
            $machineHttpClient,
            $userRepository->reveal()
        );

        $calendar = new Calendar();
        $calendar
            ->setMainUser($user = new User())
            ->setStartDate(new \DateTimeImmutable('2020-01-01 10:00:00'))
            ->setEndDate(new \DateTimeImmutable('2020-01-01 12:00:00'))
            ->setCalendarRange(new CalendarRange())
            ->addRemoteAttributes([
                'lastModifiedDateTime' => 0,
                'changeKey' => 'abcd',
            ]);
        $previousVersion = $calendar->getDateTimeVersion();
        $notification = json_decode(self::NOTIF_UPDATE, true);

        $calendarSyncer->handleCalendarSync(
            $calendar,
            $notification['value'][0],
            $user
        );

        $this->assertStringContainsString(
            '2022-06-10T15:30:00',
            $calendar->getStartDate()->format(\DateTimeImmutable::ATOM)
        );
        $this->assertStringContainsString(
            '2022-06-10T17:30:00',
            $calendar->getEndDate()->format(\DateTimeImmutable::ATOM)
        );
        $this->assertTrue($calendar->preventEnqueueChanges);
        $this->assertGreaterThan($previousVersion, $calendar->getDateTimeVersion());
    }

    public function testHandleNotMovedCalendar(): void
    {
        $machineHttpClient = new MockHttpClient([
            new MockResponse(self::REMOTE_CALENDAR_NO_ATTENDEES, ['http_code' => 200]),
        ]);
        $userRepository = $this->prophesize(UserRepositoryInterface::class);

        $calendarSyncer = new CalendarSyncer(
            new NullLogger(),
            $machineHttpClient,
            $userRepository->reveal()
        );

        $calendar = new Calendar();
        $calendar
            ->setMainUser($user = new User())
            ->setStartDate(new \DateTimeImmutable('2022-06-10 15:30:00'))
            ->setEndDate(new \DateTimeImmutable('2022-06-10 17:30:00'))
            ->setCalendarRange(new CalendarRange())
            ->addRemoteAttributes([
                'lastModifiedDateTime' => 0,
                'changeKey' => 'abcd',
            ]);
        $notification = json_decode(self::NOTIF_UPDATE, true);

        $calendarSyncer->handleCalendarSync(
            $calendar,
            $notification['value'][0],
            $user
        );

        $this->assertStringContainsString(
            '2022-06-10T15:30:00',
            $calendar->getStartDate()->format(\DateTimeImmutable::ATOM)
        );
        $this->assertStringContainsString(
            '2022-06-10T17:30:00',
            $calendar->getEndDate()->format(\DateTimeImmutable::ATOM)
        );
        $this->assertTrue($calendar->preventEnqueueChanges);
        $this->assertEquals(Calendar::STATUS_VALID, $calendar->getStatus());
    }

    public function testHandleNotOrganizer(): void
    {
        // when isOrganiser === false, nothing should happens
        $machineHttpClient = new MockHttpClient([
            new MockResponse(self::REMOTE_CALENDAR_NOT_ORGANIZER, ['http_code' => 200]),
        ]);
        $userRepository = $this->prophesize(UserRepositoryInterface::class);

        $calendarSyncer = new CalendarSyncer(
            new NullLogger(),
            $machineHttpClient,
            $userRepository->reveal()
        );

        $calendar = new Calendar();
        $calendar
            ->setMainUser($user = new User())
            ->setStartDate(new \DateTimeImmutable('2020-01-01 10:00:00'))
            ->setEndDate(new \DateTimeImmutable('2020-01-01 12:00:00'))
            ->setCalendarRange(new CalendarRange())
            ->addRemoteAttributes([
                'lastModifiedDateTime' => 0,
                'changeKey' => 'abcd',
            ]);
        $notification = json_decode(self::NOTIF_UPDATE, true);

        $calendarSyncer->handleCalendarSync(
            $calendar,
            $notification['value'][0],
            $user
        );

        $this->assertStringContainsString(
            '2020-01-01T10:00:00',
            $calendar->getStartDate()->format(\DateTimeImmutable::ATOM)
        );
        $this->assertStringContainsString(
            '2020-01-01T12:00:00',
            $calendar->getEndDate()->format(\DateTimeImmutable::ATOM)
        );

        $this->assertEquals(Calendar::STATUS_VALID, $calendar->getStatus());
    }
}
