<?php
require_once __DIR__ . '/class-ecwid-importer-task.php';
class Ecwid_Importer {

	const OPTION_TASKS                = 'ecwid_importer_tasks';
	const OPTION_CURRENT_TASK         = 'ecwid_importer_current_task';
	const OPTION_CATEGORIES           = 'ecwid_importer_categories';
	const OPTION_PRODUCTS             = 'ecwid_importer_products';
	const OPTION_STATUS               = 'ecwid_importer_status';
	const OPTION_WOO_CATALOG_IMPORTED = 'ecwid_imported_from_woo';
	const OPTION_SETTINGS             = 'ecwid_importer_settings';
	const OPTION_DEMO_PRODUCTS        = 'woo_importer_demo_products';
	const OPTION_ERROR_LOG            = 'ecwid_importer_error_log';

	const OPTIONS_SEPARATE_IMAGE_LOADING = 'ec_woo_import_separate_image_loading';

	const TICK_LENGTH = 20;

	const SETTING_UPDATE_BY_SKU = 'update-by-sku';
	const SETTING_DELETE_DEMO   = 'delete-demo';
	const DEMO_CREATE_FROM      = 1469707991;
	const DEMO_CREATE_TO        = 1469710442;

	protected $_tasks;
	protected $_start_time;

	public $_batch;
	public $_batch_progress;

	public function initiate( $settings = array() ) {
		update_option( self::OPTION_CATEGORIES, array() );
		update_option( self::OPTION_PRODUCTS, array() );
		update_option( self::OPTION_TASKS, array() );
		update_option( self::OPTION_STATUS, array() );
		update_option( self::OPTION_ERROR_LOG, array() );
		$this->_start();

		$api = new Ecwid_Api_V3();

		$settings = array(
			self::SETTING_UPDATE_BY_SKU => true,
			self::SETTING_DELETE_DEMO   => true,
		);
		$this->_set_settings( $settings );

		$this->_maybe_set_forced_settings();
		$this->_set_tasks( array() );

		$this->_build_tasks();
		$this->_set_current_task( 0 );
	}

