<?php
namespace ReyCore\Modules\EstimatedDelivery;

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

class Base extends \ReyCore\Modules\ModuleBase {

	const ASSET_HANDLE = 'reycore-module-estimated-delivery';

	const VARIATIONS_KEY = '_rey_estimated_delivery_variation';

	public $args = [];
	public $settings = [];

	public function __construct()
	{
		add_action( 'reycore/woocommerce/init', [$this, 'init']);
	}

	public function init() {

		new AcfFields();
		new Customizer();

		if( ! $this->is_enabled() ){
			return;
		}

		add_action( 'wp', [$this, 'wp']);
		add_action( 'woocommerce_product_after_variable_attributes', [$this, 'variation_settings_fields'], 10, 3 );
		add_action( 'woocommerce_save_product_variation', [$this, 'save_variation_settings_fields'], 10, 2 );
		add_action( 'reycore/module/quickview/product', [$this, 'wp']);
		add_action( 'reycore/templates/register_widgets', [$this, 'register_widgets']);
		add_action( 'woocommerce_cart_totals_before_order_total', [$this, 'cart_checkout_table_row'] );
		add_action( 'woocommerce_review_order_before_order_total', [$this, 'cart_checkout_table_row'] );
		add_action( 'woocommerce_after_cart_item_name', [$this, 'cart_custom_text'] );
		add_action( 'reycore/dynamic_tags', [$this, 'dynamic_tag']);

	}

	public static function allow_variations(){
		return get_theme_mod('estimated_delivery__variations', false);
	}


	public function set_settings(){

		$position = [
			'default' => [
				'tag' => 'reycore/woocommerce_product_meta/before',
				'priority' => 20
			],
			'custom' => []
		];

		if( ! get_theme_mod('single_product_meta_v2', true) ){
			$position['default']['tag'] = 'woocommerce_single_product_summary';
			$position['default']['priority'] = 39; // before beta (40)
		}

		$position_option = get_theme_mod('estimated_delivery__position', 'default');

		$this->settings = apply_filters('reycore/woocommerce/estimated_delivery', [
			'days_text'       => esc_html_x('days', 'Estimated delivery string', 'rey-core'),
			'date_format'     => "l, M dS",
			'exclude_dates'   => [], // array (YYYY-mm-dd) eg. array("2012-05-02","2015-08-01")
			'margin_excludes' => [], // ["Saturday", "Sunday"]
			'position'        => isset($position[ $position_option ]) ? $position[ $position_option ] : $position[ 'default' ],
			'use_locale'      => get_theme_mod('estimated_delivery__locale', false),
			'locale'          => get_locale(),
			'locale_format'   => get_theme_mod('estimated_delivery__locale_format', "%A, %b %d"),
			'variations'      => self::allow_variations(),
			'limit_days'      => 180, // 180 days maximum,
			'use_weeks_after' => 0, // use weeks after x days,
			'week_text'       => esc_html_x('weeks', 'Estimated delivery string', 'rey-core'),
		]);

	}

	function wp(){

		if( ! $this->is_enabled() ){
			return;
		}

		if( ! reycore_wc__is_product() ){
			return;
		}

		$this->set_settings();
		$this->set_args();

		if( wp_doing_ajax() && isset($_REQUEST['id']) && $product_id = absint($_REQUEST['id']) ){
			$this->args['product'] = wc_get_product($product_id);
		}

		if( isset($this->settings['position']['tag']) ){
			add_action($this->settings['position']['tag'], [$this, 'display'], $this->settings['position']['priority']);
		}

		add_shortcode('rey_estimated_delivery', [$this, 'display']);

		add_filter( 'woocommerce_available_variation', [$this, 'load_variation_settings_fields'] );
		add_action( 'woocommerce_single_product_summary', [$this, 'display_shipping_class'], 39);

	}

	public function set_args(){
		$this->args = [
			'product'         => wc_get_product(),
			'days'            => reycore__get_option('estimated_delivery__days', 3),
			'days_individual' => reycore__acf_get_field( 'estimated_delivery__days' ),
			'margin'          => reycore__get_option('estimated_delivery__days_margin', ''),
			'excludes'        => get_theme_mod('estimated_delivery__exclude', ["Saturday", "Sunday"]),
			'inventory'       => get_theme_mod('estimated_delivery__inventory', ['instock']),
		];
	}

	public function register_widgets($widgets_manager){
		$widgets_manager->register( new PdpElement );
	}

	public function dynamic_tag( $tags ){
		$tags->get_manager()->register( new DynamicTag() );
	}

