<?php
/**
 * BlocksCheckoutAddressValidationExtension class.
 *
 * Extends the WooCommerce Store API to add address validation to the checkout block.
 *
 * @package Automattic/WCShipping
 */

namespace Automattic\WCShipping\StoreApi\Extensions;

use Automattic\WCShipping\Checkout\CheckoutService;
use Automattic\WCShipping\Shipment\Address;
use Automattic\WCShipping\StoreApi\AbstractStoreApiExtension;
use Automattic\WCShipping\Utilities\AddressUtils;
use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
use Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema;
use WC_Customer;

defined( 'ABSPATH' ) || exit;

/**
 * Class BlocksCheckoutAddressValidationExtension
 */
class BlocksCheckoutAddressValidationExtension extends AbstractStoreApiExtension {
	/**
	 * The checkout service.
	 *
	 * @var CheckoutService
	 */
	private CheckoutService $checkout_service;

	/**
	 * BlocksCheckoutAddressValidationExtension constructor.
	 *
	 * @param ExtendSchema    $extend_schema    The ExtendSchema instance.
	 * @param CheckoutService $checkout_service The checkout service.
	 */
	public function __construct( ExtendSchema $extend_schema, CheckoutService $checkout_service ) {
		parent::__construct( $extend_schema );

		$this->checkout_service = $checkout_service;
	}

	/**
	 * Get the endpoint to extend.
	 *
	 * Should return one of the keys from the $endpoints array.
	 *
	 * @return string
	 */
	public function get_endpoint(): string {
		return self::$endpoints['cart'];
	}

	/**
	 * The data callback method.
	 *
	 * This is where you can define the data this endpoint should return.
	 *
	 * @return array
	 */
	public function data_callback(): array {
		$data = array(
			'notices' => array(),
		);

		// If checkout address validation is disabled, return early.
		if ( ! CheckoutService::is_address_validation_enabled() ) {
			return $data;
		}

		// Validate the shipping address.
		$response = $this->checkout_service->validate_shipping_address();
		if ( ! $response['success'] || empty( $response['notices'] ) ) {
			return $data;
		}

		// Get the HTML formatter.
		$html_formatter = self::$extend_schema->get_formatter( 'html' );

		// Format the notices.
		foreach ( $response['notices'] as $notice ) {
			$notice_message = $notice->get_message();
			$notice_data    = $notice->get_data();

			$notice->set_message( $html_formatter->format( $notice_message ) );

			if ( ! empty( $notice_data ) ) {
				$notice->set_data( $html_formatter->format( $notice_data ) );
			}

			$data['notices'][] = $notice->to_array();
		}

		return $data;
	}

	/**
	 * The schema callback method.
	 *
	 * This is where you can define the schema for the endpoint.
	 *
	 * @return array
	 */
	public function schema_callback(): array {
		return array(
			'notices' => array(
				'description' => __( 'WC Shipping checkout address validation notices', 'woocommerce-shipping' ),
				'type'        => array( 'array' ),
				'context'     => array( 'view' ),
				'readonly'    => true,
			),
		);
	}

	/**
	 * The update callback method.
	 *
	 * This is where you can listen for updates to the endpoint and handle accordingly.
	 *
	 * @param array $data Data to update.
	 *
	 * @return void
	 */
	public function update_callback( array $data ): void {
		if ( ! isset( $data['action'] ) || 'apply_suggested_shipping_address' !== $data['action'] ) {
			return;
		}

		// Get the suggested address.
		$suggested_address = ! empty( $data['suggested_address'] )
			? json_decode( $data['suggested_address'], true )
			: false;

		// Return if no suggested address.
		if ( empty( $suggested_address ) || ! is_array( $suggested_address ) ) {
			throw new RouteException( 'error_applying_suggested_address', 'error' );
		}

		// Get the cart customer.
		$customer = CheckoutService::get_cart_customer_instance();
		if ( ! $customer instanceof WC_Customer ) {
			throw new RouteException( 'error_applying_suggested_address', 'error' );
		}

		$address = new Address( $suggested_address );

		// Validate the address.
		$validate_address = $address->validate();
		if ( is_wp_error( $validate_address ) ) {
			throw new RouteException( 'error_applying_suggested_address', 'error' );
		}

		// Sanitize the address.
		$address->sanitize();

		$address_1 = $address->address_1;
		$address_2 = $address->address_2;
		$city      = $address->city;
		$state     = $address->state_code;
		$postcode  = $address->postcode;
		$country   = $address->country_code;

		// Set the cart customer shipping address.
		$customer->set_shipping_address_1( $address_1 );
		$customer->set_shipping_address_2( $address_2 );
		$customer->set_shipping_city( $city );
		$customer->set_shipping_state( $state );
		$customer->set_shipping_postcode( $postcode );
		$customer->set_shipping_country( $country );

		// Maybe set the billing address.
		if ( ! empty( $data['use_shipping_as_billing'] ) ) {
			$customer->set_billing_address_1( $address_1 );
			$customer->set_billing_address_2( $address_2 );
			$customer->set_billing_city( $city );
			$customer->set_billing_state( $state );
			$customer->set_billing_postcode( $postcode );
			$customer->set_billing_country( $country );
		}

		// Save the cart customer.
		$customer->save();
	}

	/**
	 * Get the schema type to extend the endpoint with.
	 *
	 * Should return one of the keys from the $schema_types array.
	 *
	 * @return string
	 */
	public function get_schema_type(): string {
		return self::$schema_types['array_a'];
	}
}