	public function tick() {
		set_time_limit( 0 );
		$results = array();

		$start = time();

		$status   = get_option( self::OPTION_STATUS, array( 'plan_limit' => array() ) );
		$progress = array(
			'success'         => array(),
			'error'           => array(),
			'total'           => count( $this->_tasks ),
			'imagesProcessed' => 0,
		);

		do {
			$current_task = $this->_get_current_task();
			$task_data    = $this->_tasks[ $current_task ];
			if ( ! isset( $status['plan_limit'] )
				|| ! is_array( $status['plan_limit'] )
				|| ! array_key_exists( $task_data['type'], $status['plan_limit'] )
			) {
				$task   = Ecwid_Importer_Task::load_task( $task_data['type'] );
				$result = $task->execute( $this, $task_data );

				if ( ! is_array( $result ) ) {
					++$current_task;

					if ( $current_task >= count( $this->_tasks ) ) {
						break;
					}

					$this->_set_current_task( $current_task );

					$progress['current'] = $current_task;
					$progress['total']   = count( $this->_tasks );

					$this->_set_tasks( $this->_tasks );

					if ( $start + self::TICK_LENGTH <= time() ) {
						$progress['status'] = 'in_progress';
						break;
					}
					continue;
				}//end if

				if ( $task instanceof Ecwid_Importer_Task_Upload_Product_Image
					|| $task instanceof Ecwid_Importer_Task_Upload_Product_Gallery_Image
					|| $task instanceof Ecwid_Importer_Task_Upload_Category_Image
				) {
					++$progress['imagesProcessed'];
				}

				if ( $result['status'] == 'error' ) {
					$progress['error'][] = $task_data['type'];

					$error_data = $result['data'];

					if ( ! is_wp_error( $error_data ) && is_array( $error_data ) && isset( $error_data['response']['code'] ) && $error_data['response']['code'] == 402 ) {
						$status['plan_limit'][ $task_data['type'] ] = true;
					}

					$message = '';
					if ( is_wp_error( $error_data ) ) {
						$message = var_export( $result['data']->get_error_message(), true );
					} elseif ( is_array( $error_data ) ) {
						$message = @$error_data['response']['code'];
						if ( $error_data['response']['message'] ) {
							$message .= ' ' . $error_data['response']['message'];
						}
						if ( @$error_data['http_message'] ) {
							$message .= ' ' . $error_data['http_message'];
						}
						if ( @$error_data['api_message'] ) {
							$message .= ':' . $error_data['api_message'];
						}

						if ( @$error_data['api_code'] ) {
							$message .= '(' . $error_data['api_code'] . ')';
						}
					} elseif ( @$error_data == 'skipped' ) {
						$message = $result['message'];
					}

					$this->_tasks[ $current_task ]['error'] = $message;
					if ( ! isset( $progress['error_messages'][ $task_data['type'] ] ) ) {
						$progress['error_messages'][ $task_data['type'] ] = array();
					}
					if ( ! isset( $progress['error_messages'][ $task_data['type'] ][ $message ] ) ) {
						$progress['error_messages'][ $task_data['type'] ][ $message ] = array();
					}

					$error_data = array();
					if ( $task instanceof Ecwid_Importer_Task_Product_Base ) {
						$error_data['woo_id']   = $task->get_woo_id();
						$error_data['woo_link'] = $task->get_woo_link();
						$error_data['name']     = $task->get_product_name();

						if ( $task->get_ecwid_id() ) {
							$error_data['ecwid_id']   = $task->get_ecwid_id();
							$error_data['ecwid_link'] = $task->get_ecwid_link();
						}

						if ( $task instanceof Ecwid_Importer_Task_Create_Product_Variation || $task instanceof Ecwid_Importer_Task_Upload_Product_Variation_Image ) {
							$error_data['variation_id'] = $task_data['variation_id'];
						}
					} else {
						$error_data = $task_data;
					}

					$progress['error_messages'][ $task_data['type'] ][ $message ][] = $error_data;
				} elseif ( isset( $result['status'] ) ) {
					$progress['success'][] = $task_data['type'];
				}//end if

				if ( isset( $status['plan_limit'] ) && is_array( $status['plan_limit'] ) && count( $status['plan_limit'] ) ) {
					update_option( self::OPTION_STATUS, $status );
				}
			} else {
				$progress['error'][]          = $task_data['type'];
				$progress['plan_limit_hit'][] = $task_data['type'];
			}//end if

			++$current_task;

			if ( $current_task >= count( $this->_tasks ) ) {
				break;
			}

			$this->_set_current_task( $current_task );

			$progress['current'] = $current_task;
			$progress['total']   = count( $this->_tasks );

			$this->_set_tasks( $this->_tasks );

			if ( $start + self::TICK_LENGTH <= time() ) {
				$progress['status'] = 'in_progress';
				break;
			}
		} while ( 1 );

		if ( count( $this->get_batch() ) ) {
			if ( $this->execute_batch() ) {
				$this->_set_tasks( $this->_tasks );
				$progress['status'] = 'in_progress';
			}
		}

		if ( is_array( $this->_batch_progress ) ) {
			if ( isset( $this->_batch_progress['success'] ) ) {
				$progress['success'] = array_merge( $progress['success'], $this->_batch_progress['success'] );
			}
			if ( isset( $this->_batch_progress['error'] ) ) {
				$progress['error'] = array_merge( $progress['error'], $this->_batch_progress['error'] );
			}
			if ( isset( $this->_batch_progress['error_messages'] ) ) {
				if ( ! isset( $progress['error_messages'] ) ) {
					$progress['error_messages'] = $this->_batch_progress['error_messages'];
				} else {
					$progress['error_messages'] = array_merge( $progress['error_messages'], $this->_batch_progress['error_messages'] );
				}
			}
		}

		if ( isset( $progress['error_messages'] ) ) {
			$error_log = get_option( self::OPTION_ERROR_LOG, array() );

			if ( count( $error_log ) ) {
				$progress['error_messages'] = array_merge( $progress['error_messages'], $error_log );
			}
			update_option( self::OPTION_ERROR_LOG, $progress['error_messages'] );
		}

		if ( isset( $progress['status'] ) && $progress['status'] == 'in_progress' ) {
			$progress['tasks'] = $this->_tasks;
			return $progress;
		}

		$this->_set_tasks( null );

		$status = get_option( self::OPTION_STATUS, array() );
		if ( isset( $status['plan_limit'] ) ) {
			$progress['plan_limit_hit'] = $status['plan_limit'];
		}

		$progress['error_messages'] = get_option( self::OPTION_ERROR_LOG, array() );
		$progress['status']         = 'complete';

		update_option( self::OPTION_WOO_CATALOG_IMPORTED, 'true' );

		return $progress;
	}

	public function append_batch( $batch_item ) {
		$this->_batch[] = $batch_item;

		return count( $this->_batch ) - 1;
	}

