<?php
/**
 * Main code for article sync.
 *
 * @package flo-sync
 */

namespace NMFS\Sync\Items\Articles;

use NMFS\Admin\Settings_Fields;
use NMFS\Shared;
use NMFS\Sync\Admin_Status;
use NMFS\Sync\Items\Articles\Models\Article_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Main_API;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Models\Hub_API_Properties;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use WC_Product;
use WC_Product_Variable;

defined( 'ABSPATH' ) || exit;

/**
 * Class for article sync.
 */
class Articles {

	public const PRODUCT_STATUS_PUBLISH          = 'publish';
	public const PRODUCT_STATUS_DRAFT            = 'draft';
	public const META_KEY_ORDER_TICKET_DIALOGUE  = 'nmf_order_ticket_dialogue';
	public const META_KEY_ORDER_TICKET_TEXT      = 'nmf_order_ticket_text';
	public const META_KEY_BARCODE                = '_custom_product_barcodes_field';
	public const META_KEY_GROUP_ID               = 'mf_group_id';
	public const META_KEY_IMAGE_ID               = '_mf_sync_image_id';
	public const META_KEY_IMAGE_NAME             = '_mf_sync_image_name';
	public const META_KEY_TMP_LOCK               = 'nmf_temp_lock';
	public const CORE_META_KEY_MANAGE_STOCK      = '_manage_stock';
	public const CORE_META_KEY_DOWNLOADABLE      = '_downloadable';
	public const CORE_META_KEY_VIRTUAL           = '_virtual';
	public const CORE_META_KEY_STOCKSTATUS       = '_stock_status';
	public const CORE_META_VALUE_IN_STOCK        = 'instock';
	public const CORE_TAX_PROD_TYPE_VALUE_SIMPLE = 'simple';
	public const CORE_TAX_PROD_TYPE              = 'product_type';

	/**
	 * Holds the status of whether the save article hooks are enabled.
	 *
	 * @var bool $save_article_hook_enabled
	 */
	public static bool $save_article_hook_enabled;

	/**
	 * Construct class.
	 */
	public static function setup(): void {
		self::set_hook_save_article( true );
	}

	/**
	 * Adds or removes the hook for saving articles.
	 *
	 * @param bool $add_hook Whether to add or remove the hooks.
	 */
	public static function set_hook_save_article( bool $add_hook ): void {
		if (
			$add_hook &&
			! Shared::instance()->admin->settings->is_setting_enabled( Settings_Fields::SYNC_ONLY_STOCK )
		) {
			add_action( 'woocommerce_new_product', array( self::class, 'save_product' ), 10, 2 );
			add_action( 'woocommerce_update_product', array( self::class, 'save_product' ), 10, 2 );
		} else {
			remove_action( 'woocommerce_new_product', array( self::class, 'save_product' ) );
			remove_action( 'woocommerce_update_product', array( self::class, 'save_product' ) );
		}

		self::$save_article_hook_enabled = $add_hook;
	}

	/**
	 * Updates product sync status.
	 *
	 * @param int $product_id Woo product ID.
	 */
	public static function update_product_sync_status( int $product_id ): void {
		$product = wc_get_product( $product_id );

		if ( $product instanceof WC_Product ) {
			$article_model = Article_Model_Factory::create_from_wc( $product );
			$new_hash      = $article_model->generate_article_hash();

			Article_Sync_Meta_Helpers::update_hash( $product_id, $new_hash );
			Article_Sync_Meta_Helpers::set_sync_status_synced( $product_id );

			Admin_Status::set_sync_status_ok( $product_id );
		}
	}

	/**
	 * Maybe set product as draft.
	 *
	 * @param int $product_id Woo product ID.
	 */
	public static function maybe_set_as_draft( int $product_id ): void {
		if ( ! Shared::instance()->admin->settings->is_setting_enabled( Settings_Fields::SYNC_PRODUCT_STATUS ) ) {
			return;
		}

		if ( ! empty( $product_id ) ) {
			$product = wc_get_product( $product_id );
			if ( 'publish' === $product->get_status( 'edit' ) ) {
				if ( 'simple' === $product->get_type() ) {
					$product->set_status( 'draft' );
					$product->save();

					Shared::instance()->logger->log(
						'sync',
						'Product status set to draft.',
						array(
							'product_id' => $product_id,
						),
						Log_Level_Enum::DEBUG
					);
				} elseif ( 'variation' === $product->get_type() ) {
					$product->set_status( 'private' );
					$product->save();

					Shared::instance()->logger->log(
						'sync',
						'Product variation status set to private.',
						array(
							'product_id' => $product_id,
						),
						Log_Level_Enum::DEBUG
					);
				}
			}
		}
	}

