<?php
/**
 * Class AddressRESTController
 *
 * @package Automattic\WCShipping
 */

namespace Automattic\WCShipping\LabelPurchase;

use Automattic\WCShipping\Exceptions\RESTRequestException;
use Automattic\WCShipping\OriginAddresses\OriginAddressService;
use Automattic\WCShipping\WCShippingRESTController;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;

/**
 * REST controller for origin and destination address verification.
 */
class AddressRESTController extends WCShippingRESTController {

	/**
	 * API endpoint path.
	 *
	 * @var string
	 */
	protected $rest_base = 'address';

	/**
	 * Address normalization service.
	 *
	 * @var AddressNormalizationService
	 */
	private $normalization_service;

	/**
	 * Origin address service.
	 *
	 * @var OriginAddressService
	 */
	private $origin_address_service;


	/**
	 * REST controller constructor.
	 *
	 * @param AddressNormalizationService $normalization_service Service to manage address normalization.
	 */
	public function __construct(
		AddressNormalizationService $normalization_service,
		OriginAddressService $origin_address_service
	) {
		$this->normalization_service  = $normalization_service;
		$this->origin_address_service = $origin_address_service;
	}

	/**
	 * Register API routes.
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/update_origin',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'update_origin' ),
					'permission_callback' => array( $this, 'ensure_rest_permission' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<order_id>\d+)/update_destination',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'update_destination' ),
					'permission_callback' => array( $this, 'ensure_rest_permission' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<order_id>\d+)/verify_order',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'verify_order_shipping_address' ),
					'permission_callback' => array( $this, 'ensure_rest_permission' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/normalize',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'normalize_address' ),
					'permission_callback' => array( $this, 'ensure_rest_permission' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\w]+)', // IDs are generated using uniqid() which returns a 13 character long string
			array(
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete' ),
					'permission_callback' => array( $this, 'ensure_rest_permission' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/origins',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_origin_addresses' ),
					'permission_callback' => array( $this, 'ensure_rest_permission' ),
				),
			)
		);
	}

	/**
	 * Confirm and update origin address.
	 *
	 * @param WP_REST_Request $request REST request object.
	 * @return WP_REST_Response|WP_Error REST response or error.
	 */
	public function update_origin( WP_REST_Request $request ) {
		try {
			[ $origin ] = $this->get_and_check_body_params( $request, array( 'address', 'isVerified' ) );
		} catch ( RESTRequestException $error ) {
			return rest_ensure_response( $error->get_error_response() );
		}

		$clear_store_address_drift = (bool) $request->get_param( 'clearStoreAddressDrift' );

		/**
		 * Always set is_approved to true when updating an origin address.
		 * Note: is_verified indicates whether the verified address is being used.
		 */
		$origin = array_merge(
			$origin,
			array( 'is_approved' => true )
		);

		$response = $this->normalization_service->update_origin_address( $origin );

		if ( $clear_store_address_drift && ! is_wp_error( $response ) && ! empty( $response['success'] ) ) {
			$this->origin_address_service->mark_main_origin_store_address_in_sync();
		}

		return rest_ensure_response( $response );
	}

	/**
	 * Confirm and update destination address.
	 *
	 * @param WP_REST_Request $request REST request object.
	 * @return WP_REST_Response|WP_Error REST response or error.
	 */
	public function update_destination( WP_REST_Request $request ) {
		try {
			[ $destination, $is_verified ] = $this->get_and_check_body_params( $request, array( 'address', 'isVerified' ) );
			[ $order_id ]                  = $this->get_and_check_request_params( $request, array( 'order_id' ) );
		} catch ( RESTRequestException $error ) {
			return rest_ensure_response( $error->get_error_response() );
		}

		return rest_ensure_response( $this->normalization_service->update_destination_address( $order_id, $destination, $is_verified ) );
	}

	/**
	 * Verify if shipping destination is normalized,
	 * intended to be called on order summary details page.
	 *
	 * @param WP_REST_Request $request REST request object.
	 * @return WP_REST_Response|WP_Error REST response or error.
	 */
	public function verify_order_shipping_address( WP_REST_Request $request ) {
		try {
			[ $order_id ] = $this->get_and_check_request_params( $request, array( 'order_id' ) );
		} catch ( RESTRequestException $error ) {
			return rest_ensure_response( $error->get_error_response() );
		}

		return rest_ensure_response( $this->normalization_service->is_destination_address_verified( $order_id ) );
	}

	/**
	 * Submits address to normalization service and returns response.
	 *
	 * @param WP_REST_Request $request REST request object.
	 * @return WP_REST_Response|WP_Error REST response or error.
	 */
	public function normalize_address( WP_REST_Request $request ) {
		try {
			[ $address ] = $this->get_and_check_body_params( $request, array( 'address' ) );
		} catch ( RESTRequestException $error ) {
			return rest_ensure_response( $error->get_error_response() );
		}

		/*
		 * Optional — older clients didn't send it. Default to `destination`
		 * so the request still works for them. The connect server has
		 * accepted `destination` since forever and now also accepts
		 * `origin` per WOOSHIP-2230.
		 */
		$address_type = $request->get_param( 'addressType' );
		$address_type = 'origin' === $address_type ? 'origin' : 'destination';

		return rest_ensure_response(
			$this->normalization_service->get_normalization_response( $address, $address_type )
		);
	}


	/**
	 * Delete an origin address.
	 *
	 * @param  WP_REST_Request $request The request body contains the origin address to delete.
	 * @return WP_Error|WP_REST_Response
	 */
	public function delete( $request ) {
		try {
			[ $id ] = $this->get_and_check_request_params( $request, array( 'id' ) );
		} catch ( RESTRequestException $error ) {
			return rest_ensure_response( $error->get_error_response() );
		}

		$this->origin_address_service->delete_origin_address( wc_clean( $id ) );
		return rest_ensure_response(
			array(
				'success'    => true,
				'deleted_id' => $id,
			)
		);
	}

	/**
	 * Get all origin addresses.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 */
	public function get_origin_addresses() {
		return rest_ensure_response( $this->origin_address_service->get_origin_addresses() );
	}
}