<?php
/**
 * Coupon parser during import & export
 *
 * @author      StoreApps
 * @since       3.3.0
 * @version     1.7.0
 *
 * @package     woocommerce-smart-coupons/includes/
 */

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

if ( ! class_exists( 'WC_SC_Coupon_Parser' ) ) {

	/**
	 * Class to parse values for WC_Coupon for importing
	 */
	class WC_SC_Coupon_Parser {

		/**
		 * The post type
		 *
		 * @var string $post_type
		 */
		public $post_type;

		/**
		 * Reserved fields
		 *
		 * @var array $reserved_fields
		 */
		public $reserved_fields;       // Fields we map/handle (not custom fields).

		/**
		 * POst defaults
		 *
		 * @var array $post_defaults
		 */
		public $post_defaults;         // Default post data.

		/**
		 * Postmeta defaults
		 *
		 * @var array $postmeta_defaults
		 */
		public $postmeta_defaults;     // default post meta.

		/**
		 * Term defaults
		 *
		 * @var array $term_defaults
		 */
		public $term_defaults;     // default term data.

		/**
		 * Row number
		 *
		 * @var int $row
		 */
		public $row;

		/**
		 * Constructor
		 *
		 * @param string $post_type The post type.
		 */
		public function __construct( $post_type = 'shop_coupon' ) {

			$this->post_type = $post_type;

			$this->reserved_fields = array(
				'id',
				'post_id',
				'post_type',
				'menu_order',
				'postmeta',
				'post_status',
				'post_title',
				'post_name',
				'comment_status',
				'post_date',
				'post_date_gmt',
				'post_content',
				'post_excerpt',
				'post_parent',
				'post_password',
				'discount_type',
				'coupon_amount',
				'free_shipping',
				'expiry_date',
				'minimum_amount',
				'maximum_amount',
				'individual_use',
				'exclude_sale_items',
				'product_ids',
				'exclude_product_ids',
				'product_categories',
				'exclude_product_categories',
				'customer_email',
				'usage_limit',
				'usage_limit_per_user',
				'limit_usage_to_x_items',
				'usage_count',
				'_used_by',
			);

			$this->post_defaults = array(
				'post_type'      => $this->post_type,
				'menu_order'     => '',
				'postmeta'       => array(),
				'post_status'    => 'publish',
				'post_title'     => '',
				'post_name'      => '',
				'comment_status' => 'closed',
				'post_date'      => '',
				'post_date_gmt'  => '',
				'post_content'   => '',
				'post_excerpt'   => '',
				'post_parent'    => 0,
				'post_password'  => '',
				'post_author'    => get_current_user_id(),
			);

			$this->postmeta_defaults = apply_filters(
				'smart_coupons_parser_postmeta_defaults',
				array(
					'discount_type'                  => 'fixed_cart',
					'coupon_amount'                  => '',
					'free_shipping'                  => '',
					'expiry_date'                    => '',
					'sc_coupon_validity'             => '',
					'validity_suffix'                => '',
					'auto_generate_coupon'           => '',
					'coupon_title_prefix'            => '',
					'coupon_title_suffix'            => '',
					'is_pick_price_of_product'       => '',
					'minimum_amount'                 => '',
					'maximum_amount'                 => '',
					'individual_use'                 => '',
					'exclude_sale_items'             => '',
					'product_ids'                    => '',
					'exclude_product_ids'            => '',
					'product_categories'             => '',
					'exclude_product_categories'     => '',
					'customer_email'                 => '',
					'sc_disable_email_restriction'   => '',
					'usage_limit'                    => '',
					'usage_limit_per_user'           => '',
					'limit_usage_to_x_items'         => '',
					'sc_is_visible_storewide'        => '',
					'usage_count'                    => '',
					'_used_by'                       => '',
					'sc_restrict_to_new_user'        => '',
					'wc_sc_max_discount'             => '',
					'apply_before_tax'               => '',
					'wc_sc_no_of_selectable_product' => '',
					'product_brands'                 => '',
					'exclude_product_brands'         => '',
				)
			);

			$this->term_defaults = array(
				'sc_coupon_category' => '',
			);
		}

		/**
		 * Handle call to functions which is not available in this class
		 *
		 * @param string $function_name The function name.
		 * @param array  $arguments Array of arguments passed while calling $function_name.
		 * @return result of function call
		 */
		public function __call( $function_name, $arguments = array() ) {

			global $woocommerce_smart_coupon;

			if ( ! is_callable( array( $woocommerce_smart_coupon, $function_name ) ) ) {
				return;
			}

			if ( ! empty( $arguments ) ) {
				return call_user_func_array( array( $woocommerce_smart_coupon, $function_name ), $arguments );
			} else {
				return call_user_func( array( $woocommerce_smart_coupon, $function_name ) );
			}
		}

		/**
		 * Format data passed from CSV
		 *
		 * @param array  $data The data to format.
		 * @param string $enc encoding The encoding.
		 */
		public function format_data_from_csv( $data, $enc ) {
			return ( 'UTF-8' === $enc ) ? $data : mb_convert_encoding( $data, 'UTF-8', mb_detect_encoding( $data ) );
		}

		/**
		 * Parse data
		 *
		 * @param string $file Imported file.
		 * @return array parsed data with headers
		 */
		public function parse_data( $file ) {

			// Set locale.
			$enc = $this->mb_detect_encoding( $file, 'UTF-8, ISO-8859-1', true );
			if ( $enc ) {
				setlocale( LC_ALL, 'en_US.' . $enc );
			}
			if ( ! $this->is_php_gte( '8.0.0' ) ) {
				ini_set( 'auto_detect_line_endings', true ); // phpcs:ignore
			}

			$parsed_data = array();

			$handle = fopen( $file, 'r' ); // phpcs:ignore

			// Put all CSV data into an associative array.
			if ( false !== $handle ) {

				$header = fgetcsv( $handle, 0, ',', '"', '\\' );

				while ( false !== ( $postmeta = fgetcsv( $handle, 0 ) ) ) { // phpcs:ignore
					$row = array();
					foreach ( $header as $key => $heading ) {

						$s_heading = strtolower( $heading );

						$row[ $s_heading ] = ( isset( $postmeta[ $key ] ) ) ? $this->format_data_from_csv( stripslashes( $postmeta[ $key ] ), $enc ) : '';

						$raw_headers[ $s_heading ] = $heading;
					}

					$parsed_data[] = $row;

					unset( $postmeta, $row );

				}

				fclose( $handle ); // phpcs:ignore
			}

			return array( $parsed_data, $raw_headers );
		}

		/**
		 * Parse data one row at a time
		 *
		 * @param  boolean $file_handler        CSV file handler.
		 * @param  array   $header        CSV header meta column name.
		 * @param  integer $file_position file pointer posistion to read from.
		 * @param  string  $encoding      Character encoding.
		 * @return array  $result parsed data with current file pointer position
		 */
		public function parse_data_by_row( $file_handler = false, $header = array(), $file_position = 0, $encoding = '' ) {

			$parsed_csv_data = array();

			$reading_completed = false;

			if ( false !== $file_handler ) {

				if ( $file_position > 0 ) {

					fseek( $file_handler, (int) $file_position );

				}

				if ( false !== ( $postmeta = fgetcsv( $file_handler, 0 ) ) ) { // phpcs:ignore
					$row = array();
					foreach ( $header as $key => $heading ) {

						$s_heading = strtolower( $heading );

						// Put all CSV data into an associative array by row.
						$row[ $s_heading ] = ( isset( $postmeta[ $key ] ) ) ? $this->format_data_from_csv( stripslashes( $postmeta[ $key ] ), $encoding ) : '';
					}

					$parsed_csv_data = $row;

					unset( $postmeta, $row );

				} else {

					$reading_completed = true;

				}

				$file_position = ftell( $file_handler );
			}

			$result = array(
				'parsed_csv_data'   => $parsed_csv_data,
				'file_position'     => $file_position,
				'reading_completed' => $reading_completed,
			);

			return $result;
		}

		/**
		 * Parse coupon
		 *
		 * @param array $item The imported item.
		 * @return array $coupon
		 */
		public function parse_coupon( $item ) {
			global $wc_csv_coupon_import;

			++$this->row;
			$postmeta  = array();
			$term_data = array();
			$coupon    = array();

			$post_id = ( ! empty( $item['id'] ) ) ? absint( $item['id'] ) : 0;
			$post_id = ( ! empty( $item['post_id'] ) ) ? absint( $item['post_id'] ) : $post_id;

			$product['post_id'] = $post_id;

			// Get post fields.
			foreach ( $this->post_defaults as $column => $default ) {
				if ( isset( $item[ $column ] ) ) {
					$product[ $column ] = $item[ $column ];
				}
			}

			// Get custom fields.
			foreach ( $this->postmeta_defaults as $column => $default ) {
				if ( isset( $item[ $column ] ) ) {
					$postmeta[ $column ] = (string) $item[ $column ];
				} elseif ( isset( $item[ '_' . $column ] ) ) {
					$postmeta[ $column ] = (string) $item[ '_' . $column ];
				}
			}

			// Get term fields.
			foreach ( $this->term_defaults as $column => $default ) {
				if ( isset( $item[ $column ] ) ) {
					$term_data[ $column ] = $item[ $column ];
				}
			}

			// Merge post meta with defaults.
			$coupon    = wp_parse_args( $product, $this->post_defaults );
			$postmeta  = wp_parse_args( $postmeta, $this->postmeta_defaults );
			$term_data = wp_parse_args( $term_data, $this->term_defaults );

			$all_discount_types = wc_get_coupon_types();

			if ( ! empty( $postmeta['discount_type'] ) ) {
				$discount_type = __( $postmeta['discount_type'], 'woocommerce' ); // phpcs:ignore
			} elseif ( $this->is_wc_gte_30() ) {
					$discount_type = __( 'Percentage discount', 'woocommerce' );
			} else {
				$discount_type = __( 'Cart % Discount', 'woocommerce' );
			}

			$discount_type = ( ! empty( $discount_type ) && in_array( $discount_type, $all_discount_types, true ) ) ? $discount_type : $postmeta['discount_type'];

			// discount types.
			if ( ! empty( $discount_type ) ) {

				if ( in_array( $discount_type, $all_discount_types, true ) ) {
					$postmeta['discount_type'] = array_search( $discount_type, $all_discount_types, true );
				}

				if ( empty( $postmeta['discount_type'] ) ) {
					$postmeta['discount_type'] = 'percent';
				}
			}

			// product_ids.
			if ( isset( $postmeta['product_ids'] ) && ! is_array( $postmeta['product_ids'] ) ) {
					$ids                     = array_filter( array_map( 'trim', explode( '|', $postmeta['product_ids'] ) ) );
					$ids                     = implode( ',', $ids );
					$postmeta['product_ids'] = $ids;
			}

			// exclude_product_ids.
			if ( isset( $postmeta['exclude_product_ids'] ) && ! is_array( $postmeta['exclude_product_ids'] ) ) {
					$ids                             = array_filter( array_map( 'trim', explode( '|', $postmeta['exclude_product_ids'] ) ) );
					$ids                             = implode( ',', $ids );
					$postmeta['exclude_product_ids'] = $ids;
			}

			// product_categories.
			if ( isset( $postmeta['product_categories'] ) && ! is_array( $postmeta['product_categories'] ) ) {
					$ids                            = array_filter( array_map( 'trim', explode( '|', $postmeta['product_categories'] ) ) );
					$postmeta['product_categories'] = $ids;
			}

			// exclude_product_categories.
			if ( isset( $postmeta['exclude_product_categories'] ) && ! is_array( $postmeta['exclude_product_categories'] ) ) {
					$ids                                    = array_filter( array_map( 'trim', explode( '|', $postmeta['exclude_product_categories'] ) ) );
					$postmeta['exclude_product_categories'] = $ids;
			}

			// customer_email.
			if ( isset( $postmeta['customer_email'] ) && ! is_array( $postmeta['customer_email'] ) ) {
					$email_ids                  = array_filter( array_map( 'trim', explode( ',', $postmeta['customer_email'] ) ) );
					$postmeta['customer_email'] = $email_ids;
			}

			// expiry date.
			if ( isset( $postmeta['expiry_date'] ) ) {
				$timestamp_expiry_date = '';
				if ( ! empty( $postmeta['expiry_date'] ) ) {
					$timestamp_expiry_date = ( function_exists( 'wc_string_to_timestamp' ) ) ? wc_string_to_timestamp( $postmeta['expiry_date'] ) : 0;
				}
				if ( ! empty( $postmeta['expiry_date'] ) && empty( $timestamp_expiry_date ) ) {
					/* translators: 1. Coupon code 2. Expiry date */
					$this->log( 'error', sprintf( __( 'Incorrect format for expiry date of coupon "%1$s". Entered date is %2$s. Expected date format: YYYY-MM-DD', 'woocommerce-smart-coupons' ), $coupon['post_title'], $postmeta['expiry_date'] ) );
				}
				$postmeta['expiry_date'] = ( ! empty( $timestamp_expiry_date ) ) ? gmdate( 'Y-m-d', $timestamp_expiry_date ) : '';
			}

			// usage count.
			if ( isset( $postmeta['usage_count'] ) ) {
				$postmeta['usage_count'] = ( ! empty( $postmeta['usage_count'] ) ) ? $postmeta['usage_count'] : 0;
			}

			// used_by.
			if ( isset( $postmeta['_used_by'] ) ) {
				$postmeta['_used_by'] = ( ! empty( $postmeta['_used_by'] ) ) ? $postmeta['_used_by'] : '';
			}

			// Coupon currency data.
			if ( ! empty( $item['_coupon_currency_data'] ) ) {
				$postmeta['_coupon_currency_data'] = maybe_serialize( $item['_coupon_currency_data'] );
			}

			// Put set core product postmeta into product array.
			foreach ( $postmeta as $key => $value ) {
				$coupon['postmeta'][] = array(
					'key'   => esc_attr( $key ),
					'value' => $value,
				);
			}

			// term data.
			foreach ( $term_data as $key => $value ) {
				$coupon['term_data'][] = array(
					'key'   => esc_attr( $key ),
					'value' => $value,
				);
			}

			unset( $item, $postmeta );

			return $coupon;
		}
	}

}