	/**
	 * Function hooked to Woo product save.
	 *
	 * @param int        $product_id The product ID.
	 * @param WC_Product $product    The product.
	 */
	public static function save_product( int $product_id, WC_Product $product ): void {
		if ( ! WP_Repository::get_current_main_account() ) {
			Shared::instance()->logger->log(
				'sync',
				'Save product skipped due to no main account.',
				array(
					'product_id' => $product_id,
				),
				Log_Level_Enum::NOTICE
			);

			return;
		}

		if ( get_post_meta( $product_id, self::META_KEY_TMP_LOCK, true ) ) {
			Shared::instance()->logger->log(
				'sync',
				'Save product skipping due to temporary lock.',
				array(
					'product_id' => $product_id,
				),
				Log_Level_Enum::NOTICE
			);

			return;
		}

		$article_models = self::get_article_models_for_updated_product( $product );

		if ( empty( $article_models ) ) {
			Shared::instance()->logger->log(
				'sync',
				'No article models to sync.',
				array(
					'product_id' => $product_id,
				),
				Log_Level_Enum::DEBUG
			);
			return;
		}

		// Remove any empty article models.
		$article_models = array_filter( $article_models );

		foreach ( $article_models as $article_model ) {
			self::process_article_model( $article_model );
		}
	}

	/**
	 * Get article models for updated product.
	 *
	 * @param WC_Product $product Woo product.
	 *
	 * @return Article_Model[]
	 */
	private static function get_article_models_for_updated_product( WC_Product $product ): array {
		$product_id = $product->get_id();

		/**
		 * Article models.
		 *
		 * @var Article_Model[] $article_models
		 */
		$article_models = array();
		$ignore_status  = array( self::PRODUCT_STATUS_DRAFT, 'auto-draft' );
		$product_status = $product->get_status();

		if ( $product instanceof WC_Product_Variable && ! in_array( $product_status, $ignore_status, true ) ) {

			$variations = $product->get_children();
			$parent_sku = $product->get_sku();

			if ( is_array( $variations ) && ! empty( $variations ) ) {
				foreach ( $variations as $variant_id ) {
					$variation_model = Article_Model_Factory::create_from_wc_variation( $product, $variant_id );

					if ( ! empty( $variation_model ) ) {
						// Skip variations with the same SKU as parent product.
						if ( $variation_model->sku === $parent_sku ) {
							Shared::instance()->logger->log(
								'sync',
								'Skipping variation with same SKU as parent product.',
								array(
									'product_id'   => $product_id,
									'variation_id' => $variant_id,
									'sku'          => $variation_model->sku,
								),
								Log_Level_Enum::NOTICE
							);
							continue;
						}

						$article_models[ $variant_id ] = $variation_model;
					}
				}
			}
		} else {
			$article_models[ $product_id ] = Article_Model_Factory::create_from_wc( $product );
		}

		return $article_models;
	}

	/**
	 * Process article model before sync.
	 *
	 * @param Article_Model $article_model The article model.
	 */
	private static function process_article_model( Article_Model $article_model ): void {
		$wc_product_id = $article_model->wc_product->get_id();
		Shared::instance()->logger->log(
			'sync',
			'Saving article',
			array(
				'article' => $article_model,
			),
			Log_Level_Enum::DEBUG
		);

		$last_sync_sku = Article_Sync_Meta_Helpers::get_last_sync_sku( $wc_product_id );

		$article_exists_in_hub = false;

		// Only consider existing sync data if SKU is the same.
		if ( $last_sync_sku === $article_model->sku ) {

			if ( $article_model->stored_hash ) {
				$article_exists_in_hub = true;
			}

			$new_hash = $article_model->generate_article_hash();

			if ( $article_model->stored_hash === $new_hash ) {
				Shared::instance()->logger->log(
					'sync',
					'Product hash verification failed, same as last sync.',
					array(
						'article'       => $article_model->get_values_for_hash(),
						'last_sync_sku' => $last_sync_sku,
					),
					Log_Level_Enum::NOTICE
				);

				return;
			}
		} else {
			$new_hash = $article_model->generate_article_hash();
		}

		if ( ! $article_model->is_full_product() ) {
			Shared::instance()->logger->log(
				'sync',
				'Trying to sync article without all required data',
				array(
					'product_id' => $wc_product_id,
					'article'    => $article_model,
				),
				Log_Level_Enum::ERROR
			);
			return;
		}

		$main_api = new Main_API();

		$article_api_data = $article_model->get_data_to_send_to_api( ! $article_exists_in_hub );
		$wc_product       = $article_model->wc_product;

		/**
		 * Filter the article API data before sending an article in Hub.
		 *
		 * @param array      $article_api_data The article API data.
		 * @param WC_Product $wc_product       The WooCommerce product.
		 * @param bool       $article_exists_in_hub Whether the article already exists in Hub.
		 *
		 * @return array
		 */
		$article_api_data = apply_filters( 'northmill_sync_save_article_api_data', $article_api_data, $wc_product, $article_exists_in_hub );

		if ( $article_exists_in_hub ) {
			$response = $main_api->put_articles( $article_api_data );
		} else {
			$response = $main_api->post_articles( $article_api_data );
		}

		if ( $response ) {
			Article_Sync_Meta_Helpers::update_hash( $wc_product_id, $new_hash );
			Article_Sync_Meta_Helpers::set_sync_status_synced( $wc_product_id );
			Article_Sync_Meta_Helpers::update_last_sync_response( $wc_product_id, $response );

			Shared::instance()->logger->log(
				'sync',
				'Product pushed and meta data updated.',
				array(
					'product_id' => $wc_product_id,
				),
				Log_Level_Enum::DEBUG
			);

			Admin_Status::set_sync_status_from_response( $wc_product_id, $response );
		}
	}
}
