<?php
namespace EM_Pro\API;

class REST {

	const NAMESPACE = 'events-manager-pro/v1';

	public static function init() {
		add_filter( 'rest_authentication_errors', array( static::class, 'authenticate_application_password' ), 30 );
		add_action( 'rest_api_init', array( static::class, 'register_routes' ) );
	}

	public static function authenticate_application_password( $result ) {
		if ( !empty( $result ) || is_user_logged_in() || !static::is_pro_request() || !class_exists( '\EM_OAuth\OAuth_Authorization_Server' ) ) {
			return $result;
		}
		$app_password_result = \EM_OAuth\OAuth_Authorization_Server::authenticate_application_password();
		if ( $app_password_result === true ) {
			return $result;
		}
		if ( is_wp_error( $app_password_result ) ) {
			$data = (array) $app_password_result->get_error_data();
			if ( empty( $data['status'] ) ) {
				$data['status'] = 401;
				$app_password_result->add_data( $data );
			}
			return $app_password_result;
		}
		return $result;
	}

	public static function register_routes() {
		register_rest_route( static::NAMESPACE, '/coupons', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'list_coupons' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::collection_args(),
			),
			array(
				'methods' => 'POST',
				'callback' => array( static::class, 'create_coupon' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::coupon_args(),
			),
		) );
		register_rest_route( static::NAMESPACE, '/coupons/(?P<id>\d+)', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'get_coupon' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::id_args( __( 'Coupon ID.', 'em-pro' ) ),
			),
			array(
				'methods' => 'PATCH',
				'callback' => array( static::class, 'update_coupon' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => array_merge( static::id_args( __( 'Coupon ID.', 'em-pro' ) ), static::coupon_args() ),
			),
			array(
				'methods' => 'DELETE',
				'callback' => array( static::class, 'delete_coupon' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::id_args( __( 'Coupon ID.', 'em-pro' ) ),
			),
		) );
		register_rest_route( static::NAMESPACE, '/coupons/(?P<id>\d+)/validate', array(
			'methods' => 'GET',
			'callback' => array( static::class, 'validate_coupon' ),
			'permission_callback' => array( static::class, 'can_manage_bookings' ),
			'args' => array_merge( static::id_args( __( 'Coupon ID.', 'em-pro' ) ), array(
				'event_id' => array( 'type' => 'string', 'description' => __( 'Event ID or event ID with timeslot suffix.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
				'total' => array( 'type' => 'number', 'description' => __( 'Optional booking total used to quote the discount.', 'em-pro' ) ),
			) ),
		) );

		register_rest_route( static::NAMESPACE, '/events/(?P<id>[\d:]+)/coupons', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'list_event_coupons' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
			),
			array(
				'methods' => 'POST',
				'callback' => array( static::class, 'attach_event_coupon' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
			),
			array(
				'methods' => 'DELETE',
				'callback' => array( static::class, 'detach_event_coupon' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
			),
		) );
		register_rest_route( static::NAMESPACE, '/events/(?P<id>[\d:]+)/coupons/(?P<coupon_id>\d+)', array(
			'methods' => 'DELETE',
			'callback' => array( static::class, 'detach_event_coupon' ),
			'permission_callback' => array( static::class, 'can_manage_bookings' ),
		) );

		register_rest_route( static::NAMESPACE, '/bookings/transactions', array(
			'methods' => 'GET',
			'callback' => array( static::class, 'list_transactions' ),
			'permission_callback' => array( static::class, 'can_manage_bookings' ),
			'args' => array_merge( static::collection_args(), static::transaction_args() ),
		) );
		register_rest_route( static::NAMESPACE, '/bookings/transactions/(?P<id>\d+)', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'get_transaction' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::id_args( __( 'Transaction ID.', 'em-pro' ) ),
			),
			array(
				'methods' => 'DELETE',
				'callback' => array( static::class, 'delete_transaction' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::id_args( __( 'Transaction ID.', 'em-pro' ) ),
			),
		) );
		register_rest_route( static::NAMESPACE, '/bookings/(?P<id>\d+)/transactions', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'list_booking_transactions' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => array_merge( static::id_args( __( 'Booking ID.', 'em-pro' ) ), static::collection_args(), static::transaction_args() ),
			),
			array(
				'methods' => 'POST',
				'callback' => array( static::class, 'record_booking_payment' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => array_merge( static::id_args( __( 'Booking ID.', 'em-pro' ) ), static::payment_args() ),
			),
		) );

