<?php
/**
 * Class SettingsProcessor
 *
 * @package WPDesk\FS\TableRate\ShippingMethod
 */

namespace WPDesk\FS\TableRate\ShippingMethod;

use Exception;
use WPDesk\FS\TableRate\Rule\Condition\ConditionsFactory;
use WPDesk\FS\TableRate\Rule\Settings\Validator\Conditions;
use WPDesk\FS\TableRate\Rule;
use WPDesk\FS\TableRate\Rule\Cost\RuleAdditionalCostFactory;
use WPDesk\FS\TableRate\Rule\Cost\RuleCostFieldsFactory;
use WPDesk_Flexible_Shipping;

/**
 * Can process Flexible Shipping Method settings.
 */
class SettingsProcessor {

	/**
	 * @var string
	 */
	private $id;

	/**
	 * @var string
	 */
	private $instance_id;

	/**
	 * @var string
	 */
	private $shipping_methods_option;

	/**
	 * @var string
	 */
	private $shipping_method_order_option;

	/**
	 * Save_Rules constructor.
	 *
	 * @param string $id .
	 * @param string $instance_id .
	 * @param array  $shipping_methods_option .
	 * @param array  $shipping_method_order_option .
	 */
	public function __construct( $id, $instance_id, $shipping_methods_option, $shipping_method_order_option ) {
		$this->id                           = $id;
		$this->instance_id                  = $instance_id;
		$this->shipping_methods_option      = $shipping_methods_option;
		$this->shipping_method_order_option = $shipping_method_order_option;
	}

	/**
	 * @param string $action .
	 * @param array  $posted_data .
	 *
	 * @return array
	 *
	 * @throws Exception .
	 */
	public function process_and_save_settings( $action, $posted_data ) {
		$shipping_method_settings = $this->save_settings( $action, $posted_data );
		$this->add_method_creation_date();

		return $shipping_method_settings;
	}

	/**
	 * @param string $action           .
	 * @param array  $shipping_methods .
	 * @param array  $posted_data      .
	 *
	 * @return string
	 */
	private function get_method_id( $action, $shipping_methods, $posted_data ) {
		if ( 'new' === $action ) {
			$method_id = get_option( 'flexible_shipping_method_id', 0 );

			foreach ( $shipping_methods as $shipping_method ) {
				if ( intval( $shipping_method['id'] ) > $method_id ) {
					$method_id = intval( $shipping_method['id'] );
				}
			}
			++$method_id;

			return $method_id;
		}

		return sanitize_text_field( wp_unslash( $posted_data['method_id'] ) );
	}

	/**
	 * @param string $action .
	 * @param array  $posted_data .
	 *
	 * @return array
	 */
	private function save_settings( $action, $posted_data ) {
		$shipping_method  = [];
		$shipping_methods = get_option( $this->shipping_methods_option, [] );

		$method_id = $this->get_method_id( $action, $shipping_methods, $posted_data );

		if ( 'new' === $action ) {
			$method_id_for_shipping = $this->id . '_' . $this->instance_id . '_' . $method_id;
		} else {
			$method_id_for_shipping = sanitize_text_field( wp_unslash( $posted_data['method_id_for_shipping'] ) );
			$shipping_method        = isset( $shipping_methods[ $method_id ] ) ? $shipping_methods[ $method_id ] : [];
		}

		$shipping_method = $this->update_shipping_methods( $method_id, $method_id_for_shipping, $shipping_method, $shipping_methods, $posted_data );
		$this->update_rates( $shipping_methods );

		if ( 'new' === $action ) {
			$shipping_method_order = get_option( $this->shipping_method_order_option, [] );

			$shipping_method_order[ $method_id ] = $method_id;

			update_option( $this->shipping_method_order_option, $shipping_method_order );
			update_option( 'flexible_shipping_method_id', $method_id );
		}

		return $shipping_method;
	}

	/**
	 * @param string $method_id .
	 * @param string $method_id_for_shipping .
	 * @param array  $shipping_method .
	 * @param array  $shipping_methods .
	 * @param array  $posted_data .
	 *
	 * @return array
	 */
	private function update_shipping_methods( $method_id, $method_id_for_shipping, $shipping_method, $shipping_methods, $posted_data ) {
		// Prepare fields for settings.
		$shipping_method_settings = $this->prepare_shipping_method_settings( $method_id, $method_id_for_shipping, $posted_data );

		// Add shipping method to list.
		$shipping_methods[ $method_id ] = array_merge( $shipping_method, $shipping_method_settings );

		// Save shipping methods.
		update_option( $this->shipping_methods_option, $shipping_methods );

		return $shipping_method_settings;
	}

	/**
	 * Add method creation date.
	 */
	private function add_method_creation_date() {
		if ( ! get_option( 'flexible_shipping_method_creation_date' ) ) {
			add_option( 'flexible_shipping_method_creation_date', current_time( 'mysql' ) );
		}
	}