	public function display( $atts = [] ){

		if( !isset($this->settings) ){
			$this->set_settings();
		}

		if( empty($this->args) ){
			$this->set_args();
		}

		if( isset($atts['id']) && $product_id = absint($atts['id']) ){
			$this->args['product'] = wc_get_product($product_id);
		}

		$this->args = wp_parse_args($this->args, $atts);

		// for shortcode
		if( ! empty($atts['return']) && 1 === absint($atts['return']) ){
			ob_start();
			$this->output($this->args);
			return ob_get_clean();
		}

		$this->output($this->args);
	}

	protected function output($args) {

		$args = wp_parse_args($args, [
			'custom_days' => '',
			'product'     => false,
			'product_id'  => 0,
			'title'       => true,
			'wrapper'     => true,
			'before'      => '',
			'after'       => '',
		]);

		if( ! $args['product'] ){
			if( $product_id = $args['product_id'] ){
				$args['product'] = wc_get_product($product_id);
			}
		}

		if( ! $args['product'] ){
			return;
		}

		if ( $args['product']->is_virtual() ) {
			return;
		}

		if( $custom_days = $args['custom_days'] ){
			$args['days'] = $custom_days;
		}

		$args['stock_status'] = $args['product']->get_stock_status();

		$args['date'] = $this->calculate_date([
			'days'        => absint($args['days']),
			'skipdays'    => $args['excludes'],
		]);

		// It's out of stock && has fallback text
		if( $args['stock_status'] === 'outofstock' && ! in_array( $args['stock_status'], $args['inventory'], true) &&
			($text = get_theme_mod('estimated_delivery__text_outofstock', '')) ){
			$this->print_wrapper( $text );
		}

		// It's on backorder && has fallback text
		else if( $args['stock_status'] === 'onbackorder' && ! in_array( $args['stock_status'], $args['inventory'], true) &&
			($text = get_theme_mod('estimated_delivery__text_onbackorder', '')) ){
			$this->print_wrapper( $text );
		}

		if( empty($args['inventory']) && current_user_can('administrator') ){
			echo '<p class="rey-estimatedDelivery-title"><small>Estimated delivery not showing because nothing is selected in the "Show for" stock option. If you don\'t plan using Estimated Delivery option please disable the module or access Customizer > WooCommerce > Product page > Components in Summary and disable Estimated delivery option. <em>This message only shows up for Administrators</em>.</small></p>';
		}

		if( ! in_array( $args['stock_status'], $args['inventory'], true) ){
			return;
		}

		if( $custom_text = reycore__acf_get_field('estimated_delivery__custom_text') ){

			if( $args['wrapper'] ){
				$this->print_wrapper( $custom_text, $args['before'], $args['after'] );
			}
			else {
				echo $args['before'] . $custom_text . $args['after'];
			}
			return;
		}

		$display_type = get_theme_mod('estimated_delivery__display_type', 'number');

		$days = absint($args['days']);

		// no global days & no per product days
		if( ! $days && (is_null($args['days_individual']) || '' === $args['days_individual']) ){
			return;
		}

		$html = '';

		if( $args['title'] ){
			$html = sprintf('<span class="rey-estimatedDelivery-title">%s</span>&nbsp;',
				get_theme_mod('estimated_delivery__prefix',
				esc_html__('Estimated delivery:', 'rey-core'))
			);
		}

		// just mark as today
		if( ! $custom_days && $args['days_individual'] == '0' ){
			$html .= sprintf('<span class="rey-estimatedDelivery-date">%s</span>', esc_html__('Today', 'rey-core') );
		}

		// make sure it can be disabled, only to be used individually
		else {

			$the_margin = absint($args['margin']);

			$margin_date = '';

			// eg: 10th October
			if( $display_type === 'date' ){

				if( $the_margin ){
					$margin_excludes = $this->settings['margin_excludes'] ? $this->settings['margin_excludes'] : $args['excludes'];
					$margin_date = ' - ' . $this->calculate_date( [
						'days'        => $days + $the_margin,
						'skipdays'    => $margin_excludes,
					]);
				}

				$html .= sprintf('<span class="rey-estimatedDelivery-date">%s%s</span>',
					$args['date'],
					$margin_date
				);

			}

			// eg: 1-3 days
			else {

				$date_text = $this->settings['days_text'];

				if( $the_margin ){

					if( $days > $the_margin || $days === $the_margin ){
						$the_margin = $days + 1;
					}

					$margin_date = ' - ' . $the_margin;
				}

				$html .= sprintf('<span class="rey-estimatedDelivery-date">%1$s %2$s</span>',
					$days . $margin_date,
					$date_text
				);
			}

		}

		if( reycore__acf_get_field('estimated_delivery__hide') ){
			return;
		}

		if( $args['wrapper'] ){
			$this->print_wrapper( $html, $args['before'], $args['after'] );
		}
		else {
			echo $args['before'] . $html . $args['after'];
		}
	}

