<?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;

use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\CalendarBundle\Entity\Invite;
use Chill\CalendarBundle\Messenger\Message\CalendarMessage;
use Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent;
use Chill\MainBundle\Entity\User;
use Symfony\Component\HttpFoundation\Response;

/**
 * Contract for connectors that synchronize Chill calendars with a remote
 * calendar provider (for example Microsoft 365/Graph, Zimbra, ...).
 *
 * Implementations act as an adapter between Chill domain objects
 * (Calendar, CalendarRange, Invite) and the remote provider API. They must:
 * - expose a readiness flow for per-user authorization when applicable
 *   (see {@see getMakeReadyResponse()} and {@see isReady()});
 * - list and count remote events in a time range for a given user;
 * - mirror local lifecycle changes to the remote provider for calendars,
 *   calendar ranges (availability/busy blocks) and invites/attendees.
 *
 * Use {@see MSGraphRemoteCalendarConnector} as a reference implementation for
 * expected behaviours, error handling and parameter semantics.
 */
interface RemoteCalendarConnectorInterface
{
    public function countEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int;

    /**
     * Returns a Response (typically a RedirectResponse) that lets the current
     * user perform the steps required to make the connector usable (for
     * example, OAuth consent or account linking). After completion, the user
     * should be redirected back to the given path.
     */
    public function getMakeReadyResponse(string $returnPath): Response;

    /**
     * Returns true when the connector is ready to access the remote provider
     * on behalf of the current user (e.g. required tokens/consent exist).
     */
    public function isReady(): bool;

    /**
     * Lists events from the remote provider for the given user and time range.
     *
     * Implementations should map provider-specific payloads to instances of
     * {@see RemoteEvent}.
     *
     * @return array|RemoteEvent[]
     */
    public function listEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array;

    /**
     * Removes a calendar (single event) from the remote provider.
     *
     * **Note**: calendar (single event) which are canceled will appears in this
     * method, and not in syncCalendar method.
     *
     * Parameters:
     * - remoteId: the provider identifier of the remote event to delete. If
     *   empty, implementations should no-op.
     * - remoteAttributes: provider-specific metadata previously stored with the
     *   local entity (e.g. change keys, etags) that can help perform safe
     *   concurrency checks when deleting. Implementations may ignore unknown
     *   keys.
     * - user: the user in whose remote calendar the event lives and on whose
     *   behalf the deletion must be performed.
     * - associatedCalendarRange: when provided, the implementation should
     *   update/synchronize the corresponding remote busy-time block after the
     *   event removal so that availability stays consistent.
     */
    public function removeCalendar(string $remoteId, array $remoteAttributes, User $user, ?CalendarRange $associatedCalendarRange = null): void;

    /**
     * Removes a remote busy-time block (calendar range) identified by
     * provider-specific id and attributes for the given user.
     *
     * Implementations should no-op if the id is empty.
     */
    public function removeCalendarRange(string $remoteId, array $remoteAttributes, User $user): void;

    /**
     * Synchronizes a Calendar entity to the remote provider.
     *
     * Typical cases to support (see MSGraph implementation):
     * - Creating the event on the remote calendar when it has no remote id.
     * - Updating the existing remote event when details or attendees change.
     * - Handling main user changes: cancel on the previous user's calendar,
     *   (re)create associated ranges where needed, then create on the new
     *   main user's calendar.
     * - If the Calendar uses a CalendarRange that already exists remotely,
     *   implementations should remove/update that remote range when the event
     *   becomes the source of truth for busy times.
     *
     * The implementation should not expects to receive calendar which are canceled
     * here.
     *
     * Parameters:
     * - calendar: the domain Calendar to mirror remotely.
     * - action: a hint about what triggered the sync; implementations should not rely
     *   solely on this value and must base decisions on the Calendar state.
     * - previousCalendarRange: if the Calendar was previously attached to a
     *   different range, this contains the former range so it can be recreated
     *   remotely to preserve availability history when applicable.
     * - previousMainUser: the former main user, when the main user changed;
     *   used to cancel the event in the previous user's calendar.
     * - oldInvites: the attendee snapshot before the change. Each item is an
     *   array with keys: inviteId, userId, userEmail, userLabel.
     * - newInvites: the attendee snapshot after the change, same shape as
     *   oldInvites. Implementations can compute diffs to add/remove attendees.
     *
     * The $action argument is a string tag indicating what happened to the
     * calendar. It MUST be one of the constants defined on
     * {@see CalendarMessage}:
     * - {@see CalendarMessage::CALENDAR_PERSIST}
     * - {@see CalendarMessage::CALENDAR_UPDATE}
     *
     * @param array<array{inviteId: int, userId: int, userEmail: int, userLabel: string}> $oldInvites
     *
     * @phpstan-param (CalendarMessage::CALENDAR_PERSIST|CalendarMessage::CALENDAR_UPDATE) $action
     */
    public function syncCalendar(Calendar $calendar, string $action, ?CalendarRange $previousCalendarRange, ?User $previousMainUser, ?array $oldInvites, ?array $newInvites): void;

    /**
     * Creates or updates a remote busy-time block representing the provided
     * CalendarRange. If the range has a remote id, it should be updated;
     * otherwise it should be created remotely, and the range enriched with
     * the new id/attributes by the caller.
     */
    public function syncCalendarRange(CalendarRange $calendarRange): void;

    /**
     * Synchronizes a single Invite (attendee) change to the remote provider.
     * Implementations may need to lookup the attendee's personal calendar to
     * find provider-specific identifiers before patching the main event.
     */
    public function syncInvite(Invite $invite): void;
}