	public function get_batch() {

		if ( ! is_array( $this->_batch ) ) {
			$this->clear_batch();
		}

		return $this->_batch;
	}

	public function clear_batch() {
		$this->_batch = array();
	}

	public function execute_batch() {

		$batch = $this->get_batch();

		$api    = new Ecwid_Api_V3();
		$result = $api->create_batch( $batch );

		if ( ! is_wp_error( $result ) && $result['response']['code'] == '200' ) {
			$data   = json_decode( $result['body'] );
			$ticket = $data->ticket;

			$this->append_task(
				Ecwid_Importer_Task_Batch_Status::build(
					array( 'ticket' => $ticket )
				)
			);

			$this->clear_batch();

			return true;
		}

		return false;
	}

	public function send_import_mark_to_log() {
		$api    = new Ecwid_Api_V3();
		$params = array( 'wp_import_woo' => get_ecwid_store_id() );

		return $api->get_store_update_stats( $params );
	}

	public static function is_localhost() {

		if ( get_option( self::OPTIONS_SEPARATE_IMAGE_LOADING, false ) ) {
			return true;
		}

		if ( ! isset( $_SERVER['REMOTE_ADDR'] ) ) {
			return false;
		}

		return in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
	}

	public function append_task( $task ) {
		$this->_tasks[] = $task;

		return count( $this->_tasks ) - 1;
		// array_splice( $this->_tasks, (int)$this->_get_current_task() + 1, 0, array( $task ) );
	}

	/**
	 * Appends $task as a child of current task. It skips current task, skips all task with the same type
	 * and appends $task after all its siblings
	 *
	 * @param $task
	 */
	public function append_child( $task ) {
		$ind       = $this->_get_current_task();
		$this_task = $this->_tasks[ $ind ];
		++$ind;
		while ( isset( $this->_tasks[ $ind ] ) && ( $this->_tasks[ $ind ]['type'] == $task['type'] || $this->_tasks[ $ind ]['type'] == $this_task['type'] ) ) {
			++$ind;
		}
		return $this->append_after( $task, $ind - 1 );
	}
	public function append_after( $task, $index ) {
		array_splice( $this->_tasks, $index + 1, 0, array( $task ) );
		return $index + 1;
	}

	public function append_after_type( $task ) {
		$ind  = $this->_get_current_task();
		$type = $this->_tasks[ $ind ];

		++$ind;
		while ( $this->_tasks[ $ind ]['type'] == $type && isset( $this->_tasks[ $ind ] ) ) {
			++$ind;
		}

		return $this->append_after( $task, $ind );
	}

	public function has_begun() {
		return $this->_load_tasks() != null;
	}

	public function proceed() {
		$this->_load_tasks();
		$this->_get_current_task();

		return $this->tick();
	}

	public function get_ecwid_category_id( $woo_category_id ) {
		if ( ! $woo_category_id ) {
			return 0;
		}

		$categories = get_option( self::OPTION_CATEGORIES, array() );

		return @$categories[ $woo_category_id ];
	}

	public function save_ecwid_category( $woo_category_id, $ecwid_category_id ) {
		$categories                     = get_option( self::OPTION_CATEGORIES, array() );
		$categories[ $woo_category_id ] = $ecwid_category_id;

		update_option( self::OPTION_CATEGORIES, $categories );
	}

	public function get_ecwid_product_id( $woo_product_id ) {
		$products = get_option( self::OPTION_PRODUCTS, array() );
		return @$products[ $woo_product_id ];
	}
	public function save_ecwid_product_id( $woo_product_id, $ecwid_product_id ) {
		$products                    = get_option( self::OPTION_PRODUCTS, array() );
		$products[ $woo_product_id ] = $ecwid_product_id;
		update_option( self::OPTION_PRODUCTS, $products );
	}

	protected function _start() {
		$this->_start_time = time();
	}

	protected function _build_tasks() {
		$this->append_task(
			Ecwid_Importer_Task_Main::build( array() )
		);
	}

	protected function _set_tasks( $tasks ) {
		$this->_tasks = $tasks;
		update_option( self::OPTION_TASKS, $this->_tasks );
	}

	protected function _load_tasks() {
		if ( ! empty( $this->_tasks ) ) {
			return $this->_tasks;
		}

		return $this->_tasks = get_option( self::OPTION_TASKS, null );
	}

	protected function _set_current_task( $ind ) {
		update_option( self::OPTION_CURRENT_TASK, $ind );
	}

	protected function _get_current_task() {
		return (int) get_option( self::OPTION_CURRENT_TASK, null );
	}

