<?php
/**
 * Minimal Google Calendar API client using the Niwa OAuth relay.
 *
 * @package Niwa_AI
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Niwa_AI_Google_Calendar_Client {

	/** @var Niwa_AI_Calendar_OAuth_Relay_Client */
	private $relay;

	public function __construct( Niwa_AI_Calendar_OAuth_Relay_Client $relay ) {
		$this->relay = $relay;
	}

	/** Return normalized primary-calendar busy periods for a UTC range. */
	public function busy_periods( $connector_token, DateTimeInterface $time_min, DateTimeInterface $time_max ) {
		$access_token = $this->relay->access_token( $connector_token );
		if ( is_wp_error( $access_token ) ) {
			return $access_token;
		}

		$result = $this->calendar_request(
			'POST',
			'/freeBusy',
			$access_token,
			array(
				'timeMin' => $time_min->format( DateTimeInterface::RFC3339 ),
				'timeMax' => $time_max->format( DateTimeInterface::RFC3339 ),
				'items'   => array( array( 'id' => 'primary' ) ),
			)
		);
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		if ( ! is_array( $result['calendars']['primary'] ?? null ) ) {
			return new WP_Error( 'google_calendar_freebusy_failed', __( 'Google Calendar returned an incomplete busy-time response.', 'niwa-ai' ) );
		}
		$primary = $result['calendars']['primary'];
		if ( ! empty( $primary['errors'] ) ) {
			return new WP_Error( 'google_calendar_freebusy_failed', __( 'Google Calendar could not read busy times for the connected calendar.', 'niwa-ai' ) );
		}
		return self::normalize_busy_periods( $primary['busy'] ?? array() );
	}

	/** Convert Google RFC3339 busy rows to timestamp pairs. */
	public static function normalize_busy_periods( $rows ) {
		$periods = array();
		foreach ( is_array( $rows ) ? $rows : array() as $row ) {
			$raw_start = trim( (string) ( $row['start'] ?? '' ) );
			$raw_end   = trim( (string) ( $row['end'] ?? '' ) );
			if ( '' === $raw_start || '' === $raw_end ) {
				continue;
			}
			try {
				$start = new DateTimeImmutable( $raw_start );
				$end   = new DateTimeImmutable( $raw_end );
			} catch ( Exception $error ) {
				continue;
			}
			if ( $end <= $start ) {
				continue;
			}
			$periods[] = array(
				'start' => $start->getTimestamp(),
				'end'   => $end->getTimestamp(),
			);
		}
		return $periods;
	}

	/** Create/update or remove the event for one appointment. */
	public function sync_appointment( $connector_token, array $appointment, $create_first = false ) {
		$access_token = $this->relay->access_token( $connector_token );
		if ( is_wp_error( $access_token ) ) {
			return $access_token;
		}

		$status = isset( $appointment['status'] ) ? (string) $appointment['status'] : '';
		if ( in_array( $status, array( 'cancelled', 'no_show' ), true ) ) {
			return $this->delete_event( $access_token, self::event_id( $appointment ) );
		}

		$event_id = self::event_id( $appointment );
		$body     = self::event_body( $appointment, $event_id );
		if ( is_wp_error( $body ) ) {
			return $body;
		}

		if ( $create_first ) {
			$result = $this->calendar_request( 'POST', '/calendars/primary/events', $access_token, $body );
			return is_wp_error( $result ) && 409 === (int) $result->get_error_data( 'google_calendar_http' )
				? $this->calendar_request( 'PUT', '/calendars/primary/events/' . rawurlencode( $event_id ), $access_token, $body )
				: $result;
		}

		$result = $this->calendar_request( 'PUT', '/calendars/primary/events/' . rawurlencode( $event_id ), $access_token, $body );
		return is_wp_error( $result ) && 404 === (int) $result->get_error_data( 'google_calendar_http' )
			? $this->calendar_request( 'POST', '/calendars/primary/events', $access_token, $body )
			: $result;
	}

	/** Stable Google event ID; identical calendars naturally deduplicate it. */
	public static function event_id( array $appointment ) {
		$booking_uid = isset( $appointment['booking_uid'] ) ? (string) $appointment['booking_uid'] : (string) ( $appointment['id'] ?? '' );
		return sha1( home_url( '/' ) . '|niwa-booking|' . $booking_uid );
	}

	/** Build the Google event payload from the canonical appointment snapshot. */
	public static function event_body( array $appointment, $event_id = '' ) {
		$start = self::utc_datetime( $appointment['starts_at_utc'] ?? '' );
		$end   = self::utc_datetime( $appointment['ends_at_utc'] ?? '' );
		if ( '' === $start || '' === $end ) {
			return new WP_Error( 'calendar_time_invalid', __( 'The booking has no valid UTC start or end time.', 'niwa-ai' ) );
		}

		$service  = trim( (string) ( $appointment['service_name'] ?? __( 'Appointment', 'niwa-ai' ) ) );
		$customer = trim( (string) ( $appointment['customer_name'] ?? '' ) );
		$summary  = '' !== $customer ? $service . ' — ' . $customer : $service;
		$details  = array();
		foreach (
			array(
				__( 'Customer', 'niwa-ai' ) => $customer,
				__( 'Email', 'niwa-ai' )    => $appointment['customer_email'] ?? '',
				__( 'Phone', 'niwa-ai' )    => $appointment['customer_phone'] ?? '',
				__( 'Team member', 'niwa-ai' ) => $appointment['team_member_name'] ?? '',
				__( 'Booking reference', 'niwa-ai' ) => $appointment['booking_uid'] ?? '',
			) as $label => $value
		) {
			$value = trim( (string) $value );
			if ( '' !== $value ) {
				$details[] = $label . ': ' . $value;
			}
		}
		foreach ( array( 'details_snapshot', 'notes' ) as $field ) {
			$value = trim( (string) ( $appointment[ $field ] ?? '' ) );
			if ( '' !== $value ) {
				$details[] = '';
				$details[] = $value;
			}
		}

		return array(
			'id'          => '' !== $event_id ? (string) $event_id : self::event_id( $appointment ),
			'summary'     => $summary,
			'description' => implode( "\n", $details ),
			'start'       => array( 'dateTime' => $start ),
			'end'         => array( 'dateTime' => $end ),
			'reminders'   => array( 'useDefault' => true ),
			'extendedProperties' => array(
				'private' => array( 'niwa_booking_uid' => (string) ( $appointment['booking_uid'] ?? '' ) ),
			),
		);
	}

	private function delete_event( $access_token, $event_id ) {
		$result = $this->calendar_request( 'DELETE', '/calendars/primary/events/' . rawurlencode( $event_id ), $access_token );
		if ( is_wp_error( $result ) && in_array( (int) $result->get_error_data( 'google_calendar_http' ), array( 404, 410 ), true ) ) {
			return array( 'deleted' => true );
		}
		return $result;
	}

	private function calendar_request( $method, $path, $access_token, array $body = null ) {
		$args = array(
			'method'  => (string) $method,
			'timeout' => 20,
			'headers' => array(
				'Authorization' => 'Bearer ' . (string) $access_token,
				'Content-Type'  => 'application/json; charset=UTF-8',
			),
		);
		if ( null !== $body ) {
			$args['body'] = wp_json_encode( $body );
		}
		$response = wp_remote_request( 'https://www.googleapis.com/calendar/v3' . $path, $args );
		if ( ! is_wp_error( $response ) && 204 === (int) wp_remote_retrieve_response_code( $response ) ) {
			return array( 'deleted' => true );
		}
		return $this->decode_response( $response, 'google_calendar_http' );
	}

	private function decode_response( $response, $error_code ) {
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		$status = (int) wp_remote_retrieve_response_code( $response );
		$body   = (string) wp_remote_retrieve_body( $response );
		$data   = '' !== $body ? json_decode( $body, true ) : array();
		if ( $status < 200 || $status >= 300 ) {
			$error   = is_array( $data ) ? ( $data['error'] ?? '' ) : '';
			$message = is_array( $error ) ? (string) ( $error['message'] ?? '' ) : (string) $error;
			if ( '' === $message && is_array( $data ) ) {
				$message = (string) ( $data['error_description'] ?? '' );
			}
			return new WP_Error( $error_code, '' !== $message ? $message : sprintf( __( 'Google Calendar returned HTTP %d.', 'niwa-ai' ), $status ), $status );
		}
		return is_array( $data ) ? $data : array();
	}

	private static function utc_datetime( $value ) {
		$date = DateTimeImmutable::createFromFormat( 'Y-m-d H:i:s', (string) $value, new DateTimeZone( 'UTC' ) );
		return $date ? $date->format( 'Y-m-d\TH:i:s\Z' ) : '';
	}
}