	/**
	 * @param array $shipping_methods .
	 */
	private function update_rates( $shipping_methods ) {
		$rates = [];

		foreach ( $shipping_methods as $shipping_method ) {
			$id = sprintf( '%s_%s_%s', $this->id, $this->instance_id, sanitize_title( $shipping_method['method_title'] ) );

			$id = apply_filters( 'flexible_shipping_method_rate_id', $id, $shipping_method );

			if ( ! isset( $rates[ $id ] ) && 'yes' === $shipping_method['method_enabled'] ) {
				$rates[ $id ] = [
					'identifier' => $id,
					'title'      => $shipping_method['method_title'],
				];
			}
		}
		update_option( 'flexible_shipping_rates', $rates );
	}


	/**
	 * @param int   $method_id .
	 * @param int   $method_id_for_shipping .
	 * @param array $posted_data .
	 *
	 * @return array
	 */
	private function prepare_shipping_method_settings( $method_id, $method_id_for_shipping, $posted_data ) {

		$shipping_method = [
			'woocommerce_method_instance_id'     => $this->instance_id,
			'id'                                 => $method_id,
			'id_for_shipping'                    => $method_id_for_shipping,
			'method_title'                       => sanitize_text_field( $this->get_field_value( 'method_title', $posted_data ) ),
			'method_description'                 => sanitize_text_field( $this->get_field_value( 'method_description', $posted_data ) ),
			CommonMethodSettings::METHOD_LOGO_ID => absint( $this->get_field_value( CommonMethodSettings::METHOD_LOGO_ID, $posted_data ) ),
		];

		$shipping_method[ WPDesk_Flexible_Shipping::FIELD_METHOD_FREE_SHIPPING ] = '';

		$method_free_shipping = $this->get_field_value( 'method_free_shipping', $posted_data );
		if ( ! empty( $method_free_shipping ) ) {
			$shipping_method[ WPDesk_Flexible_Shipping::FIELD_METHOD_FREE_SHIPPING ] = wc_format_decimal( sanitize_text_field( $method_free_shipping ) );
		}

		$method_integration                    = $this->get_field_value( 'method_integration', $posted_data );
		$shipping_method['method_integration'] = sanitize_text_field( $method_integration );

		$shipping_method['method_free_shipping_label']                                    = sanitize_text_field( $this->get_field_value( 'method_free_shipping_label', $posted_data ) );
		$shipping_method['method_calculation_method']                                     = sanitize_text_field( $this->get_field_value( 'method_calculation_method', $posted_data ) );
		$shipping_method[ WPDesk_Flexible_Shipping::SETTING_METHOD_FREE_SHIPPING_NOTICE ] = $this->get_value_of_yes_no( WPDesk_Flexible_Shipping::SETTING_METHOD_FREE_SHIPPING_NOTICE, $posted_data );

		$shipping_method['method_default']    = $this->get_value_of_yes_no( 'method_default', $posted_data );
		$shipping_method['method_visibility'] = $this->get_value_of_yes_no( 'method_visibility', $posted_data );
		$shipping_method['method_debug_mode'] = $this->get_value_of_yes_no( 'method_debug_mode', $posted_data );
		$shipping_method['method_enabled']    = $this->get_value_of_yes_no( 'method_enabled', $posted_data );

		$shipping_method[ WPDesk_Flexible_Shipping::SETTING_METHOD_RULES ] = $this->get_normalized_rules(
			isset( $posted_data[ WPDesk_Flexible_Shipping::SETTING_METHOD_RULES ] ) ? $posted_data[ WPDesk_Flexible_Shipping::SETTING_METHOD_RULES ] : []
		);

		return apply_filters( 'flexible_shipping_process_admin_options', $shipping_method );
	}

	/**
	 * @param array $posted_rules_settings .
	 *
	 * @return array
	 */
	private function get_normalized_rules( $posted_rules_settings ) {
		$rules_settings_processor = new Rule\Settings\SettingsProcessor(
			$posted_rules_settings,
			( new ConditionsFactory() )->get_conditions(),
			( new RuleCostFieldsFactory() )->get_fields(),
			( new Rule\Cost\RuleAdditionalCostFieldsFactory( ( new RuleAdditionalCostFactory() )->get_additional_costs() ) )->get_fields(),
			( new Rule\SpecialAction\SpecialActionFieldsFactory( ( new Rule\SpecialAction\SpecialActionFactory() )->get_special_actions() ) )->get_fields()
		);
		return $rules_settings_processor->prepare_settings();
	}

	/**
	 * @param string $name .
	 * @param array  $posted_data .
	 *
	 * @return mixed
	 */
	private function get_field_value( $name, $posted_data ) {
		$key = sprintf( 'woocommerce_%s_%s', $this->id, $name );

		return isset( $posted_data[ $key ] ) ? wp_unslash( $posted_data[ $key ] ) : null;
	}

	/**
	 * @param string $name .
	 * @param array  $posted_data .
	 *
	 * @return string
	 */
	private function get_value_of_yes_no( $name, $posted_data ) {
		return filter_var( $this->get_field_value( $name, $posted_data ), FILTER_VALIDATE_BOOLEAN ) ? 'yes' : 'no';
	}
}
