<?php

namespace Automattic\WooCommerce\Blocks\Domain\Services;

use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;

/**
 * Service class managing checkout fields and its related extensibility points in the admin area.
 */
class CheckoutFieldsAdmin {

	/**
	 * Checkout field controller.
	 *
	 * @var CheckoutFields
	 */
	private $checkout_fields_controller;

	/**
	 * Sets up core fields.
	 *
	 * @param CheckoutFields $checkout_fields_controller Instance of the checkout field controller.
	 */
	public function __construct( CheckoutFields $checkout_fields_controller ) {
		$this->checkout_fields_controller = $checkout_fields_controller;
	}

	/**
	 * Initialize hooks. This is not run Store API requests.
	 */
	public function init() {
		add_filter( 'woocommerce_admin_billing_fields', array( $this, 'admin_address_fields' ), 10, 3 );
		add_filter( 'woocommerce_admin_billing_fields', array( $this, 'admin_contact_fields' ), 10, 3 );
		add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_address_fields' ), 10, 3 );
		add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_order_fields' ), 10, 3 );
	}

	/**
	 * Converts the shape of a checkout field to match whats needed in the WooCommerce meta boxes.
	 *
	 * @param array  $field The field to format.
	 * @param string $key The field key. This will be used for the ID of the field when passed to the meta box.
	 * @return array Formatted field.
	 */
	protected function format_field_for_meta_box( $field, $key ) {
		$formatted_field = array(
			'id'              => $key,
			'label'           => $field['label'],
			'value'           => $field['value'],
			'type'            => $field['type'],
			'update_callback' => array( $this, 'update_callback' ),
			'show'            => true,
			'wrapper_class'   => 'form-field-wide',
		);

		if ( 'select' === $field['type'] ) {
			$formatted_field['options'] = array_column( $field['options'], 'label', 'value' );
		}

		if ( 'checkbox' === $field['type'] ) {
			$formatted_field['checked_value']   = '1';
			$formatted_field['unchecked_value'] = '0';
		}

		return $formatted_field;
	}

	/**
	 * Updates a field value for an order.
	 *
	 * @param string    $key The field key.
	 * @param mixed     $value The field value.
	 * @param \WC_Order $order The order to update the field for.
	 */
	public function update_callback( $key, $value, $order ) {
		list( $group, $key ) = explode( '/', $key, 2 );
		$group               = CheckoutFields::get_group_name( $group );
		$this->checkout_fields_controller->persist_field_for_order( $key, $value, $order, $group, false );
	}

	/**
	 * Injects address fields in WC admin orders screen.
	 *
	 * @param array             $fields The fields to show.
	 * @param \WC_Order|boolean $order The order to show the fields for.
	 * @param string            $context The context to show the fields for.
	 * @return array
	 */
	public function admin_address_fields( $fields, $order = null, $context = 'edit' ) {
		if ( ! $order instanceof \WC_Order ) {
			return $fields;
		}

		$group_name        = doing_action( 'woocommerce_admin_billing_fields' ) ? 'billing' : 'shipping';
		$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'address', $group_name, $context );
		foreach ( $additional_fields as $key => $field ) {
			$prefixed_key              = CheckoutFields::get_group_key( $group_name ) . $key;
			$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
		}

		array_splice(
			$fields,
			array_search(
				'state',
				array_keys( $fields ),
				true
			) + 1,
			0,
			$additional_fields
		);

		return $fields;
	}

	/**
	 * Injects contact fields in WC admin orders screen.
	 *
	 * @param array             $fields The fields to show.
	 * @param \WC_Order|boolean $order The order to show the fields for.
	 * @param string            $context The context to show the fields for.
	 * @return array
	 */
	public function admin_contact_fields( $fields, $order = null, $context = 'edit' ) {
		if ( ! $order instanceof \WC_Order ) {
			return $fields;
		}

		$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'contact', 'other', $context );

		foreach ( $additional_fields as $key => $field ) {
			$prefixed_key              = CheckoutFields::get_group_key( 'other' ) . $key;
			$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
		}

		return array_merge( $fields, $additional_fields );
	}

	/**
	 * Injects additional fields in WC admin orders screen.
	 *
	 * @param array             $fields The fields to show.
	 * @param \WC_Order|boolean $order The order to show the fields for.
	 * @param string            $context The context to show the fields for.
	 * @return array
	 */
	public function admin_order_fields( $fields, $order = null, $context = 'edit' ) {
		if ( ! $order instanceof \WC_Order ) {
			return $fields;
		}

		$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'order', 'other', $context );

		foreach ( $additional_fields as $key => $field ) {
			$prefixed_key              = CheckoutFields::get_group_key( 'other' ) . $key;
			$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
		}

		return array_merge( $fields, $additional_fields );
	}
}