	public function print_wrapper($html, $before = '', $after = ''){

		if( ! $html ){
			return;
		}

		echo apply_filters( 'reycore/woocommerce/estimated_delivery/output', sprintf('<div class="rey-estimatedDelivery">%s</div>', $before . $html . $after), $this, $html, $before, $after );
	}

	public function calculate_date($args = []) {

		$args = wp_parse_args($args, [
			'timestamp'   => strtotime('today'),
			'days'        => 0,
			'skipdays'    => [],
			'date_format' => $this->settings['date_format'],
			'locale_format' => $this->settings['locale_format'],
		]);

		// limit to n days
		if( $args['days'] > $this->settings['limit_days'] ){
			$args['days'] = $this->settings['limit_days'];
		}

		$i = 1;

		while ($args['days'] >= $i) {
			$args['timestamp'] = strtotime("+1 day", $args['timestamp']);
			if ( (in_array(date("l", $args['timestamp']), $args['skipdays'])) || (in_array(date("Y-m-d", $args['timestamp']), $this->settings['exclude_dates'])) )
			{
				$args['days']++;
			}
			$i++;
		}

		if( $this->settings['use_locale'] ){

			// https://php.watch/versions/8.1/strftime-gmstrftime-deprecated
			if ( version_compare(PHP_VERSION, '8.1.0', '>=') && class_exists('\IntlDateFormatter')) {
				$formatter = new \IntlDateFormatter($this->settings['locale'], \IntlDateFormatter::LONG, \IntlDateFormatter::NONE);
				$intlFormat = self::strftime_to_intl($args['locale_format']);
				$formatter->setPattern($intlFormat);
				return $formatter->format($args['timestamp']);
			}

			// deprecated since PHP 8.1
			else {
				setlocale(LC_TIME, $this->settings['locale']);
				return strftime($args['locale_format'], $args['timestamp']);
			}
		}

		return date($args['date_format'], $args['timestamp']);
	}

	public function display_shipping_class(){

		if( ! get_theme_mod('single_extras__shipping_class', false) ){
			return;
		}

		global $product;

		if( $shipping_class = $product->get_shipping_class() ) {
			$term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
			if( is_a($term, '\WP_Term') ){
				echo apply_filters('reycore/woocommerce/product_page/shipping_class', '<p class="rey-shippingClass">' . $term->name . '</p>', $term);
			}
		}
	}

	public function variation_settings_fields( $loop, $variation_data, $variation ) {

		if( ! $this->is_enabled() ){
			return;
		}

		if( ! self::allow_variations() ){
			return;
		}

		reycore_assets()->add_styles('rey-form-row');

		woocommerce_wp_text_input([
			'id'            => self::VARIATIONS_KEY. $loop,
			'name'          => self::VARIATIONS_KEY. '[' . $loop . ']',
			'value'         => get_post_meta( $variation->ID, self::VARIATIONS_KEY, true ),
			'label'         => __( 'Estimated days delivery', 'rey-core' ),
			'desc_tip'      => true,
			'description'   => __( 'Add an estimation delivery date for this variation.', 'rey-core' ),
			'wrapper_class' => 'form-row form-row-full',
			'class' => 'input-text',
		]);
	}

	public function save_variation_settings_fields( $variation_id, $loop ) {

		if( ! $this->is_enabled() ){
			return;
		}

		if( ! self::allow_variations() ){
			return;
		}

		if ( isset( $_POST[self::VARIATIONS_KEY][ $loop ] ) ) {
			update_post_meta( $variation_id, self::VARIATIONS_KEY, reycore__clean( $_POST[self::VARIATIONS_KEY][ $loop ] ));
		}

	}

	public function load_variation_settings_fields( $variation ) {

		if( ! reycore_wc__is_product() ){
			return $variation;
		}

		if( ! $this->settings['variations'] ){
			return $variation;
		}

		if( ! ( $variation_estimation = get_post_meta( $variation[ 'variation_id' ], self::VARIATIONS_KEY, true ) ) ){
			return $variation;
		}

		ob_start();

		$args = $this->args;
		$args['custom_days'] = $variation_estimation;
		$args['product_id'] = $variation[ 'variation_id' ];

		$this->output($args);

		$variation['estimated_delivery'] = ob_get_clean();

		return $variation;
	}

