<?php //phpcs:ignore
/**
 * Routes for meetings
 *
 * @package SOVLIX\GOODMEET
 */

namespace SOVLIX\GOODMEET\API;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

//phpcs:ignore
use WP_REST_Controller;
//phpcs:ignore
use WP_REST_Server;
//phpcs:ignore
use WP_Error;

if ( ! class_exists( 'Goodmeet_Meetings' ) ) {
	/**
	 * Goodmeet_Meetings Class
	 */
	class Goodmeet_Meetings extends \WP_REST_Controller {
		/**
		 * Google meet Api class.
		 *
		 * @var Goodmeet_Events
		 */
		private $google_meet_api;

		/**
		 * Goodmeet_Meetings constructor
		 */
		public function __construct() {
			$this->namespace       = 'goodmeet/v1';
			$this->rest_base       = 'meetings';
			$this->google_meet_api = new \SOVLIX\GOODMEET\Googlemeet\Goodmeet_Events();
		}

		/**
		 * Register REST API routes
		 *
		 * @since 1.0.0
		 */
		public function register_routes() {

			register_rest_route(
				$this->namespace,
				'/' . $this->rest_base,
				array(
					array(
						'methods'             => WP_REST_Server::READABLE,
						'callback'            => array( $this, 'get_items' ),
						'permission_callback' => array( $this, 'check_permissions' ),
					),
					array(
						'methods'             => WP_REST_Server::CREATABLE,
						'callback'            => array( $this, 'create_item' ),
						'permission_callback' => array( $this, 'check_permissions' ),
					),

				)
			);

			register_rest_route(
				$this->namespace,
				'/' . $this->rest_base . '/(?P<id>[\d]+)',
				array(
					'args' => array(
						'id' => array(
							'description' => __( 'Unique identifier for the object.', 'goodmeet' ),
							'type'        => 'integer',
						),
					),
				//phpcs:ignore
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_item' ),
					'permission_callback' => array( $this, 'check_permissions' ),
					'args'                => array(
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
					),
				),
				//phpcs:ignore
				array(
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => array( $this, 'update_item' ),
					'permission_callback' => array( $this, 'check_permissions' ),
				),
				//phpcs:ignore
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_item' ),
					'permission_callback' => array( $this, 'check_permissions' ),
				),
				)
			);

			register_rest_route(
				$this->namespace,
				'/' . $this->rest_base . '/delete-multiple',
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_multiple_items' ),
					'permission_callback' => array( $this, 'check_permissions' ),
				)
			);
		}

		/**
		 * Check if the current user has permission to edit posts.
		 *
		 * This function checks if the current user has the capability to edit posts.
		 * It returns true if the user has the 'edit_posts' capability, and false otherwise.
		 *
		 * @return bool True if the current user can edit posts, false otherwise.
		 */
		public function check_permissions() {
			return current_user_can( 'edit_posts' );
		}

		/**
		 * Verify the caller is allowed to act on a specific meeting post.
		 *
		 * Returns true for admins (manage_options) and meeting owners.
		 * Returns WP_Error 403 for everyone else, including the post being
		 * wrong type, which prevents IDOR against arbitrary post IDs.
		 *
		 * @param int $post_id Post ID supplied by the request.
		 * @return true|\WP_Error
		 */
		protected function check_meeting_ownership( $post_id ) {
			$post_id = (int) $post_id;
			$post    = get_post( $post_id );

			if ( ! $post || 'goodmeet_meeting' !== $post->post_type ) {
				return new WP_Error(
					'rest_forbidden',
					__( 'Sorry, you are not allowed to access this meeting.', 'goodmeet' ),
					array( 'status' => 403 )
				);
			}

			if ( current_user_can( 'manage_options' ) ) {
				return true;
			}

			if ( get_current_user_id() === (int) $post->post_author ) {
				return true;
			}

			return new WP_Error(
				'rest_forbidden',
				__( 'Sorry, you are not allowed to access this meeting.', 'goodmeet' ),
				array( 'status' => 403 )
			);
		}

		/**
		 * Retrieves all meetings.
		 *
		 * @param mixed $request Post request.
		 *
		 * @return mixed
		 */
		public function get_items( $request ) {

			$data     = array();
			$meetings = goodmeet_meetings();

			foreach ( $meetings as $meeting ) {
				$response = $this->prepare_item_for_response( $meeting, $request );
				$data[]   = $this->prepare_response_for_collection( $response );
			}

			$response = rest_ensure_response( $data );
			return $response;
		}

		/**
		 * Get the address, if the ID is valid.
		 *
		 * @param int $id Supplied ID.
		 *
		 * @return Object|\WP_Error
		 */
		protected function get_meeting( $id ) {
			$meeting = goodmeet_meeting( $id );

			if ( ! $meeting ) {
				return new WP_Error(
					'rest_contact_invalid_id',
					__( 'Invalid contact ID.', 'goodmeet' ),
					array( 'status' => 404 )
				);
			}

			return $meeting;
		}

		/**
		 * Retrieves one item from the collection.
		 *
		 * @param \WP_REST_Request $request Post request.
		 *
		 * @return \WP_Error|\WP_REST_Response
		 */
		public function get_item( $request ) {
			$allowed = $this->check_meeting_ownership( $request['id'] );
			if ( is_wp_error( $allowed ) ) {
				return $allowed;
			}

			$meeting  = goodmeet_meeting( $request['id'] );
			$response = $this->prepare_item_for_response( $meeting, $request );
			$response = rest_ensure_response( $response );

			return $response;
		}

		/**
		 * Creates one item from the collection.
		 *
		 * @param \WP_REST_Request $request Post request.
		 *
		 * @return \WP_Error|WP_REST_Response
		 */
		public function create_item( $request ) {
			$meeting = $this->prepare_item_for_database( $request );

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

			$meeting_settings              = maybe_unserialize( $meeting['settings'] );
			$serialized_with_zoom_password = null;

			$duration = ! empty( $meeting_settings['duration_hours'] ) || ! empty( $meeting_settings['duration_minutes'] ) ? goodmeet_convert_to_minutes( $meeting_settings['duration_hours'], $meeting_settings['duration_minutes'] ) : 40;

			$google_meet_data = array();

			if ( goodmeet_fs()->can_use_premium_code__premium_only() && function_exists( 'goodmeet_pro_prepare_google_meet_data' ) ) {
				$google_meet_data = goodmeet_pro_prepare_google_meet_data( $meeting['title'], $meeting_settings, $duration );
			} else {
				$google_meet_data = goodmeet_prepare_google_meet_data( $meeting['title'], $meeting_settings, $duration );
			}

			$google_meet_response = $this->google_meet_api->create_meeting( $google_meet_data );

			//phpcs:ignore
			if ( ! empty( $google_meet_response ) && isset( $google_meet_response->id, $google_meet_response->hangoutLink ) ) {
				$meeting_id = goodmeet_insert_meeting( $meeting['title'], $meeting['settings'], $meeting['meeting_description'] );
				update_post_meta( $meeting_id, 'goodmeet_google_meeting_id', $google_meet_response->id );
				//phpcs:ignore
				update_post_meta( $meeting_id, 'goodmeet_google_join_link', $google_meet_response->hangoutLink );
				update_post_meta( $meeting_id, 'goodmeet_google_meeting_duration', $duration );

				/**
				 * Fires after a meeting and its Google Calendar event are successfully created.
				 *
				 * Used by goodmeet-pro to reconcile co-hosts via Meet REST API.
				 *
				 * @param int    $meeting_id           WP post ID.
				 * @param object $google_meet_response Google API event response.
				 * @param array  $meeting_settings     Unserialized settings array.
				 */
				do_action( 'goodmeet_after_meeting_created', $meeting_id, $google_meet_response, $meeting_settings );
			}

			return $google_meet_response;
		}

		/**
		 * Updates one item from the collection.
		 *
		 * @param \WP_REST_Request $request Post request.
		 *
		 * @return \WP_Error|\WP_REST_Response
		 */
		public function update_item( $request ) {
			$meeting = $this->prepare_item_for_database( $request );

			if ( isset( $meeting['id'] ) ) {
				$allowed = $this->check_meeting_ownership( $meeting['id'] );
				if ( is_wp_error( $allowed ) ) {
					return $allowed;
				}
			}

			$meeting_settings              = maybe_unserialize( $meeting['settings'] );
			$serialized_with_zoom_password = null;

			$duration = ! empty( $meeting_settings['duration_hours'] ) || ! empty( $meeting_settings['duration_minutes'] ) ? goodmeet_convert_to_minutes( $meeting_settings['duration_hours'], $meeting_settings['duration_minutes'] ) : 40;

			$google_meeting_id = get_post_meta( $meeting['id'], 'goodmeet_google_meeting_id', true );

			$google_meet_response = $this->google_meet_api->update_meeting( $google_meeting_id, $meeting['title'], $meeting_settings );

			//phpcs:ignore
			if ( ! empty( $google_meet_response ) && isset( $google_meet_response->id, $google_meet_response->hangoutLink ) ) {
				$meeting_id = goodmeet_update_meeting( $meeting['title'], $meeting['settings'], $meeting['id'], $meeting['meeting_description'] );

				update_post_meta( $meeting['id'], 'goodmeet_google_meeting_id', $google_meet_response->id );
				//phpcs:ignore
				update_post_meta( $meeting['id'], 'goodmeet_google_join_link', $google_meet_response->hangoutLink );
				update_post_meta( $meeting['id'], 'goodmeet_google_meeting_duration', $duration );

				/**
				 * Fires after a meeting and its Google Calendar event are successfully updated.
				 *
				 * @param int    $meeting_id           WP post ID.
				 * @param object $google_meet_response Google API event response.
				 * @param array  $meeting_settings     Unserialized settings array.
				 */
				do_action( 'goodmeet_after_meeting_updated', (int) $meeting['id'], $google_meet_response, $meeting_settings );
			}

			return $google_meet_response;
		}

		/**
		 * Prepares one item for create or update operation.
		 *
		 * @param \WP_REST_Request $request Post request.
		 *
		 * @return \WP_Error|object
		 */
		protected function prepare_item_for_database( $request ) {
			$posted = json_decode( $request->get_body() );

			$prepared = array();
			$settings = array();

			if ( isset( $posted ) ) {
				foreach ( $posted as $key => $post ) {
					$settings[ $key ] = $post;
				}

				unset( $settings['title'] );
				unset( $settings['meeting_description'] );

				$settings['host'] = get_current_user_id();

				$settings = $this->sanitize_access_control_meta( $settings );

				if ( isset( $posted->id ) ) {
					$prepared['id'] = $posted->id;
				}

				$prepared['title']               = sanitize_text_field( $posted->title );
				$prepared['settings']            = maybe_serialize( $settings );
				$prepared['meeting_description'] = $posted->meeting_description;
			}

			return $prepared;
		}

		/**
		 * Sanitize per-meeting access control keys; strip when not Pro.
		 *
		 * @param array $settings Meeting settings array (mutable).
		 * @return array
		 */
		protected function sanitize_access_control_meta( $settings ) {
			// Drop rollback keys regardless of Pro state.
			unset( $settings['moderator_access'], $settings['moderator_roles_custom'] );

			$pro_active = goodmeet_fs()->can_use_premium_code__premium_only();

			if ( ! $pro_active ) {
				unset(
					$settings['meeting_visibility'],
					$settings['visibility_roles_custom'],
					$settings['cohost_mode'],
					$settings['cohost_users_custom'],
					$settings['cohost_emails_custom']
				);
				return $settings;
			}

			$allowed_visibility = array( 'inherit', 'public', 'logged_in', 'role_based' );
			$allowed_cohost     = array( 'inherit', 'custom_extra', 'custom_only' );
			$valid_roles        = array_keys( wp_roles()->get_names() );

			// Visibility.
			if ( isset( $settings['meeting_visibility'] ) ) {
				$settings['meeting_visibility'] = sanitize_text_field( $settings['meeting_visibility'] );
				if ( ! in_array( $settings['meeting_visibility'], $allowed_visibility, true ) ) {
					$settings['meeting_visibility'] = 'inherit';
				}
			} else {
				$settings['meeting_visibility'] = 'inherit';
			}

			if ( isset( $settings['visibility_roles_custom'] ) && is_array( $settings['visibility_roles_custom'] ) ) {
				$settings['visibility_roles_custom'] = array_values(
					array_intersect(
						array_map( 'sanitize_text_field', $settings['visibility_roles_custom'] ),
						$valid_roles
					)
				);
			} else {
				$settings['visibility_roles_custom'] = array();
			}

			// Co-host mode.
			if ( isset( $settings['cohost_mode'] ) ) {
				$settings['cohost_mode'] = sanitize_text_field( $settings['cohost_mode'] );
				if ( 'custom' === $settings['cohost_mode'] ) {
					// Migrate prior simplified 'custom' to 'custom_extra'.
					$settings['cohost_mode'] = 'custom_extra';
				}
				if ( ! in_array( $settings['cohost_mode'], $allowed_cohost, true ) ) {
					$settings['cohost_mode'] = 'inherit';
				}
			} else {
				$settings['cohost_mode'] = 'inherit';
			}

			// Co-host WP user IDs.
			if ( isset( $settings['cohost_users_custom'] ) && is_array( $settings['cohost_users_custom'] ) ) {
				$users = array();
				foreach ( $settings['cohost_users_custom'] as $uid ) {
					$uid = (int) $uid;
					if ( $uid > 0 && get_userdata( $uid ) ) {
						$users[] = $uid;
					}
				}
				$settings['cohost_users_custom'] = array_values( array_unique( $users ) );
			} else {
				$settings['cohost_users_custom'] = array();
			}

			// Co-host emails.
			if ( isset( $settings['cohost_emails_custom'] ) && is_array( $settings['cohost_emails_custom'] ) ) {
				$emails = array();
				foreach ( $settings['cohost_emails_custom'] as $email ) {
					$email = sanitize_email( $email );
					if ( $email && is_email( $email ) ) {
						$emails[] = strtolower( $email );
					}
				}
				$settings['cohost_emails_custom'] = array_values( array_unique( $emails ) );
			} else {
				$settings['cohost_emails_custom'] = array();
			}

			return $settings;
		}

		/**
		 * Prepares the item for the REST response.
		 *
		 * @param mixed            $item    WordPress representation of the item.
		 * @param \WP_REST_Request $request Request object.
		 *
		 * @return \WP_Error|WP_REST_Response
		 */
		public function prepare_item_for_response( $item, $request ) {
			$data   = array();
			$fields = $this->get_fields_for_response( $request );

			if ( isset( $item['id'] ) && in_array( 'id', $fields, true ) ) {
				$data['id'] = (int) $item['id'];
			}

			if ( isset( $item['title'] ) && in_array( 'title', $fields, true ) ) {
				$data['title'] = $item['title'];
			}

			if ( isset( $item['settings'] ) && in_array( 'settings', $fields, true ) ) {
				$data['settings'] = $item['settings'];
			}

			if ( isset( $item['meeting_description'] ) && in_array( 'meeting_description', $fields, true ) ) {
				$data['meeting_description'] = $item['meeting_description'];
			}

			if ( isset( $item['date'] ) && in_array( 'date', $fields, true ) ) {
				$data['date'] = mysql_to_rfc3339( $item['date'] );
			}

			$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
			$data    = $this->filter_response_by_context( $data, $context );

			$response = rest_ensure_response( $data );
			$response->add_links( $this->prepare_links( $item ) );

			return $response;
		}


		/**
		 * Deletes one item from the collection.
		 *
		 * @param \WP_REST_Request $request Request object.
		 *
		 * @return \WP_Error|WP_REST_Response
		 */
		public function delete_item( $request ) {
			$allowed = $this->check_meeting_ownership( $request['id'] );
			if ( is_wp_error( $allowed ) ) {
				return $allowed;
			}

			$serialized_settings = get_post_meta( $request['id'], 'goodmeet__meeting_settings', true );
			$settings            = maybe_unserialize( $serialized_settings );

			$google_meeting_id      = get_post_meta( $request['id'], 'goodmeet_google_meeting_id', true );
			$google_meeting_deleted = $this->google_meet_api->delete_meeting( $google_meeting_id );

			$deleted = goodmeet_delete_meeting( $request['id'] );

			if ( ! $deleted ) {
				return new WP_Error(
					'rest_not_deleted',
					__( 'Sorry, the address could not be deleted.', 'goodmeet' ),
					array( 'status' => 400 )
				);
			}

			$data = array(
				'deleted' => true,
			);

			$response = rest_ensure_response( $data );

			return $data;
		}

		/**
		 * Deletes multiple items from the collection.
		 *
		 * @param \WP_REST_Request $request Request object.
		 *
		 * @return \WP_Error|WP_REST_Response
		 */
		public function delete_multiple_items( $request ) {
			$ids = $request->get_param( 'ids' );

			if ( empty( $ids ) || ! is_array( $ids ) ) {
				return new \WP_Error(
					'rest_invalid_param',
					__( 'Invalid or missing parameter: ids.', 'goodmeet' ),
					array( 'status' => 400 )
				);
			}

			$response_data = array();

			foreach ( $ids as $id ) {
				$allowed = $this->check_meeting_ownership( $id );
				if ( is_wp_error( $allowed ) ) {
					$response_data[] = array(
						'id'     => $id,
						'status' => 'error',
					);
					continue;
				}

				$serialized_settings = get_post_meta( $id, 'goodmeet__meeting_settings', true );
				$settings            = maybe_unserialize( $serialized_settings );

				$google_meeting_id      = get_post_meta( $id, 'goodmeet_google_meeting_id', true );
				$google_meeting_deleted = $this->google_meet_api->delete_meeting( $google_meeting_id );

				$deleted = goodmeet_delete_meeting( $id );

				if ( is_wp_error( $deleted ) ) {
					$response_data[] = array(
						'id'     => $id,
						'status' => 'error',
					);
				} else {
					$response_data[] = array(
						'id'     => $id,
						'status' => 'deleted',
					);
				}
			}

			return rest_ensure_response( $response_data );
		}


		/**
		 * Prepares links for the request.
		 *
		 * @param mixed $item Get Post.
		 *
		 * @return array Links for the given post.
		 */
		protected function prepare_links( $item ) {
			$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );

			$links = array();

			if ( isset( $item['id'] ) ) {
				$links['self'] = array(
					'href' => rest_url( trailingslashit( $base ) . $item['id'] ),
				);
			}

			$links['collection'] = array(
				'href' => rest_url( $base ),
			);

			return $links;
		}


		/**
		 * Retrieves the contact schema, conforming to JSON Schema.
		 *
		 * @return array
		 */
		public function get_item_schema() {
			if ( $this->schema ) {
				return $this->add_additional_fields_schema( $this->schema );
			}

			$schema = array(
				'$schema'    => 'http://json-schema.org/draft-04/schema#',
				'title'      => 'contact',
				'type'       => 'object',
				'properties' => array(
					'id'                  => array(
						'description' => __( 'Unique identifier for the object.', 'goodmeet' ),
						'type'        => 'integer',
						'context'     => array( 'view', 'edit' ),
						'readonly'    => true,
					),
					'title'               => array(
						'description' => __( 'Name of the meeting.', 'goodmeet' ),
						'type'        => 'string',
						'context'     => array( 'view', 'edit' ),
						'required'    => true,
						'arg_options' => array(
							'sanitize_callback' => 'sanitize_text_field',
						),
					),
					'settings'            => array(
						'description' => __( 'Meeting setting', 'goodmeet' ),
						'type'        => 'string',
						'context'     => array( 'view', 'edit' ),
						'required'    => true,
					),
					'meeting_description' => array(
						'description' => __( 'Meeting setting', 'goodmeet' ),
						'type'        => 'string',
						'context'     => array( 'view', 'edit' ),
						'required'    => true,
					),
					'date'                => array(
						'description' => __( "The date the object was published, in the site's timezone.", 'goodmeet' ),
						'type'        => 'string',
						'format'      => 'date-time',
						'context'     => array( 'view' ),
						'readonly'    => true,
					),
				),
			);

			$this->schema = $schema;

			return $this->add_additional_fields_schema( $this->schema );
		}
	}
}