	public function get_setting( $name ) {
		$settings = get_option( self::OPTION_SETTINGS, array() );
		return @$settings[ $name ];
	}

	protected function _set_setting( $name, $value ) {
		$settings = get_option( self::OPTION_SETTINGS, array() );

		$settings[ $name ] = $value;

		update_option( self::OPTION_SETTINGS, $settings );
	}

	protected function _set_settings( $settings ) {
		$saved_settings = array();

		if ( $settings[ self::SETTING_UPDATE_BY_SKU ] ) {
			$saved_settings[ self::SETTING_UPDATE_BY_SKU ] = true;
		}

		if ( @$settings[ self::SETTING_DELETE_DEMO ] ) {
			$saved_settings[ self::SETTING_DELETE_DEMO ] = true;
		}

		update_option( self::OPTION_SETTINGS, $saved_settings );
	}

	protected function _maybe_set_forced_settings() {
		if ( self::count_ecwid_demo_products() > 0 && self::count_ecwid_demo_products() == self::count_ecwid_products() ) {
			$this->_set_setting( self::SETTING_DELETE_DEMO, true );
		}
	}

	protected static function _get_woo_categories( $args ) {

		$args = wp_parse_args(
			$args,
			array(
				'menu_order'   => 'ASC',
				'hide_empty'   => 0,
				'hierarchical' => 1,
				'taxonomy'     => 'product_cat',
				'pad_counts'   => 1,
				'get'          => 'all',
			)
		);

		if ( isset( $args['parent'] ) && $args['parent'] ) {
			$args = apply_filters( 'woocommerce_product_subcategories_args', $args );
		}

		return get_categories( $args );
	}

	public static function count_woo_categories() {
		$all_categories = self::_get_woo_categories( array( 'count' => true ) );

		$count = count( $all_categories );

		$default             = self::_get_woo_categories( array( 'objectIds' => array( get_option( 'default_product_cat' ) ) ) );
		$children_of_default = self::_get_woo_categories( array( 'parent' => get_option( 'default_product_cat' ) ) );

		if ( count( $default ) > 0 && count( $children_of_default ) == 0 ) {
			--$count;
		}

		return $count;
	}

	public static function count_woo_products() {
		$count = wp_count_posts( 'product' );
		return $count->publish;
	}

	public static function count_ecwid_products() {
		$api            = new Ecwid_Api_V3();
		$max            = 100;
		$ecwid_products = $api->get_products( array( 'limit' => $max ) );

		if ( $ecwid_products->total <= $max ) {
			$demo = array();
			foreach ( $ecwid_products->items as $item ) {
				if ( $item->isSampleProduct ) {
					$demo[] = $item->id;
				}
			}

			EcwidPlatform::set( self::OPTION_DEMO_PRODUCTS, $demo );
		}

		return $ecwid_products->total;
	}

	public static function count_ecwid_categories() {
		$api              = new Ecwid_Api_V3();
		$ecwid_categories = $api->get_categories( array( 'limit' => 1 ) );
		return $ecwid_categories->total;
	}

	public static function count_ecwid_demo_products() {
		$ecwid_products = self::get_ecwid_demo_products();

		return count( $ecwid_products );
	}

	public static function get_ecwid_demo_products() {

		// Actual gathering happens in count_ecwid_products
		// TODO: fix this discrapency
		$demo_products = EcwidPlatform::get( self::OPTION_DEMO_PRODUCTS, array() );

		if ( is_array( $demo_products ) ) {
			return $demo_products;
		}

		return array();
	}


	public function gather_categories( $parent = 0 ) {
		$product_categories = self::_get_woo_categories( array( 'parent' => $parent ) );

		if ( count( $product_categories ) == 0 ) {
			return array();
		}

		$result = array();
		foreach ( $product_categories as $category ) {
			$children = $this->gather_categories( $category->term_id );

			if ( $category->term_id != get_option( 'default_product_cat' ) || count( $children ) > 0 ) {
				$result[] = array(
					'woo_id'    => $category->term_id,
					'parent_id' => $parent,
					'has_image' => get_term_meta( $category->term_id, 'thumbnail_id', true ),
				);

				$result = array_merge(
					$result,
					$children
				);
			}
		}
		return $result;
	}

	public function gather_products() {
		$products = get_posts(
			array(
				'post_type'      => 'product',
				'posts_per_page' => 2500,
				'fields'         => 'ids',
			)
		);

		$return = array();
		foreach ( $products as $id ) {
			$return[] = array(
				'woo_id' => $id,
			);
		}

		return $return;
	}
}
