<?php

namespace SugarCalendarBookings\Integrations\Outlook;

use SugarCalendarBookings\Integrations\OAuthRelay\AbstractIntegrationActionsOAuthClient;
use SugarCalendarBookings\Integrations\OAuthRelay\OAuthConnectionInterface;
use SugarCalendarBookings\Vendor\ProductApi\Http\Response;
use WP_Error;

/**
 * Outlook Actions Client.
 *
 * Client for Microsoft Graph (Outlook Calendar) integration actions via Product API.
 *
 * @since 1.5.0
 */
class OutlookActionsClient extends AbstractIntegrationActionsOAuthClient {

	/**
	 * List the authenticated user's calendars.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection OAuth connection.
	 *
	 * @return array|WP_Error Calendars list payload or error.
	 */
	public function list_calendars( OAuthConnectionInterface $connection ) {

		$response = $this->get( '/microsoft/calendars', $connection )
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Create an event on a calendar.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection  OAuth connection.
	 * @param string                   $calendar_id Calendar ID to create the event on.
	 * @param array                    $data        Event payload (subject, start, end, attendees, etc.).
	 *
	 * @return array|WP_Error Created event payload or error.
	 */
	public function create_event(
		OAuthConnectionInterface $connection,
		string $calendar_id,
		array $data
	) {

		$path = '/microsoft/calendars/' . rawurlencode( $calendar_id ) . '/events';

		$response = $this->post( $path, $connection )
			->json( [ 'params' => [ 'body' => $data ] ] )
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Get an event by ID.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection OAuth connection.
	 * @param string                   $event_id   Event ID to fetch.
	 *
	 * @return array|WP_Error Event payload or error.
	 */
	public function get_event(
		OAuthConnectionInterface $connection,
		string $event_id
	) {

		$response = $this->get( '/microsoft/events/' . rawurlencode( $event_id ), $connection )
			->header( 'Prefer', 'outlook.timezone="UTC"' )
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Update an event (partial update).
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection OAuth connection.
	 * @param string                   $event_id   Event ID to update.
	 * @param array                    $data       Partial event payload.
	 *
	 * @return array|WP_Error Updated event payload or error.
	 */
	public function update_event(
		OAuthConnectionInterface $connection,
		string $event_id,
		array $data
	) {

		$response = $this->patch( '/microsoft/events/' . rawurlencode( $event_id ), $connection )
			->json( [ 'params' => [ 'body' => $data ] ] )
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Cancel an event and notify attendees.
	 *
	 * Sends a cancellation email to all attendees and marks the event as
	 * cancelled in their calendars. The event itself remains in the organiser's
	 * calendar — call {@see self::delete_event()} afterwards to remove it.
	 *
	 * Treats a 404 response as success (event already gone).
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection OAuth connection.
	 * @param string                   $event_id   Event ID to cancel.
	 * @param string                   $comment    Optional message sent to attendees.
	 *
	 * @return true|WP_Error True on success (including 404), or error.
	 */
	public function cancel_event(
		OAuthConnectionInterface $connection,
		string $event_id,
		string $comment = ''
	) {

		$path    = '/microsoft/events/' . rawurlencode( $event_id ) . '/cancel';
		$payload = $comment !== '' ? [ 'params' => [ 'body' => [ 'Comment' => $comment ] ] ] : [];

		$request = $this->post( $path, $connection );

		if ( ! empty( $payload ) ) {
			$request->json( $payload );
		}

		$response = $request->send();

		if ( $response instanceof Response && $response->get_status_code() === 404 ) {
			return true;
		}

		$result = $this->handle_response( $response );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return true;
	}

	/**
	 * Delete an event.
	 *
	 * Treats a 404 response as success (already deleted).
	 *
	 * The relay needs `calendar_id` so it can forward to Graph's calendar-scoped
	 * path (`/me/calendars/{calendarId}/events/{eventId}`). Without it, consumer
	 * accounts return 404 for events that live outside the primary calendar.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection  OAuth connection.
	 * @param string                   $event_id    Event ID to delete.
	 * @param string                   $calendar_id Calendar ID the event was created in.
	 *
	 * @return true|WP_Error True on success (including 404 already-deleted), or error.
	 */
	public function delete_event(
		OAuthConnectionInterface $connection,
		string $event_id,
		string $calendar_id
	) {

		$response = $this->delete( '/microsoft/events/' . rawurlencode( $event_id ), $connection )
			->json( [ 'calendar_id' => $calendar_id ] )
			->send();

		if ( $response instanceof Response && $response->get_status_code() === 404 ) {
			return true;
		}

		$result = $this->handle_response( $response );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return true;
	}

	/**
	 * List events in a specific calendar within a date range.
	 *
	 * Uses the calendar-scoped events endpoint instead of getSchedule so it
	 * works for both personal (Outlook.com) and work/school (Office 365)
	 * accounts and returns individual events filtered to a specific calendar.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection  OAuth connection.
	 * @param string                   $calendar_id Calendar ID to list events from.
	 * @param string                   $from_utc    Start datetime (Y-m-d\TH:i:s, UTC).
	 * @param string                   $to_utc      End datetime (Y-m-d\TH:i:s, UTC).
	 *
	 * @return array|WP_Error Events list payload or error.
	 */
	public function list_calendar_events(
		OAuthConnectionInterface $connection,
		string $calendar_id,
		string $from_utc,
		string $to_utc
	) {

		$path = '/microsoft/calendars/' . rawurlencode( $calendar_id ) . '/events';

		$response = $this->get( $path, $connection )
			// Force start/end in UTC so the parser doesn't have to handle
			// Windows TZ names (Graph defaults to the user's mailbox zone).
			->header( 'Prefer', 'outlook.timezone="UTC"' )
			->query(
				[
					// Overlap filter: captures events that start before $to and end after $from.
					'$filter'  => "start/dateTime lt '{$to_utc}' and end/dateTime gt '{$from_utc}'",
					'$select'  => 'id,subject,start,end,isAllDay',
					'$top'     => 500,
					'$orderby' => 'start/dateTime',
				]
			)
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Get free/busy schedule for one or more users.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection OAuth connection.
	 * @param array                    $data       Function getSchedule payload (schedules, startTime, endTime, availabilityViewInterval).
	 *
	 * @return array|WP_Error Schedule payload or error.
	 */
	public function get_schedule(
		OAuthConnectionInterface $connection,
		array $data
	) {

		$response = $this->post( '/microsoft/getSchedule', $connection )
			->json( [ 'params' => [ 'body' => $data ] ] )
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Create a change-notification subscription.
	 *
	 * Posts to the provider-agnostic `/integrations/v1/{license}/subscriptions`
	 * endpoint with `provider: outlook` at the top level — the relay dispatches
	 * to Microsoft Graph internally based on that flag.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection OAuth connection.
	 * @param array                    $data       Subscription payload (resource, changeType, site_webhook_url, expirationDateTime, etc.).
	 *
	 * @return array|WP_Error Subscription payload or error.
	 */
	public function create_subscription(
		OAuthConnectionInterface $connection,
		array $data
	) {

		// Microsoft Graph validates the subscription synchronously during creation:
		// it calls the relay's notification URL and waits for the echo before
		// responding. That round trip routinely exceeds WordPress's default 5s
		// request timeout, so give it more headroom to avoid a silent failure.
		$response = $this->post( '/subscriptions', $connection )
			->timeout( 15 )
			->json(
				[
					'provider' => 'outlook',
					'params'   => [ 'body' => $data ],
				]
			)
			->send();

		return $this->handle_response( $response );
	}

	/**
	 * Delete a change-notification subscription.
	 *
	 * Treats a 404 response as success (already deleted). The relay looks the
	 * subscription's provider up from its own store, so no body is needed.
	 *
	 * @since 1.5.0
	 *
	 * @param OAuthConnectionInterface $connection      OAuth connection.
	 * @param string                   $subscription_id Subscription ID to delete.
	 *
	 * @return true|WP_Error True on success (including 404 already-deleted), or error.
	 */
	public function delete_subscription(
		OAuthConnectionInterface $connection,
		string $subscription_id
	) {

		$path = '/subscriptions/' . rawurlencode( $subscription_id );

		$response = $this->delete( $path, $connection )
			->send();

		if ( $response instanceof Response && $response->get_status_code() === 404 ) {
			return true;
		}

		$result = $this->handle_response( $response );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return true;
	}
}
