<?php
/**
 * Handles syncing of articles.
 *
 * @package flo-sync
 */

namespace NMFS\Sync\Items\Articles;

use NMFS\Admin\Settings_Fields;
use NMFS\Shared;
use NMFS\Sync\Items\Articles\Articles as Article_Functions;
use NMFS\Sync\Items\Articles\Models\Article_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Models\Hub_API_Properties;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Sync\Abstract_Syncer;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;

defined( 'ABSPATH' ) || exit;

/**
 * Class Sync
 */
class Sync_Articles extends Abstract_Syncer {

	/**
	 * Pull articles from API.
	 *
	 * @param array|null $args Arguments to pass to API, or null if no arguments.
	 * @param bool       $is_first_sync If this sync is the first page of the first Hub account.
	 */
	public function sync( ?array $args = null, bool $is_first_sync = false ): bool {
		$current_backoffice_account = $this->get_current_hub_account();

		if ( ! $current_backoffice_account ) {
			return false;
		}

		$articles = $current_backoffice_account->api->get_articles( $args );

		if (
			! isset( $articles['Total'] ) ||
			! isset( $articles['Page'] ) ||
			! isset( $articles['list'] )
		) {
			return false;
		}

		$this->update_status( 'page', $articles['Page'] );
		$this->update_status( 'total', $articles['Total'] );

		if ( is_array( $articles['list'] ) ) {
			foreach ( $articles['list'] as $article ) {

				if ( ! empty( $article['Article'] ) ) {

					if ( is_callable( $args['custom_callback'] ?? false ) ) {
						$action = call_user_func( $args['custom_callback'], $article['Article'] );
					} else {
						$action = $this->process_pulled_article( $article['Article'] );
					}

					if ( $action ) {
						$this->increase_status( $action );
					}
				}
			}
		}

		return true;
	}

	/**
	 * Process article from API.
	 *
	 * @param array $article Article.
	 *
	 * @return string|null Action, or null on failure.
	 */
	public function process_pulled_article( array $article ): ?string {
		$article_model = Article_Model_Factory::create_from_api( $article );

		Shared::instance()->logger->log(
			'sync',
			'Processing article ' . $article_model->sku,
			array(),
			Log_Level_Enum::DEBUG
		);

		if ( $this->is_shipping_article( $article_model->sku ) ) {
			return null;
		}

		$product_id = wc_get_product_id_by_sku( $article_model->sku );

		if (
			! $article_model->is_web_shop_article ||
			Hub_API_Properties::ARTICLE_TYPE_GOODS !== $article_model->hub_type
		) {
			// Skip product if it isn't a Web shop article, or if it isn't a goods article.
			Shared::instance()->logger->log(
				'sync',
				'Skip product that isn\'t a Web shop article or isn\'t a goods article',
				array(
					'product_id'          => $product_id,
					'sku'                 => $article_model->sku,
					'is_web_shop_article' => $article_model->is_web_shop_article,
					'ArticleType'         => $article_model->hub_type,
				),
				Log_Level_Enum::DEBUG
			);

			// Maybe set product status to draft if it already exists.
			Article_Functions::maybe_set_as_draft( $product_id );

			return null;
		}

		return $this->handle_article( $product_id, $article_model );
	}

	/**
	 * Stops handling of shipping articles.
	 *
	 * @param string $sku The SKU of the article to check.
	 *
	 * @return bool Returns true the article is a shipping article, false otherwise.
	 */
	private function is_shipping_article( string $sku ): bool {
		$current_account = $this->get_current_hub_account();

		if ( $current_account && $sku === $current_account->get_shipping_settings()->get_article_sku() ) {
			Shared::instance()->logger->log(
				'sync',
				'Skipping due to is shipping article',
				array( 'sku' => $sku ),
				Log_Level_Enum::DEBUG
			);
			return true;
		}

		return false;
	}

	/**
	 * Handle the processing of an article by updating or creating based on product ID.
	 *
	 * @param int           $product_id The ID of the product to update; if 0, a new article is created.
	 * @param Article_Model $article_model Article model.
	 *
	 * @return string|null Returns 'update' if the article is updated, or null if a new article is created.
	 */
	private function handle_article( int $product_id, Article_Model $article_model ): ?string {

		if ( empty( $product_id ) ) {
			return $this->handle_new_article( $article_model );
		}

		update_post_meta( $product_id, Articles::META_KEY_TMP_LOCK, 'yes' );
		$article_model->store_in_wc();
		Shared::instance()->logger->log(
			'sync',
			'Product updated in WC',
			array(
				'sku' => $article_model->sku,
			),
			Log_Level_Enum::DEBUG
		);
		delete_post_meta( $product_id, Articles::META_KEY_TMP_LOCK, 'yes' );

		Articles::update_product_sync_status( $product_id );

		Shared::instance()->logger->log(
			'sync',
			"Done processing article $article_model->sku",
			array(),
			Log_Level_Enum::DEBUG
		);

		return 'update';
	}

	/**
	 * Handle the creation of a new article and its synchronization status.
	 *
	 * @param Article_Model $article_model Article model.
	 *
	 * @return string|null Returns 'add' if the article is successfully processed, null otherwise.
	 */
	private function handle_new_article( Article_Model $article_model ): ?string {
		if ( Articles::PRODUCT_STATUS_PUBLISH !== $article_model->publish_status ) {
			// Skip product if it isn't active in Hub.
			Shared::instance()->logger->log(
				'sync',
				'Skip product due to inactive status',
				array(
					'sku'            => $article_model->sku,
					'article_status' => $article_model->publish_status,
				),
				Log_Level_Enum::DEBUG
			);

			return null;
		}

		$product_id = $article_model->store_in_wc();

		if ( empty( $product_id ) ) {
			Shared::instance()->logger->log(
				'sync',
				'Failed to create product',
				array(
					'sku' => $article_model->sku,
				),
				Log_Level_Enum::ERROR
			);

			return null;
		}

		Shared::instance()->logger->log(
			'sync',
			'Product created in WC',
			array( 'sku' => $article_model->sku ),
			Log_Level_Enum::DEBUG
		);

		Articles::update_product_sync_status( $product_id );

		Shared::instance()->logger->log(
			'sync',
			"Done processing article $article_model->sku",
			array(),
			Log_Level_Enum::DEBUG
		);

		return 'add';
	}

	/**
	 * Disables article save action in article sync loop.
	 *
	 * @param array|null $args API params passed by reference.
	 */
	public function before_sync_loop( ?array &$args ): void {
		Articles::set_hook_save_article( false );
	}

	/**
	 * Checks if sync is enabled.
	 *
	 * @return bool
	 */
	public function is_enabled(): bool {
		if ( Shared::instance()->admin->settings->is_setting_enabled( Settings_Fields::SYNC_ONLY_STOCK ) ) {
			Shared::instance()->logger->log(
				'sync',
				'Sync only product stock amount is enabled, skipping article sync',
				array(),
				Log_Level_Enum::DEBUG
			);
			return false;
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_display_name(): string {
		return __( 'Articles', 'flo-sync' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_last_synced_wp_option_name(): string {
		return 'mf_article_sync_last_updated';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_last_synced_api_parameter(): ?string {
		return 'ArticleInfoUpdatedSince';
	}
}