		register_rest_route( static::NAMESPACE, '/bookings/attendance/(?P<id>[\w-]+)', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'get_attendance' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => static::id_args( __( 'Ticket booking ID or UUID.', 'em-pro' ), 'string' ),
			),
			array(
				'methods' => 'POST',
				'callback' => array( static::class, 'set_attendance' ),
				'permission_callback' => array( static::class, 'can_manage_bookings' ),
				'args' => array_merge( static::id_args( __( 'Ticket booking ID or UUID.', 'em-pro' ), 'string' ), array(
					'action' => array(
						'type' => 'string',
						'enum' => array( 'checkin', 'checkout' ),
						'required' => true,
						'description' => __( 'Attendance action to perform.', 'em-pro' ),
						'sanitize_callback' => 'sanitize_key',
					),
				) ),
			),
		) );

		register_rest_route( static::NAMESPACE, '/bookings/rsvp/(?P<id>[\w-]+)', array(
			array(
				'methods' => 'GET',
				'callback' => array( static::class, 'get_rsvp' ),
				'permission_callback' => array( static::class, 'is_logged_in' ),
				'args' => static::id_args( __( 'Booking ID or UUID.', 'em-pro' ), 'string' ),
			),
			array(
				'methods' => 'POST',
				'callback' => array( static::class, 'set_rsvp' ),
				'permission_callback' => array( static::class, 'is_logged_in' ),
				'args' => array_merge( static::id_args( __( 'Booking ID or UUID.', 'em-pro' ), 'string' ), array(
					'status' => array(
						'type' => array( 'integer', 'null' ),
						'enum' => array( 0, 1, 2, null ),
						'required' => false,
						'description' => __( 'RSVP status: 1 confirmed, 0 declined, 2 maybe, or null for no response.', 'em-pro' ),
					),
				) ),
			),
		) );
	}

	public static function list_coupons( $request ) {
		return static::respond( Service::list_coupons( Utils::get_request_data( $request ) ) );
	}

	public static function get_coupon( $request ) {
		return static::respond( Service::get_coupon( $request['id'] ) );
	}

	public static function create_coupon( $request ) {
		return static::respond( Service::create_coupon( Utils::get_request_data( $request ) ), 201 );
	}

	public static function update_coupon( $request ) {
		return static::respond( Service::update_coupon( $request['id'], Utils::get_request_data( $request ) ) );
	}

	public static function delete_coupon( $request ) {
		return static::respond( Service::delete_coupon( $request['id'] ) );
	}

	public static function validate_coupon( $request ) {
		return static::respond( Service::validate_coupon( $request['id'], Utils::get_request_data( $request ) ) );
	}

	public static function list_event_coupons( $request ) {
		return static::respond( Service::list_event_coupons( $request['id'], Utils::get_request_data( $request ) ) );
	}

	public static function attach_event_coupon( $request ) {
		$data = Utils::get_request_data( $request );
		return static::respond( Service::attach_event_coupon( $request['id'], $data['coupon_id'] ?? $data['id'] ?? 0 ) );
	}

	public static function detach_event_coupon( $request ) {
		$data = Utils::get_request_data( $request );
		return static::respond( Service::detach_event_coupon( $request['id'], $request['coupon_id'] ?? $data['coupon_id'] ?? 0 ) );
	}

	public static function list_transactions( $request ) {
		return static::respond( Service::list_transactions( Utils::get_request_data( $request ) ) );
	}

	public static function list_booking_transactions( $request ) {
		$data = Utils::get_request_data( $request );
		$data['booking_id'] = $request['id'];
		return static::respond( Service::list_transactions( $data ) );
	}

	public static function get_transaction( $request ) {
		return static::respond( Service::get_transaction( $request['id'] ) );
	}

	public static function delete_transaction( $request ) {
		return static::respond( Service::delete_transaction( $request['id'] ) );
	}

	public static function get_attendance( $request ) {
		return static::respond( Service::get_attendance( $request['id'] ) );
	}

	public static function set_attendance( $request ) {
		$data = Utils::get_request_data( $request );
		return static::respond( Service::set_attendance( $request['id'], $data['action'] ?? '' ) );
	}

	public static function get_rsvp( $request ) {
		return static::respond( Service::get_rsvp( $request['id'] ) );
	}

	public static function set_rsvp( $request ) {
		$data = Utils::get_request_data( $request );
		if ( !array_key_exists( 'status', $data ) ) {
			return Utils::error( 'emp_api_rsvp_status_required', __( 'RSVP status is required. Use null to clear a response.', 'em-pro' ), 400 );
		}
		return static::respond( Service::set_rsvp( $request['id'], $data['status'] ) );
	}

	public static function record_booking_payment( $request ) {
		return static::respond( Service::record_manual_payment( $request['id'], Utils::get_request_data( $request ) ), 201 );
	}

	public static function respond( $result, $status = 200 ) {
		if ( is_wp_error( $result ) ) return $result;
		$response = new \WP_REST_Response( $result, $status );
		if ( isset( $result['pagination'] ) && is_array( $result['pagination'] ) ) {
			$response->header( 'X-WP-Total', $result['pagination']['total'] );
			$response->header( 'X-WP-TotalPages', $result['pagination']['total_pages'] );
		}
		return $response;
	}

	public static function can_manage_bookings() {
		return Utils::can_manage_bookings();
	}

	public static function is_logged_in() {
		return is_user_logged_in();
	}

	protected static function collection_args() {
		return array(
			'page' => array(
				'type' => 'integer',
				'default' => 1,
				'minimum' => 1,
				'description' => __( 'Page number.', 'em-pro' ),
				'sanitize_callback' => 'absint',
			),
			'per_page' => array(
				'type' => 'integer',
				'default' => 20,
				'minimum' => 1,
				'maximum' => 100,
				'description' => __( 'Number of items to return per page.', 'em-pro' ),
				'sanitize_callback' => 'absint',
			),
			'search' => array(
				'type' => 'string',
				'description' => __( 'Search term.', 'em-pro' ),
				'sanitize_callback' => 'sanitize_text_field',
			),
		);
	}

	protected static function id_args( $description, $type = 'integer' ) {
		return array(
			'id' => array(
				'type' => $type,
				'required' => true,
				'description' => $description,
				'sanitize_callback' => $type === 'integer' ? 'absint' : 'sanitize_text_field',
			),
		);
	}

	protected static function coupon_args() {
		return array(
			'code' => array( 'type' => 'string', 'description' => __( 'Coupon code.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
			'name' => array( 'type' => 'string', 'description' => __( 'Coupon name.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
			'description' => array( 'type' => 'string', 'description' => __( 'Coupon description.', 'em-pro' ), 'sanitize_callback' => 'wp_kses_post' ),
			'start' => array( 'type' => 'string', 'description' => __( 'Coupon start date.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
			'end' => array( 'type' => 'string', 'description' => __( 'Coupon end date.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
			'max' => array( 'type' => 'integer', 'description' => __( 'Maximum number of uses.', 'em-pro' ), 'sanitize_callback' => 'absint' ),
			'type' => array( 'type' => 'string', 'enum' => array( '%', '#' ), 'description' => __( 'Discount type.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
			'tax' => array( 'type' => 'string', 'enum' => array( 'pre', 'post' ), 'description' => __( 'Whether discount applies before or after tax.', 'em-pro' ), 'sanitize_callback' => 'sanitize_key' ),
			'discount' => array( 'type' => 'number', 'description' => __( 'Discount amount.', 'em-pro' ) ),
			'eventwide' => array( 'type' => 'boolean', 'description' => __( 'Whether this coupon applies to all events owned by the coupon owner.', 'em-pro' ) ),
			'sitewide' => array( 'type' => 'boolean', 'description' => __( 'Whether this coupon applies to all site events.', 'em-pro' ) ),
			'private' => array( 'type' => 'boolean', 'description' => __( 'Whether this coupon is restricted to logged-in users.', 'em-pro' ) ),
		);
	}

	protected static function transaction_args() {
		return array(
			'gateway' => array( 'type' => 'string', 'description' => __( 'Gateway slug filter.', 'em-pro' ), 'sanitize_callback' => 'sanitize_key' ),
			'booking_id' => array( 'type' => 'integer', 'description' => __( 'Booking ID filter.', 'em-pro' ), 'sanitize_callback' => 'absint' ),
			'status' => array( 'type' => 'string', 'description' => __( 'Transaction status filter.', 'em-pro' ), 'sanitize_callback' => 'sanitize_text_field' ),
			'orderby' => array( 'type' => 'string', 'enum' => array( 'transaction_id', 'transaction_timestamp', 'transaction_total_amount', 'transaction_gateway', 'transaction_status' ), 'description' => __( 'Sort field.', 'em-pro' ), 'sanitize_callback' => 'sanitize_key' ),
			'order' => array( 'type' => 'string', 'enum' => array( 'ASC', 'DESC' ), 'default' => 'DESC', 'description' => __( 'Sort direction.', 'em-pro' ), 'sanitize_callback' => 'sanitize_key' ),
		);
	}

	protected static function payment_args() {
		return array(
			'amount' => array(
				'type' => 'number',
				'required' => true,
				'description' => __( 'Payment amount received, in the site currency. Use a negative number to credit.', 'em-pro' ),
			),
			'type' => array(
				'type' => 'string',
				'default' => 'cash',
				'description' => __( 'Payment method, e.g. cash, card, cheque, transfer.', 'em-pro' ),
				'sanitize_callback' => 'sanitize_text_field',
			),
			'note' => array(
				'type' => 'string',
				'description' => __( 'Optional note stored against the transaction.', 'em-pro' ),
				'sanitize_callback' => 'sanitize_text_field',
			),
			'approve' => array(
				'type' => 'boolean',
				'default' => true,
				'description' => __( 'Approve the booking automatically once it is fully paid.', 'em-pro' ),
			),
		);
	}

	protected static function is_pro_request() {
		$namespace = trim( static::NAMESPACE, '/' );
		$rest_route = isset( $_GET['rest_route'] ) ? trim( (string) wp_unslash( $_GET['rest_route'] ), '/' ) : '';
		if ( $rest_route === $namespace || strpos( $rest_route, $namespace . '/' ) === 0 ) {
			return true;
		}
		$path = isset( $_SERVER['REQUEST_URI'] ) ? (string) wp_parse_url( wp_unslash( $_SERVER['REQUEST_URI'] ), PHP_URL_PATH ) : '';
		if ( !$path ) {
			return false;
		}
		$needle = '/' . trim( rest_get_url_prefix(), '/' ) . '/' . $namespace;
		return strpos( $path, $needle ) !== false;
	}
}
