<?php
/**
 * EMC Offers simulator
 */

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

if ( ! class_exists( 'emc_settings_simulator' ) ) :

/**
 * emc_settings_simulator
 */
class emc_settings_simulator extends emc_admin_settings {

    private $offers = false;

  /**
   * Constructor.
   */
  public function __construct() {
    $this->id    = 'simulator';
    $this->label = __( 'Rates simulator', 'envoimoinscher' );
    add_filter( 'emc_settings_tabs_array', array( &$this, 'add_tab' ), 20 );
  }

	/**
	* Get fields array. We're not calling it get_settings to prevent create_options from creating empty options and not using default value.
	* @param $memory_limit limit ressources usage (for activation only)
	* @return array
	*/
	public function get_settings($memory_limit = false) {
		$products = array();
        $max_products = $memory_limit ? 10 : 1000;
        $args = array( 'post_type' => 'product', 'posts_per_page' => $max_products );
		foreach(get_posts( $args ) as $value) {
            envoimoinscher_model::log($value->ID, true);
            if (function_exists('wc_get_product')) {
                $product = wc_get_product( $value->ID );
            } else {
                // fix for WC < 2.5
                $product = WC()->product_factory->get_product( $value->ID );
            }
            $product_type = envoimoinscher_model::envoimoinscher_get_product_property($product, 'type');
			if( $product_type == 'simple' ) {
				$products[$value->ID] = $value->post_title;
			} elseif ( $product_type == 'variable' ) {
				foreach($product->get_available_variations() as $variation) {
					$title = $value->post_title;
					foreach($variation['attributes'] as $attributes){
						$title .= ' ' . $attributes;
					}
					$products[$variation['variation_id']] = $title;
				}
			}
            if (count($products) > $max_products) {
                break;
            }
		}

		if( empty($products) ) {
			$products[''] = __( 'No product available in your online shop', 'envoimoinscher' );
		}

		if(method_exists(WC()->countries, 'get_countries')) {
            $display_countries =  WC()->countries->get_countries();
        } else {
            $display_countries =  WC()->countries->countries;
        }

        $settings = array(
            0 => array(
                'section_id' => 'api_simulator_options',
                'section_title' => __( 'Boxtal: shipping cost estimation', 'envoimoinscher' ),
                'section_description' => __( 'The simulation page allows you to make a quotation according to the characteristics of your parcel.<br />Prices and offers are displayed as seen by the client.', 'envoimoinscher' ),
                'fields' => array(
                    0 => array(
                        'title' => __( 'Add a product *', 'envoimoinscher' ),
                        'id' => 'product_simulator',
                        'type' => 'product_select',
                        'default' => isset($_POST['product_simulator']) ? $_POST['product_simulator'] : '',
                        'options' => $products,
                        'required' => true,
                        'css' => '',
                    ),
                    1 => array(
                        'title' => __( 'Recipient address', 'envoimoinscher' ),
                        'id' => 'recipient_address',
                        'type' => 'text',
                        'default' => isset($_POST['recipient_address']) ? $_POST['recipient_address'] : '',
                        'required' => false,
                        'css' => 'min-width:350px;',
                        'donotsave' => true
                    ),
                    2 => array(
                        'title' => __( 'Recipient ZIP code *', 'envoimoinscher' ),
                        'id' => 'recipient_postcode',
                        'type' => 'text',
                        'default' => isset($_POST['recipient_postcode']) ? $_POST['recipient_postcode'] : '',
                        'required' => true,
                        'donotsave' => true
                    ),
                    3 => array(
                        'title' => __( 'Recipient city *', 'envoimoinscher' ),
                        'id' => 'recipient_city',
                        'type' => 'text',
                        'default' => isset($_POST['recipient_city']) ? $_POST['recipient_city'] : '',
                        'required' => true,
                        'donotsave' => true
                    ),
                    4 => array(
                        'title' => __( 'Recipient country *', 'envoimoinscher' ),
                        'id' => 'recipient_country',
                        'css' => 'min-width:350px;',
                        'type' => 'select',
                        'default' => isset($_POST['recipient_country']) ? $_POST['recipient_country'] : get_option('EMC_COUNTRY', 'FR'),
                        'required' => true,
                        'options' => $display_countries,
                        'donotsave' => true
                    ),
                )
            )
        );

        return $settings;
	}