	public function cart_custom_text( $cart_item ) {

		if( ! get_theme_mod('estimated_delivery__cart_checkout', true) ){
			return;
		}

		if( empty($cart_item['product_id']) ){
			return;
		}

		$custom_text = get_field('estimated_delivery__custom_text', $cart_item['product_id']);

		if( $custom_text ){
			printf('<div class="estimated-customText">%s</div>', $custom_text);
		}

	}

	public function cart_checkout_table_row() {

		if( ! get_theme_mod('estimated_delivery__cart_checkout', true) ){
			return;
		}

		$this->set_settings();

		$display_type = get_theme_mod('estimated_delivery__display_type', 'number');
		$estimation = '';
		$global_estimation = get_theme_mod('estimated_delivery__days', 3);
		$products_estimations = [];
		$margin_estimations = [];

		foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {

			$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
			$days = get_field( 'estimated_delivery__days', $product_id ); // per product

			if( isset( $cart_item['variation_id'] ) && ($variation_id = $cart_item['variation_id']) ){

				if( $var_est = get_field( 'estimated_delivery__days', $variation_id ) ){ // per variation
					$product_id = $variation_id;
					$days = absint($var_est);
				}
			}

			$products_estimations[$product_id] = (! is_null($days) && '' !== $days) ? $days : $global_estimation;
			$margin = absint(reycore__get_option('estimated_delivery__days_margin', '', false, $product_id));

			if( 'date' === $display_type ){
				$margin_estimations[$product_id] = absint($products_estimations[$product_id]) + $margin;
			}
			else {
				$margin_estimations[$product_id] = $margin;
			}

		}

		if( empty( array_filter($products_estimations) ) ){
			return;
		}

		$min = absint(min($products_estimations));
		$max = absint(max(max($products_estimations), max($margin_estimations)));
		$is_range = $min !== $max;

		// use global
		$excludes = get_theme_mod('estimated_delivery__exclude', ["Saturday", "Sunday"]);

		// date format
		if( 'date' === $display_type ){

			// get starting point
			$estimation = $this->calculate_date([
				'days'          => $min,
				'skipdays'      => $excludes,
				'date_format'   => 'M dS',
				'locale_format' => "%B %d",
			]);

			// get end point if range
			if( $is_range ){
				$estimation .= ' - ' . $this->calculate_date([
					'days'          => $max,
					'skipdays'      => $excludes,
					'date_format'   => 'dS',
					'locale_format' => "%d",
				]);
			}
		}

		// number of days
		else {

			// get starting point
			$estimation = $min;

			// get end point if range
			if( $is_range ){
				$estimation .= ' - ' . $max;
			}

			// days text
			$estimation .= ' ' . $this->settings['days_text'];
		}

		if( '' === $estimation  ){
			return;
		} ?>

		<tr class="estimated-delivery">
			<th><?php esc_html_e( 'Estimated Delivery', 'rey-core' ); ?></th>
			<td data-title="<?php esc_html_e( 'Estimated Delivery', 'rey-core' ); ?>"><?php echo $estimation; ?></td>
		</tr><?php
	}

	public static function strftime_to_intl($format) {
		$map = [
			'%a' => 'eee',  // Short textual representation of the day
			'%A' => 'EEEE', // Full textual representation of the day
			'%d' => 'dd',  // Two digit representation of the day
			'%e' => 'd',   // Day of the month
			'%h' => 'MMM', // Abbreviated month name (same as %b)
			'%b' => 'MMM', // Abbreviated month name
			'%B' => 'MMMM', // Full month name
			'%m' => 'MM', // Two digit representation of the month
			'%y' => 'yy', // Two digit representation of the year
			'%Y' => 'yyyy', // Four digit representation of the year
			'%H' => 'HH', // Two digit representation of the hour in 24-hour format
			'%I' => 'hh', // Two digit representation of the hour in 12-hour format
			'%M' => 'mm', // Two digit representation of the minute
			'%S' => 'ss', // Two digit representation of the second
			'%p' => 'a',  // 'AM' or 'PM' for 12-hour format times
		];

		return str_replace(array_keys($map), array_values($map), $format);
	}


	public function is_enabled() {
		return get_theme_mod('single_extras__estimated_delivery', false);
	}

	public static function __config(){
		return [
			'id' => basename(__DIR__),
			'title' => esc_html_x('Estimated Delivery', 'Module name', 'rey-core'),
			'description' => esc_html_x('This tool is useful to display a specific date or timeframe until a product will be delivered.', 'Module description', 'rey-core'),
			'icon'        => '',
			'categories'  => ['woocommerce'],
			'keywords'    => ['product page'],
			'help'        => reycore__support_url('kb/estimated-delivery-text-issues-with-other-languages/'),
			'video' => true
		];
	}

	public function module_in_use(){
		return $this->is_enabled();
	}
}
