<?php
/**
 * Checkout — address selector.
 *
 * @package Vanquish\MultipleAddresses\Frontend
 */

namespace Vanquish\MultipleAddresses\Frontend;

use Vanquish\MultipleAddresses\Com\Access;
use Vanquish\MultipleAddresses\Com\AddressForm;
use Vanquish\MultipleAddresses\Com\Addresses;
use Vanquish\MultipleAddresses\Com\Settings;
use Vanquish\MultipleAddresses\Com\Template;

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

/**
 * Puts a "choose one of your saved addresses" selector above the billing and
 * shipping forms at checkout, and lets the customer add, edit or delete an
 * address without leaving the page. Free core.
 *
 * Both selectors offer the same list — an address is not tied to a purpose — and
 * each preselects whichever address the customer marked as their default for
 * that form.
 *
 * Layout constraint that shapes this class: the selector sits INSIDE
 * WooCommerce's checkout form, so it may not contain a form of its own, and the
 * add/edit editor (which is all form fields) is rendered after the checkout form
 * instead — otherwise the markup would be invalid and the editor's fields would
 * be posted with the order.
 */
class CheckoutPage {

	/**
	 * Address book service.
	 *
	 * @var Addresses
	 */
	private $addresses;

	/**
	 * Settings service.
	 *
	 * @var Settings
	 */
	private $settings;

	/**
	 * Whether the shared editor markup has been printed.
	 *
	 * @var bool
	 */
	private $editor_rendered = false;

	/**
	 * Constructor.
	 *
	 * Note: AddressAjax is NOT instantiated here. It is registered once by
	 * Plugin::load_free(); constructing it again would register its wp_ajax_
	 * handlers a second time and run every save/delete twice. Its helpers are
	 * used statically instead.
	 */
	public function __construct() {
		$this->addresses = new Addresses();
		$this->settings  = new Settings();

		add_action( 'woocommerce_before_checkout_billing_form', array( $this, 'render_billing_selector' ) );
		add_action( 'woocommerce_before_checkout_shipping_form', array( $this, 'render_shipping_selector' ) );

		// The editor lives outside the checkout form (see the class docblock).
		add_action( 'woocommerce_after_checkout_form', array( $this, 'render_editor' ) );
	}

	/**
	 * Billing address selector.
	 *
	 * @return void
	 */
	public function render_billing_selector() {
		$this->render_selector( 'billing' );
	}

	/**
	 * Shipping address selector.
	 *
	 * @return void
	 */
	public function render_shipping_selector() {
		$this->render_selector( 'shipping' );
	}

	/**
	 * Render the selector for one checkout form.
	 *
	 * @param string $purpose 'billing' or 'shipping'.
	 * @return void
	 */
	private function render_selector( $purpose ) {
		if ( ! $this->is_active( $purpose ) ) {
			return;
		}

		$saved = $this->addresses->all( get_current_user_id() );

		if ( empty( $saved ) && ! $this->show_add_button() ) {
			return;
		}

		$labels = array();
		foreach ( $saved as $id => $address ) {
			$labels[ $id ] = AddressAjax::label( $address );
		}

		Template::render(
			'checkout/address-selector.php',
			array(
				'purpose'     => $purpose,
				'addresses'   => $saved,
				'labels'      => $labels,
				'selected'    => $this->preselected( $purpose ),
				'show_add'    => $this->show_add_button(),
				'show_manage' => $this->show_manage_buttons(),
			)
		);
	}

	/**
	 * Render the single shared editor, after the checkout form.
	 *
	 * One editor serves both selectors: addresses are not tied to a form, so a
	 * second copy would only be a second set of duplicate field ids.
	 *
	 * @return void
	 */
	public function render_editor() {
		if ( $this->editor_rendered ) {
			return;
		}

		if ( ! $this->is_active( 'billing' ) && ! $this->is_active( 'shipping' ) ) {
			return;
		}

		// Nothing can open it when there is neither an add nor an edit control.
		if ( ! $this->show_add_button() && ! $this->show_manage_buttons() ) {
			return;
		}

		$this->editor_rendered = true;

		Template::render(
			'checkout/address-editor.php',
			array(
				'fields' => AddressForm::fields(),
			)
		);
	}

	// -----------------------------------------------------------------------
	// Internals
	// -----------------------------------------------------------------------

	/**
	 * Whether the selector runs on one of the checkout forms.
	 *
	 * @param string $purpose 'billing' or 'shipping'.
	 * @return bool
	 */
	private function is_active( $purpose ) {
		if ( ! is_user_logged_in() || ! Access::can_use() ) {
			return false;
		}

		$key = 'shipping' === $purpose ? 'selector_on_shipping' : 'selector_on_billing';

		return $this->settings->is_enabled( $key, true );
	}

	/**
	 * Whether the "add a new address" affordance is offered.
	 *
	 * Answered by can_add() rather than can_manage(): a customer whose allowance
	 * is full may still edit and delete, so only the add button goes.
	 *
	 * @return bool
	 */
	private function show_add_button() {
		return $this->settings->is_enabled( 'checkout_allow_add', true ) && Access::can_add();
	}

	/**
	 * Whether the edit and delete controls are offered.
	 *
	 * A customer who may pick from the book but not change it still gets the
	 * dropdown — just nothing to edit it with.
	 *
	 * @return bool
	 */
	private function show_manage_buttons() {
		return $this->settings->is_enabled( 'checkout_allow_add', true ) && Access::can_manage();
	}

	/**
	 * Which address to preselect: the customer's default for this form, unless
	 * the merchant prefers to leave whatever WooCommerce already filled in.
	 *
	 * @param string $purpose 'billing' or 'shipping'.
	 * @return string Address id, or '' for none.
	 */
	private function preselected( $purpose ) {
		if ( ! $this->settings->is_enabled( 'preselect_default_address', false ) ) {
			return '';
		}

		$default = $this->addresses->get_default( get_current_user_id(), $purpose );

		return $default ? $default['id'] : '';
	}
}
