<?php
/**
 * Syncs campaigns to WooCommerce as coupons
 *
 * @package flo-sync
 */

namespace NMFS\Sync\Items\Campaigns;

use DateInterval;
use DateTime;
use NMFS\Shared;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Sync\Abstract_Syncer_And_Cleaner;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use WC_Product_Query;

defined( 'ABSPATH' ) || exit;

/**
 * Class Sync
 */
class Sync_Campaigns extends Abstract_Syncer_And_Cleaner {

	/**
	 * Check if sync is enabled.
	 *
	 * @return bool
	 */
	public function is_enabled(): bool {
		return Campaigns::is_enabled();
	}

	/**
	 * Syncs campaigns to WooCommerce as coupons
	 *
	 * @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;
		}

		$campaigns = $current_backoffice_account->api->get_campaigns( $args );

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

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

		if ( is_array( $campaigns['list'] ) ) {
			$this->process_pulled_campaigns( $campaigns['list'] );
		}

		return true;
	}

	/**
	 * Syncs campaigns to WooCommerce as coupons
	 *
	 * @param array $api_list Array of campaigns.
	 */
	public function process_pulled_campaigns( array $api_list ): void {
		if ( empty( $api_list ) ) {
			return;
		}

		foreach ( $api_list as $campaign ) {
			if ( empty( $campaign['Campaign'] || ! is_array( $campaign['Campaign'] ) ) ) {
				continue;
			}

			$action = $this->process_pulled_campaign( $campaign['Campaign'] );

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

	/**
	 * Process campaign.
	 *
	 * @param array $campaign Campaign to process.
	 *
	 * @return string
	 */
	public function process_pulled_campaign( array $campaign ): string {
		$post_type = 'shop_coupon';

		if ( ! $campaign['Enabled'] ) {

			$deleted = Campaigns::delete_post(
				$campaign['Id'],
				$post_type,
				'campaign'
			);

			return $deleted
				? 'delete'
				: 'ignore';
		}

		if ( $campaign['MemberOnly'] ) {
			$deleted = Campaigns::delete_post(
				$campaign['Id'],
				$post_type,
				'campaign'
			);

			return $deleted
				? 'delete'
				: 'ignore';
		}

		if ( $campaign['MemberGroupName'] ) {
			$deleted = Campaigns::delete_post(
				$campaign['Id'],
				$post_type,
				'campaign'
			);

			return $deleted
				? 'delete'
				: 'ignore';
		}

		$end_time = DateTime::createFromFormat( 'Y-m-d\\TH:i:s', $campaign['EndTime'] );

		if ( ! $end_time || new DateTime() > $end_time ) {
			$deleted = Campaigns::delete_post(
				$campaign['Id'],
				$post_type,
				'campaign'
			);

			return $deleted
				? 'delete'
				: 'ignore';
		}

		$post = Campaigns::update_post(
			$campaign['Id'],
			$campaign['Name'],
			$post_type,
			'campaign'
		);

		if ( ! $post ) {
			return 'error';
		}

		// Add meta.
		update_post_meta( $post, 'discount_type', 'mf_type_' . $campaign['Type'] );
		update_post_meta( $post, 'mf_value_x', $campaign['ValueX'] );
		update_post_meta( $post, 'mf_value_y', $campaign['ValueY'] );
		update_post_meta( $post, 'mf_value', $campaign['Value'] );
		update_post_meta( $post, 'mf_value_is_global', $campaign['ValueIsGlobal'] );
		update_post_meta( $post, 'mf_priority', $campaign['Priority'] );
		update_post_meta( $post, 'mf_threshold_total', $campaign['ThresholdTotal'] );
		update_post_meta( $post, 'mf_threshold_total_include_this', $campaign['ThresholdTotalIncludeThis'] );
		update_post_meta( $post, 'mf_start_time', $campaign['StartTime'] );
		update_post_meta( $post, 'mf_end_time', $campaign['EndTime'] );

		$parts = $this->get_parts( $campaign['parts'], (bool) $campaign['ValueIsGlobal'], $campaign['Value'] );
		update_post_meta( $post, 'mf_parts_sku', wp_json_encode( $parts['sku'] ) );
		update_post_meta( $post, 'mf_parts_value', wp_json_encode( $parts['value'] ) );

		update_post_meta( $post, 'mf_end_date', $end_time->add( new DateInterval( 'P1D' ) )->format( 'Y-m-d' ) );
		update_post_meta( $post, 'mf_last_updated', time() );

		return 'add';
	}

	/**
	 * Clean old campaigns.
	 *
	 * @param bool $getting_all_data Whether sync is getting all data or not.
	 *
	 * @return void
	 */
	public function clean( bool $getting_all_data ): void {

		$post_type = 'shop_coupon';

		Shared::instance()->logger->log(
			'sync',
			'Cleaning campaigns',
			null,
			Log_Level_Enum::DEBUG
		);

		$posts = get_posts(
			array(
				'post_type'           => $post_type,
				'ignore_sticky_posts' => true,
				'fields'              => 'ids',
				'meta_query'          => array(
					'relation' => 'AND',
					array(
						'key'     => 'mf_id',
						'compare' => 'EXISTS',
					),
					array(
						'key'   => 'mf_type',
						'value' => 'campaign',
					),
					array(
						'key'     => 'mf_last_updated',
						'value'   => time() - MINUTE_IN_SECONDS,
						'compare' => '<',
					),
				),
			)
		);

		if ( ! empty( $posts ) ) {
			foreach ( $posts as $post_id ) {
				$mf_id = get_post_meta( $post_id, 'mf_id', true );
				Campaigns::delete_post( $mf_id, $post_type, 'campaign' );
			}
		}

		Shared::instance()->logger->log(
			'sync',
			'Done cleaning campaigns',
			null,
			Log_Level_Enum::DEBUG
		);
	}

	/**
	 * Get article set parts formatted for store in wp post meta.
	 *
	 * @param mixed $parts From API.
	 * @param bool  $value_is_global Whether to use global value or not.
	 * @param mixed $global_value Global value.
	 *
	 * @return array
	 */
	protected function get_parts( mixed $parts, bool $value_is_global, mixed $global_value ): array {
		$array = array(
			'sku'   => array(),
			'value' => array(),
		);

		if ( empty( $parts ) || ! is_array( $parts ) ) {
			return $array;
		}

		foreach ( $parts as $part ) {
			$part_value = $value_is_global
				? $global_value
				: $part['Value'];
			if ( 0 === $part['PartType'] ) {
				// Single SKU.
				if ( ! in_array( $part['Reference'], $array['sku'], true ) ) {
					$array['sku'][] = $part['Reference'];
				}
				$array['value'][ $part['Reference'] ] = $part_value;
			} elseif ( 2 === $part['PartType'] ) {
				// Category.
				$query    = new WC_Product_Query(
					array(
						'post_status'         => 'publish',
						'ignore_sticky_posts' => true,
						'nopaging'            => true,
						'tax_query'           => array(
							array(
								'taxonomy'         => 'product_cat',
								'field'            => 'name',
								'terms'            => $part['Reference'],
								'include_children' => true,
							),
						),
					)
				);
				$products = $query->get_products();

				if ( ! empty( $products ) ) {
					$skus = array_map(
						function ( $product ) {
							return $product->get_sku();
						},
						$products
					);
					foreach ( $skus as $sku ) {
						if ( ! in_array( $sku, $array['sku'], true ) ) {
							$array['sku'][] = $sku;
						}
						$array['value'][ $sku ] = $part_value;
					}
				}
			}
		}

		return $array;
	}

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

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

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