    /**
	 * Output product_select field
	 */
	public function output_product_select_field($field) {
        ?>
            <tr valign="top">
                <th scope="row" class="titledesc">
                    <label for="<?php echo $field['id']; ?>"><?php echo $field['title']; ?></label>
                 </th>
                <td class="forminp forminp-text">
                    <select name="<?php echo $field['id']; ?>" id="<?php echo $field['id']; ?>">
                        <?php
                            foreach ($field['options'] as $option_id => $option) {
                                echo '<option value="'.$option_id.'" ';
                                if ($option_id == $field['default']) echo 'selected="selected"';
                                echo '>'.$option.'</option>';
                            }
                        ?>
                    </select>
                    <?php _e('Quantity:', 'envoimoinscher'); ?>
                    <input type="number" class="qty" name="qty" value="1" min="1" max="999" />
                    <button class="button" name="add-product-to-simulator"><?php _e('add product', 'envoimoinscher'); ?></button>
                </td>
            </tr>
            <tr valign="top">
                <th scope="row" class="titledesc">
                    <label><?php _e('Cart content', 'envoimoinscher'); ?></label>
                 </th>
                <td id="simulator_cart_content" class="forminp forminp-text">
                    <?php
                        foreach ($_POST as $key => $value) {
                            if (strpos($key, 'product-') !== false) {
                                $product_id = str_replace('product-', '', $key);
                                echo EMC()->add_product_line($product_id, $_POST['qty-'.$product_id]);
                            }
                        }
                        echo '<div class="cart_totals">';
                        _e('Cart weight:', 'envoimoinscher');
                        echo ' <span id="simulator_cart_weight">-</span> kg<br/>';
                        _e('Cart price:', 'envoimoinscher');
                        echo ' <span id="simulator_cart_price">-</span> €<br/>';
                        _e('Cart shipping classes:', 'envoimoinscher');
                        echo ' <span id="simulator_cart_shipping_classes"></span>';
                        echo '</div>';
                    ?>
                </td>
            </tr>
        <?php
    }

    /**
	 * Action buttons.
	 */
	public function action_buttons() {
       ?>
        <input name="additional_output" class="button-primary" type="submit" value="<?php _e( 'See Offers', 'envoimoinscher' ); ?>" />
       <?php
    }

   /**
	* Output query results
	*/
	public function validate_additional_output() {
        if( isset( $_POST ) && !empty( $_POST ) ) {
            if( $this->check_fields() ){
                $offers = $this->get_simulator_offers();
                if ( is_array( $offers ) ) {
                    $this->offers = $offers;
                    return true;
                } else {
                    // if no offers or curl error or request error
                    $notice = array(
                        'type' => "simple-notice",
                        'status' => 'failure',
                        'message' => $offers,
                        'placeholders' => array(),
                        'autodestruct' => 1
                    );
                    emc_admin_notices::add_custom_notice('simulator_check_fields', $notice);
                    return false;
                }
            }
        }
	}

	/**
	* Output query results
	*/
	public function additional_output() {
        $offers = $this->offers;
        include( dirname ( dirname( __FILE__ ) ) . '/views/html-admin-simulator.php');
	}

   /**
	* Check empty values from fields
	*/
	public function check_fields() {

         $sections = $this->get_settings();
        foreach ($sections as $section) {
            foreach ($section['fields'] as $field) {
                if( isset( $field['required'] ) && ( $field['required'] == true ) && ( null == $_POST[$field['id']] ) ) {
                    $notice = array(
                        'type' => "simple-notice",
                        'status' => 'failure',
                        'message' => 'Please fill in all the required * fields.',
                        'placeholders' => array(),
                        'autodestruct' => 1
                    );
                    emc_admin_notices::add_custom_notice('simulator_check_fields', $notice);
                    return false;
                }
            }
        }

        // check cart
        $products = array();
        foreach ($_POST as $key => $value) {
            if (strpos($key, 'product-') !== false) {
                $product_id = str_replace('product-', '', $key);
                array_push($products, array('product_id' => $product_id, 'qty' => $_POST['qty-'.$product_id]));
            }
        }
        if (empty($products)) {
            $notice = array(
                'type' => "simple-notice",
                'status' => 'failure',
                'message' => 'Please add products to cart in order to simulate rates.',
                'placeholders' => array(),
                'autodestruct' => 1
            );
            emc_admin_notices::add_custom_notice('simulator_check_fields', $notice);
            return false;
        }

		return true;
	}

	/**
	* Calculate Shipping
	*/
	public function get_simulator_offers() {
        /* check POST data to simulate cart */
        $products = array();
        foreach ($_POST as $key => $value) {
            if (strpos($key, 'product-') !== false) {
                $product_id = str_replace('product-', '', $key);
                array_push($products, array('product_id' => $product_id, 'qty' => $_POST['qty-'.$product_id]));
            }
        }
        $return = EMC()->get_simulator_cart_info($products);

		if ( !empty($return['package']['contents']) )  {

			$to = array(
				'country' => $_POST['recipient_country'],
				'zipcode' => $_POST['recipient_postcode'],
				'city' => $_POST['recipient_city'],
				'type' => 'individual',
				'address' => $_POST['recipient_address'],
				'firstname' => 'Hervé',
				'lastname' => 'Dupont',
				'email' => 'herve@dupont.com',
				'phone' => '0504030201',
				'infos' => 'vide',
			);

            $offers = envoimoinscher_model::split_package_get_quotation($to, $return['package'], false);

            // stop there if there is no results
            if (!is_array($offers)) {
                return __( 'There was a problem with the quotation, please activate the logs in Help tab and check them in WooCommerce > System status.', 'envoimoinscher' );
            }
            // stop there if one shipment has an error
            foreach ($offers as $offer) {
                if (!is_array($offer)) return sprintf(__( 'Boxtal API error returned: %s', 'envoimoinscher' ), $offer );
            }

            // get package zones (if zones exists)
            $package_zone = array();
            if (envoimoinscher_model::is_zones_enabled()) {
                // add missing info to package
                $return['package']['destination'] = array(
                    'country' => $to['country'],
                    'state' => '',
                    'postcode' => $to['zipcode'],
                );
                $package_zone = WC_Shipping_Zones::get_zone_matching_package($return['package']);
            }

            // get all active shipping methods
            $emc_services = envoimoinscher_model::get_enabled_shipping_methods(true);

            // check if all addresses have the carriers, and calculate live rate, else remove from quotation
            $live_rate = array();
            $service_label = array();
            $operator_label = array();
            $currency = array();
            $characteristics = array();
            $shipment_nb = array();
            foreach($emc_services as $key => $carrier_code) {
                $live_rate[$carrier_code] = 0;
                $shipment_nb[$carrier_code] = count($offers);
                foreach($offers as $suborder_key => $carriers) {
                    if (!isset($carriers[$carrier_code])) {
                        unset($emc_services[$key]);
                    } else {
                        $service_label[$carrier_code] = $carriers[$carrier_code]['service']['label'];
                        $operator_label[$carrier_code] = $carriers[$carrier_code]['operator']['label'];
                        $currency[$carrier_code] = $carriers[$carrier_code]['price']['currency'];
                        $characteristics[$carrier_code] = $carriers[$carrier_code]['characteristics'];
                        $live_rate[$carrier_code] += $carriers[$carrier_code]['price']['tax-exclusive'];
                        envoimoinscher_model::log('Simulation - price for carrier '.$carrier_code.' is '.$carriers[$carrier_code]['price']['tax-exclusive']);
                    }
                }
            }

            // now we can check pricing rules
            $display_offers = array();
            foreach($emc_services as $carrier_code) {

                $display_offer = array(
                    'service_label' => $service_label[$carrier_code],
                    'operator_label' => $operator_label[$carrier_code],
                    'emc_price' => number_format($live_rate[$carrier_code], 2),
                    'currency' => $currency[$carrier_code],
                    'shipment_nb' => $shipment_nb[$carrier_code],
                    'characteristics' => $characteristics[$carrier_code],
                );

                // check if the service is available for the shiping zone
                if (envoimoinscher_model::is_zones_enabled()) {
                    $service_available = false;
                    $zone_shipping_methods = $package_zone->get_shipping_methods();
                    foreach($zone_shipping_methods as $shipping_method) {
                        if (method_exists($shipping_method, 'getCalledClass') && $shipping_method->getCalledClass() == $carrier_code) {
                            $service_available = true;
                            break;
                        }
                    }
                    if (!$service_available) {
                        envoimoinscher_model::log('Simulation - service '.$carrier_code.' because it is not activated for shipping zone '.$package_zone->get_zone_name() );
                        continue;
                    }
                }

                // manage rate
                $final_rate = '';
                $pricing_items = envoimoinscher_model::get_pricing($carrier_code);

                foreach ($pricing_items as $pricing_item) {
                    envoimoinscher_model::log('Simulation pricing rule - looking at pricing rule : minimum weight '.(float)$pricing_item['weight-from'].' maximum weight '
                    .(float)$pricing_item['weight-to'].' minimum price '.(float)$pricing_item['price-from'].' maximum price '.(float)$pricing_item['price-to']);
                    /* check if conditions in pricing_item meet current situation */
                    if ($pricing_item['weight-from'] != '' && $return['cart_weight'] < floatval(str_replace(',', '.', $pricing_item['weight-from']))) {
                        envoimoinscher_model::log('Simulation pricing rule - pricing rule rejected because of minimum weight, weight is '.$return['cart_weight']);
                        continue;
                    }
                    if ($pricing_item['weight-to'] != '' && $return['cart_weight'] >= floatval(str_replace(',', '.', $pricing_item['weight-to']))) {
                        envoimoinscher_model::log('Simulation pricing rule - pricing rule rejected because of maximum weight price, weight is '.$return['cart_weight']);
                        continue;
                    }
                    if ($pricing_item['price-from'] != '' && $return['cart_price'] < floatval(str_replace(',', '.', $pricing_item['price-from']))) {
                        envoimoinscher_model::log('Simulation pricing rule - pricing rule rejected because of minimum price, price is '.$return['cart_price']);
                        continue;
                    }
                    if ($pricing_item['price-to'] != '' && $return['cart_price'] >= floatval(str_replace(',', '.', $pricing_item['price-to']))) {
                        envoimoinscher_model::log('Simulation pricing rule - pricing rule rejected because of maximum price, price is '.$return['cart_price']);
                        continue;
                    }
                    $test_shipping_class = array_diff($return['cart_shipping_classes'], $pricing_item['shipping-class']);
                    if (!empty($test_shipping_class)) {
                        envoimoinscher_model::log('Simulation pricing rule - pricing rule rejected because of shipping class, shipping class is/are '.implode('/', $return['cart_shipping_classes']));
                        continue;
                    }
                    if (envoimoinscher_model::is_zones_enabled()) {
                        if ($pricing_item['zones'] == null || !in_array((string)$package_zone->get_id(), $pricing_item['zones'], true)) {
                            envoimoinscher_model::log('Pricing rule - pricing rule rejected because of zone, zone is '.$package_zone->get_zone_name());
                            continue;
                        }
                    } else {
                        if ($pricing_item['zones'] == null || !in_array($to['country'], $pricing_item['zones'])) {
                            envoimoinscher_model::log('Pricing rule - pricing rule rejected because of country, country is '.$to['country']);
                            continue;
                        }
                    }
                    envoimoinscher_model::log('Simulation pricing rule - pricing rule validated');

                    /* if this is a match, apply pricing */
                    switch ($pricing_item['pricing']) {
                        case "live":
                            $final_rate = $live_rate[$carrier_code];
                            // add handling fees
                            if($pricing_item['handling-fees'] != '') {
                                $handling_fees = floatval(str_replace(',', '.', $pricing_item['handling-fees']));
                                if($handling_fees != 0) {
                                    // check if percentage
                                    if(strpos($pricing_item['handling-fees'], '%') !== false) {
                                        $final_rate = $final_rate * (1 + $handling_fees/100);
                                    } else {
                                        $final_rate = $final_rate + $handling_fees;
                                    }
                                }
                            }
                            $display_offer['FO_display'] = number_format($final_rate, 2).' €';
                            envoimoinscher_model::log('Simulation pricing rule - final price for carrier '.$this->id.' after application (live quotation) is '.$final_rate);
                            array_push($display_offers, $display_offer);
                            break;

                        case "rate":
                            $final_rate = $pricing_item['flat-rate'];
                            // add handling fees
                            if($pricing_item['handling-fees'] != '') {
                                $handling_fees = floatval(str_replace(',', '.', $pricing_item['handling-fees']));
                                if($handling_fees != 0) {
                                    // check if percentage
                                    if(strpos($pricing_item['handling-fees'], '%') !== false) {
                                        $final_rate = $final_rate * (1 + $handling_fees/100);
                                    } else {
                                        $final_rate = $final_rate + $handling_fees;
                                    }
                                }
                            }
                            $display_offer['FO_display'] = number_format($final_rate, 2).' €';
                            envoimoinscher_model::log('Simulation pricing rule - final price for carrier '.$this->id.' after application (flat rate) is '.$final_rate);
                            array_push($display_offers, $display_offer);
                            break;

                        case "free":
                            $final_rate = 0;
                            $display_offer['FO_display'] = __('Free', 'envoimoinscher');
                            envoimoinscher_model::log('Simulation pricing rule - carrier '.$this->id.' is free');
                            array_push($display_offers, $display_offer);
                            break;

                        case "deactivate":
                            $display_offer['FO_display'] = __('Deactivated', 'envoimoinscher');
                            envoimoinscher_model::log('Simulation pricing rule - carrier '.$this->id.' is deactivated');
                            array_push($display_offers, $display_offer);
                            break;

                        default :
                            break;
                    }

                    // exit loop
                    break;
                }
            }

			return $display_offers;
		}
	}
}

endif;
