<?php

namespace MOWCPS\Common\Handlers\SyncHandler;

use MOWCPS\Common\Handlers\SupplierHandler\Supplier_Handler;
use MOWCPS\Common\Models\Sync_Details_DAO;
use MOWCPS\Common\Handlers\SyncAnalytics\Sync_Analytics;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Models\Connection_Model;
use MOWCPS\Common\Handlers\SyncHandler\Inbound_Sync_Handler;
use MOWCPS\Common\Handlers\SyncHandler\Interdependant_Sync_Handler;
use MOWCPS\Common\Handlers\ConnectExternalSupplierHandler\Connect_External_Supplier_Handler;
use MOWCPS\Common\Handlers\ConnectExternalSupplierHandler\Connect_FTP_Handler;
use MOWCPS\Common\Utils\Utils;
use MOWCPS\Common\Services\ProductDataServices\Product_Data_Services;
use MOWCPS\Common\Utils\Model_Utils;
use MOWCPS\Common\Services\Cron\Cron_Manager;
use MOWCPS\Common\Constants\Plugin_Filters;
use MOWCPS\Common\Services\ProductSync\SyncType\Simple\Sync_Simple_Products;
use MOWCPS\Common\Models\Product_Sync_Audit_Model;
use MOWCPS\Common\Models\Setting_Model;
use MOWCPS\Common\Models\Sync_Queue_Model;
use MOWCPS\Common\Utils\Sync_Utils;
use MOWCPS\Common\Controllers\Cron_Controller;
use MOWCPS\Common\Services\Logging\Supplier_Logger;
use MOWCPS\Common\Services\Auditing\Supplier_Audit_Logger;
use MOWCPS\Common\Services\ReadFileData\Read_File_Data;
use MOWCPS\Common\Services\Preview\Preview_Filter_Service;
use MOWCPS\Common\Models\Preview_Filters_Model;
use MOWCPS\Common\Services\FTP\FTP_Service;

/**
 * Abstract class for handling product sync operations.
 *
 * Provides a base structure for supplier-specific sync handlers.
 * All sync handler classes should extend this class and implement required methods.
 */
abstract class Abstract_Sync_Handler {

	/**
	 * Supplier ID.
	 *
	 * @var int
	 */
	public $supplier_id;

	/**
	 * Connection ID.
	 *
	 * @var int
	 */
	public $connection_id;

	/**
	 * Handler.
	 *
	 * @var object
	 */
	public $sync_handler;

	/**
	 * Connection operation.
	 *
	 * @var object
	 */
	public $sync_operation;

	/**
	 * Batch number.
	 *
	 * @var int
	 */
	public $batch_number = 1;

	/**
	 * Page number.
	 *
	 * @var int
	 */
	public $page_number = 1;

	/**
	 * Product data from API.
	 *
	 * @var array
	 */
	public $product_data_from_api;

	/**
	 * Products sync type.
	 *
	 * @var object
	 */
	public $products_sync_type;

	/**
	 * Configuration.
	 *
	 * @var array
	 */
	public $config = array();

	/**
	 * Schedule ID.
	 *
	 * @var int
	 */
	public $schedule_id = null;

	/**
	 * Product data.
	 *
	 * @var mixed
	 */
	public $product_data;

	/**
	 * Sync analytics object.
	 *
	 * @var Sync_Analytics|null
	 */
	public $sync_analytics;

	/**
	 * Constructor.
	 *
	 * @param int $supplier_id   Supplier ID.
	 * @param int $connection_id Connection ID. Default 0.
	 * @param int $schedule_id   Schedule ID. Default null.
	 * @param int $batch_number  Batch number. Default 1.
	 * @param int $page_number   Page number. Default 1.
	 */
	public function __construct( $supplier_id, $connection_id = 0, $schedule_id = null, $batch_number = 1, $page_number = 1 ) {
		$this->supplier_id   = $supplier_id;
		$this->connection_id = $connection_id;
		$this->batch_number  = $batch_number;
		$this->page_number   = $page_number;
		$this->schedule_id   = $schedule_id;
	}

	/**
	 * Start manual sync.
	 */
	public function start_sync() {
		// Keep schedule_id aligned with DAO so audit session paths, analytics, and finalize_sync use the same key.
		$this->schedule_id    = Sync_Details_DAO::get_instance()->get_schedule_id();
		$connection_sync_type = Sync_Details_DAO::get_instance()->get_connection_handler_type( $this->connection_id );
		Sync_Details_DAO::get_instance()->set_connection_id( $this->connection_id );

		Supplier_Logger::debug( $this->supplier_id, 'Configuring sync settings for connection: ' . $this->connection_id );

		// Allow plugins to register custom sync handlers via filter.
		$sync_handlers = apply_filters( 'mo_sync_handlers', array() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- prefix already given.

		// Check if a custom handler is registered for this sync type.
		if ( isset( $sync_handlers[ $connection_sync_type ] ) ) {
			$handler_class = $sync_handlers[ $connection_sync_type ];
			// Verify the class exists and extends our abstract class.
			if ( class_exists( $handler_class ) && is_subclass_of( $handler_class, 'MOWCPS\Common\Handlers\SyncHandler\Abstract_Sync_Handler' ) ) {
				$this->sync_handler = new $handler_class( $this->supplier_id, $this->connection_id );
			}
		} else {
			$this->sync_handler = new Interdependant_Sync_Handler( $this->supplier_id, $this->connection_id, $this->schedule_id, $this->batch_number, $this->page_number );
		}

		$connection_operator = Sync_Details_DAO::get_instance()->get_connection_sync_type( $this->connection_id );

		$sync_end_status = '';

		// Allow plugins to register custom sync handlers via filter.
		$sync_operator = apply_filters( Plugin_Filters::FILTER_SYNC_OPERATIONS, array() );

		// Check if a custom handler is registered for this sync type.
		if ( isset( $sync_operator[ $connection_operator ] ) ) {
			$handler_class = $sync_operator[ $connection_operator ];

			// Verify the class exists and extends our abstract class.
			if ( class_exists( $handler_class ) && is_subclass_of( $handler_class, 'MOWCPS\Common\Handlers\SyncHandler\Abstract_Sync_Handler' ) ) {
				$this->sync_operation = new $handler_class( $this->supplier_id, $this->connection_id, $this->sync_handler, $this->batch_number );
			}
		} else {
			$this->sync_operation = new Inbound_Sync_Handler( $this->supplier_id, $this->connection_id, $this->schedule_id, $this->batch_number, $this->page_number, $this->sync_handler );
		}

		$sync_end_status = $this->sync_operation->handle_sync();

		return $sync_end_status;
	}

	/**
	 * Sync the products based on the sync type.
	 *
	 * @param array $product_data Data of the product to sync.
	 * @return string
	 */
	protected function sync_products( $product_data ) {
		// get sync structure.
		$sync_structure = Sync_Details_DAO::get_instance()->get_sync_structure( $this->connection_id );

		// Allow plugins to register custom sync handlers via filter.
		$sync_handlers = apply_filters( Plugin_Filters::FILTER_SYNC_STRUCTURE, array() );

		// Check if a custom handler is registered for this sync type.
		if ( isset( $sync_handlers[ $sync_structure ] ) ) {
			$handler_class = $sync_handlers[ $sync_structure ];

			// Verify the class exists and extends our abstract class.
			if ( class_exists( $handler_class ) && is_subclass_of( $handler_class, 'MOWCPS\Common\Handlers\SyncHandler\Abstract_Sync_Handler' ) ) {
				$this->products_sync_type = new $handler_class( $this->supplier_id, $this->schedule_id, $this->connection_id );
			}
		} else {
			$this->products_sync_type = new Sync_Simple_Products( $this->supplier_id, $this->schedule_id, $this->connection_id );
		}

		// Assigning products data for the sync.
		$this->products_sync_type->product_data = $product_data;

		// Assigning respective analytics object.
		$this->products_sync_type->sync_analytics = $this->get_current_batch_analytics();
		$this->products_sync_type->sync_analytics->additional_sync_data['product_per_page'] = (int) get_option( 'mowcps_' . $this->connection_id . '_filter_api_response_count', 0 );
		$this->products_sync_type->sync_analytics->sync_status_code                         = Constants::SYNC_IN_PROGRESS;
		$this->products_sync_type->sync_analytics->sync_status_message                      = Constants::SYNC_IN_PROGRESS;
		$this->products_sync_type->sync_analytics->save();
		Sync_Details_DAO::get_instance()->set_batch_analytics( $this->products_sync_type->sync_analytics );
		// Setting supplier sync analytics object.( supplier level analytics object ).
		$supplier_sync_analytics                      = Sync_Details_DAO::get_instance()->get_supplier_analytics( $this->supplier_id, $this->schedule_id );
		$supplier_sync_analytics->sync_status_message = Constants::SYNC_IN_PROGRESS;
		$supplier_sync_analytics->sync_status_code    = Constants::SYNC_IN_PROGRESS;
		$supplier_sync_analytics->save();
		// Setting sync analytics object.( connection level analytics object ).
		$this->sync_analytics                      = Sync_Details_DAO::get_instance()->get_connection_analytics( $this->supplier_id, $this->schedule_id, $this->connection_id );
		$this->sync_analytics->sync_status_code    = Constants::SYNC_IN_PROGRESS;
		$this->sync_analytics->sync_status_message = Constants::SYNC_IN_PROGRESS;
		$this->sync_analytics->save();

		// Initiating sync loop based on sync structure.
		$sync_end_status = $this->products_sync_type->start_sync_process();
		$sync_type       = Sync_Details_DAO::get_instance()->get_connection_sync_type( $this->connection_id );
		if ( Constants::OUTBOUND === $sync_type ) {
			$this->products_sync_type->sync_analytics->additional_sync_data = array();
			$this->products_sync_type->sync_analytics->save();
		}
		// get detailed sync status.
		$sync_end_status = $this->get_detailed_sync_status( $sync_end_status, $sync_structure );

		return $sync_end_status;
	}


	/**
	 * Get sync status in detail.
	 *
	 * @param string $sync_end_status    End status of the current sync.
	 * @param object $products_sync_type Sync type.
	 * @return string
	 */
	public function get_detailed_sync_status( $sync_end_status, $products_sync_type = null ) {
		$batch_flag     = Sync_Details_DAO::get_instance()->get_batch_flag();
		$batch_size     = Sync_Details_DAO::get_instance()->get_batch_size();
		$page_flag      = Sync_Details_DAO::get_instance()->get_page_flag();
		$sync_analytics = Sync_Details_DAO::get_instance()->get_connection_analytics( $this->supplier_id, $this->schedule_id, $this->connection_id );

		// Check if preview filter is active and connection is primary or FILE type.
		$connection_details = Sync_Details_DAO::get_instance()->get_connection_details( $this->connection_id );
		$preview_filter     = Preview_Filters_Model::get_by_supplier_id( $this->supplier_id );
		$connection_type    = $connection_details['type'] ?? '';
		$is_primary_or_file = ( '1' === ( $connection_details['is_primary'] ?? '' ) ) || ( Constants::CONNECTION_TYPE_FILE === $connection_type );
		$file_sync_details  = Setting_Model::get_by_connection_and_operation( $this->connection_id, 'file-sync', array( 'configuration' => true ) );
		$file_in_batch      = ! empty( $file_sync_details['configuration']['file_in_batch'] ) && Constants::CONFIGURATION_ACTIVE === $file_sync_details['configuration']['file_in_batch'];

		if ( $preview_filter && $preview_filter['is_active'] && $is_primary_or_file && ( Constants::PARENT_PRODUCT_IDENTIFIER_VARIABLE_SYNC !== $products_sync_type || $file_in_batch ) ) {
			$products_parsed  = $sync_analytics->parsed_success + $sync_analytics->parsed_failed;
			$products_to_sync = Preview_Filter_Service::get_filter_product_count( $this->supplier_id );
			if ( $products_parsed === $products_to_sync ) {
				$product_operations    = Setting_Model::get_by_connection_and_operation( $this->connection_id, 'product-operations', array( 'configuration' => true ) );
				$discard_products_from = isset( $product_operations['configuration']['unavailable_products']['products_of'] ) ? $product_operations['configuration']['unavailable_products']['products_of'] : 'none';

				if ( 'none' !== $discard_products_from ) {
					return Constants::DISCARD_PRODUCTS;
				}
				return Constants::COMPLETED_SYNC_RUNNING_STATUS;
			}
		}

		if ( Constants::SYNC_ABORTED !== $sync_end_status && Constants::SYNC_PAUSED !== $sync_end_status ) {
			$product_array_size = (int) get_option( 'mowcps_' . $this->connection_id . '_api_response_count', 0 );

			$new_batch_number = $this->batch_number;

			if ( has_filter( Plugin_Filters::FILTER_ALTER_PRODUCT_SYNC_AUDIT ) ) {
				$audit            = apply_filters(
					Plugin_Filters::FILTER_ALTER_PRODUCT_SYNC_AUDIT,
					array(
						'event_specifier'  => Plugin_Filters::EVENT_ALTER_BATCH_NUMBER,
						'supplier_id'      => $this->supplier_id,
						'new_batch_number' => $new_batch_number,
					)
				);
				$new_batch_number = $audit['new_batch_number'];
			}

			$loop_size = true === $batch_flag ? ( $new_batch_number * $batch_size ) : $product_array_size;
			$loop_size = $loop_size > $product_array_size ? $product_array_size : $loop_size;

			// Check connection type for FILE connections to handle batching differently.
			$connection_details = Sync_Details_DAO::get_instance()->get_connection_details( $this->connection_id );
			$connection_type    = $connection_details['type'] ?? 'api';

			// For FILE connections in batch mode, handle batch completion.
			// If product_array_size is 0, we've reached the end of the file - mark as completed to prevent infinite loops.
			// If product_array_size > 0, mark as batch completed (handles cases where batch returns fewer products than batch_size).
			if ( true === $batch_flag && Constants::CONNECTION_TYPE_FILE === $connection_type ) {
				if ( ! empty( $products_sync_type ) ) {
					$sync_analytics->save();
				}
				if ( $product_array_size > 0 ) {
					// Got products: mark batch as completed.
					$sync_end_status = 'Batch ' . $this->batch_number . ' completed';
				} else {
					// Got 0 products: end of file reached - mark sync as completed to prevent infinite loops.
					$sync_end_status = 'Sync Completed';
				}
			} elseif ( ( true === $batch_flag && ( $product_array_size !== $loop_size ) ) || ( true === $batch_flag && 0 !== $product_array_size ) ) {
				if ( ! empty( $products_sync_type ) ) {
					$sync_analytics->save();
				}
				$sync_end_status = 'Batch ' . $this->batch_number . ' completed';
			} elseif ( true === $page_flag && ( 0 !== $product_array_size ) ) {
				if ( ! empty( $products_sync_type ) ) {
					$sync_analytics->save();
				}
				$sync_end_status = 'Page ' . $this->page_number . ' completed';
			} else {
				$product_operations    = Setting_Model::get_by_connection_and_operation( $this->connection_id, 'product-operations', array( 'configuration' => true ) );
				$discard_products_from = isset( $product_operations['configuration']['unavailable_products']['products_of'] ) ? $product_operations['configuration']['unavailable_products']['products_of'] : 'none';

				if ( 'none' !== $discard_products_from ) {
					$sync_end_status = Constants::DISCARD_PRODUCTS;
				} elseif ( has_filter( Plugin_Filters::FILTER_ALTER_PRODUCT_SYNC_AUDIT ) ) {
						$audit           = apply_filters(
							Plugin_Filters::FILTER_ALTER_PRODUCT_SYNC_AUDIT,
							array(
								'event_specifier' => Plugin_Filters::EVENT_ALTER_SYNC_END_STATUS,
								'supplier_id'     => $this->supplier_id,
								'schedule_id'     => $this->schedule_id,
								'connection_id'   => $this->connection_id,
								'batch_number'    => $this->batch_number,
								'sync_end_status' => $sync_end_status,
							)
						);
						$sync_end_status = $audit['sync_end_status'];
				} else {
					$sync_end_status = 'Sync Completed';
				}
			}
		}

		// Allow suppliers to modify sync_end_status before final return (e.g., to override "Page X completed" when connection is actually done).
		if ( has_filter( Plugin_Filters::FILTER_ALTER_PRODUCT_SYNC_AUDIT ) ) {
			$final_status_request = apply_filters(
				Plugin_Filters::FILTER_ALTER_PRODUCT_SYNC_AUDIT,
				array(
					'event_specifier' => Plugin_Filters::EVENT_ALTER_SYNC_END_STATUS_FINAL,
					'supplier_id'     => $this->supplier_id,
					'schedule_id'     => $this->schedule_id,
					'connection_id'   => $this->connection_id,
					'batch_number'    => $this->batch_number,
					'page_number'     => $this->page_number,
					'sync_end_status' => $sync_end_status,
				)
			);
			$sync_end_status      = $final_status_request['sync_end_status'];
		}

		return $sync_end_status;
	}

	/**
	 * Check supplier sync process.
	 */
	public function check_supplier_sync_running_status() {
		$supplier_sync_running_status = $this->sync_running_status( $this->supplier_id, $this->schedule_id );
		if ( 'off' === $supplier_sync_running_status ) {
			// Check if this is an outbound sync.
			$sync_type = Sync_Details_DAO::get_instance()->get_connection_sync_type( $this->connection_id );

			if ( Constants::OUTBOUND === $sync_type ) {
				Supplier_Logger::debug( $this->supplier_id, 'Sync status is OFF for outbound sync - checking supplier overall status' );

				// Get supplier's overall sync analytics to check if sync completed successfully.
				$supplier_sync_analytics = new Sync_Analytics( $this->supplier_id, $this->schedule_id );
				$supplier_sync_analytics->set_analytics_object( Product_Sync_Audit_Model::get_sync_audit( $this->supplier_id, $this->schedule_id ) );

				$supplier_status = $supplier_sync_analytics->sync_status_code;
				Supplier_Logger::debug( $this->supplier_id, 'Supplier overall status: ' . $supplier_status );

				// If supplier sync completed successfully, don't call stop_sync - this is normal completion.
				if ( Constants::COMPLETED_SYNC_RUNNING_STATUS === $supplier_status ) {
					Supplier_Logger::debug( $this->supplier_id, 'Supplier sync completed successfully - skipping stop_sync call' );
					return;
				}
			}
			Cron_Controller::stop_sync(
				array(
					'supplier_id' => $this->supplier_id,
					'schedule_id' => $this->schedule_id,
				)
			);
			return;
		}
	}


	/**
	 * End sync.
	 *
	 * @param string $sync_end_status
	 *
	 * @return void
	 */
	public function end_sync( $sync_end_status ) {
		$sync_analytics = new Sync_Analytics( $this->supplier_id, $this->schedule_id, $this->connection_id );
		$sync_analytics->set_analytics_object( Product_Sync_Audit_Model::get_sync_audit( $this->supplier_id, $this->schedule_id, $this->connection_id ) );
		$supplier_sync_analytics = new Sync_Analytics( $this->supplier_id, $this->schedule_id );
		$supplier_sync_analytics->set_analytics_object( Product_Sync_Audit_Model::get_sync_audit( $this->supplier_id, $this->schedule_id ) );
		'' !== $sync_end_status ? $sync_analytics->sync_status_message          = $sync_end_status : '';
		'' !== $sync_end_status ? $supplier_sync_analytics->sync_status_message = $sync_end_status : '';
		if ( strpos( $sync_end_status, Constants::CONNECTION_SYNC_COMPLETED ) !== false ) { // Steps to be executed when connection sync process is completed.
			$sync_analytics->sync_end_time = time();
			$sync_analytics->initiate_sync = false;
			if ( Constants::FILE_DOWNLOAD_FAILED !== $sync_analytics->sync_status_code ) {
				$sync_analytics->sync_status_code = Constants::COMPLETED_SYNC_RUNNING_STATUS;
			}
			$sync_analytics->sync_status_message          = Constants::CONNECTION_SYNC_COMPLETED;
			$supplier_sync_analytics->sync_status_code    = Constants::SYNC_IN_PROGRESS;
			$supplier_sync_analytics->sync_status_message = Constants::SYNC_IN_PROGRESS;
		} elseif ( strpos( $sync_end_status, 'Sync Completed' ) !== false ) { // Steps to be executed when sync process is completed.
			$sync_analytics->sync_end_time = time();
			if ( Constants::FILE_DOWNLOAD_FAILED !== $sync_analytics->sync_status_code ) {
				$sync_analytics->sync_status_code = Constants::COMPLETED_SYNC_RUNNING_STATUS;
			}
			$supplier_sync_analytics->sync_end_time    = time();
			$supplier_sync_analytics->sync_status_code = Constants::COMPLETED_SYNC_RUNNING_STATUS;
			$sync_analytics->initiate_sync             = false;
			$supplier_sync_analytics->initiate_sync    = false;
			Product_Sync_Audit_Model::update_sync_running_status( $this->supplier_id, $this->schedule_id, Constants::SYNC_RUNNING_STATUS_OFF );
			Sync_Utils::cleanup_all_batch_files_for_supplier( $this->supplier_id );

			// Finalize audit and generate PDF (connection context from last connection in this path).
			$audit_summary = array_merge(
				array(
					'total_parsed'  => (int) $supplier_sync_analytics->total_product_parsed,
					'total_success' => (int) $supplier_sync_analytics->product_saved_success,
					'total_failed'  => (int) $supplier_sync_analytics->product_saved_fail,
					'total_added'   => (int) ( $supplier_sync_analytics->additional_sync_data['total_added'] ?? 0 ),
					'total_updated' => (int) ( $supplier_sync_analytics->additional_sync_data['total_updated'] ?? 0 ),
				),
				Supplier_Audit_Logger::connection_context_for_audit( (int) $this->connection_id )
			);
			Supplier_Audit_Logger::finalize_sync( $this->supplier_id, $this->schedule_id, $audit_summary );
		} elseif ( strpos( $sync_end_status, 'Sync Aborted' ) !== false ) { // Steps to be executed when sync process is aborted.
			$sync_analytics->sync_end_time             = time();
			$supplier_sync_analytics->sync_end_time    = time();
			$sync_analytics->sync_status_code          = Constants::SYNC_ABORTED;
			$supplier_sync_analytics->sync_status_code = Constants::SYNC_ABORTED;
			$sync_analytics->initiate_sync             = false;
			$supplier_sync_analytics->initiate_sync    = false;
			Product_Sync_Audit_Model::update_sync_running_status( $this->supplier_id, $this->schedule_id, Constants::SYNC_RUNNING_STATUS_OFF );
			Sync_Utils::cleanup_all_batch_files_for_supplier( $this->supplier_id );

			$audit_summary_abort = array_merge(
				array(
					'total_parsed'  => (int) $supplier_sync_analytics->total_product_parsed,
					'total_success' => (int) $supplier_sync_analytics->product_saved_success,
					'total_failed'  => (int) $supplier_sync_analytics->product_saved_fail,
					'total_added'   => (int) ( $supplier_sync_analytics->additional_sync_data['total_added'] ?? 0 ),
					'total_updated' => (int) ( $supplier_sync_analytics->additional_sync_data['total_updated'] ?? 0 ),
					'audit_end'     => 'aborted',
				),
				Supplier_Audit_Logger::connection_context_for_audit( (int) $this->connection_id )
			);
			Supplier_Audit_Logger::finalize_sync( $this->supplier_id, $this->schedule_id, $audit_summary_abort );
		} elseif ( strpos( $sync_end_status, Constants::SYNC_PAUSED ) !== false ) {
			$sync_analytics->sync_start_time           = 0;
			$supplier_sync_analytics->sync_start_time  = 0;
			$sync_analytics->sync_end_time             = 0;
			$supplier_sync_analytics->sync_end_time    = 0;
			$sync_analytics->initiate_sync             = false;
			$supplier_sync_analytics->initiate_sync    = false;
			$sync_analytics->sync_status_code          = Constants::SYNC_PAUSED;
			$supplier_sync_analytics->sync_status_code = Constants::SYNC_PAUSED;
			Product_Sync_Audit_Model::update_sync_running_status( $this->supplier_id, $this->schedule_id, Constants::SYNC_RUNNING_STATUS_OFF );

			$audit_summary_pause = array_merge(
				array(
					'total_parsed'  => (int) $supplier_sync_analytics->total_product_parsed,
					'total_success' => (int) $supplier_sync_analytics->product_saved_success,
					'total_failed'  => (int) $supplier_sync_analytics->product_saved_fail,
					'total_added'   => (int) ( $supplier_sync_analytics->additional_sync_data['total_added'] ?? 0 ),
					'total_updated' => (int) ( $supplier_sync_analytics->additional_sync_data['total_updated'] ?? 0 ),
					'audit_end'     => 'paused',
				),
				Supplier_Audit_Logger::connection_context_for_audit( (int) $this->connection_id )
			);
			Supplier_Audit_Logger::finalize_sync( $this->supplier_id, $this->schedule_id, $audit_summary_pause );
		}
		$sync_analytics->save();
		$supplier_sync_analytics->save();

		if ( 'Sync Completed' === $supplier_sync_analytics->sync_status_message ) {
			Model_Utils::update_option( 'mowcps_' . $this->supplier_id . '_' . $this->schedule_id . '_sync_running_status', 'Sync Completed' );
			//ADD filter to remove manully created backup cron for backup-cron after sync completion.
			if ( has_filter( Plugin_Filters::FILTER_MANULLAY_CREATE_BACKUP_CRON ) ) {
				apply_filters(
					Plugin_Filters::FILTER_MANULLAY_CREATE_BACKUP_CRON,
					array(
						'event_specifier' => Plugin_Filters::EVENT_REMOVE_MANUAL_BACKUP_CRON,
						'supplier_id'     => $this->supplier_id,
						'connection_id'   => $this->connection_id,
						'additional_data'   => array(),
					)
				);
			}

		}
	}

	/**
	 * Append per-connection pagination/batch flags to the audit session (JSONL audit_context lines).
	 *
	 * @return void
	 */
	protected function audit_snapshot_after_fetch() {
		if ( ! Supplier_Audit_Logger::is_enabled( (int) $this->supplier_id ) ) {
			return;
		}

		$schedule_id = Sync_Details_DAO::get_instance()->get_schedule_id();
		$batch_cfg   = Setting_Model::get_by_connection_and_operation( (int) $this->connection_id, 'batch', array( 'configuration' => true ) );
		$page_cfg    = Setting_Model::get_by_connection_and_operation( (int) $this->connection_id, 'page', array( 'configuration' => true ) );

		$batch_setting_on = ! empty( $batch_cfg['is_active'] ) && '1' === (string) $batch_cfg['is_active'];
		$batch_sz         = isset( $batch_cfg['configuration']['batch_size'] ) ? (int) $batch_cfg['configuration']['batch_size'] : 0;
		$page_setting_on  = ! empty( $page_cfg['is_active'] ) && '1' === (string) $page_cfg['is_active'];
		$page_mode        = isset( $page_cfg['configuration']['type'] ) ? (string) $page_cfg['configuration']['type'] : '';

		$batch_runtime = Sync_Details_DAO::get_instance()->get_batch_flag();
		$page_runtime  = Sync_Details_DAO::get_instance()->get_page_flag();

		$identity = Supplier_Audit_Logger::get_audit_connection_identity_for_context( (int) $this->connection_id );

		Supplier_Audit_Logger::append_session_audit_context(
			(int) $this->supplier_id,
			$schedule_id,
			array_merge(
				$identity,
				array(
					'audit_batch_setting_enabled' => $batch_setting_on,
					'audit_batch_size'            => $batch_sz,
					'audit_batch_runtime_active'  => (bool) $batch_runtime,
					'audit_page_setting_enabled'  => $page_setting_on,
					'audit_pagination_mode'       => sanitize_text_field( $page_mode ),
					'audit_page_runtime_active'   => (bool) $page_runtime,
				)
			)
		);
	}

	/**
	 * Retrieve data from API.
	 *
	 * @param array  $headers API request headers.
	 * @param array  $body    API request body.
	 * @param string $url     API request URL.
	 * @return array
	 */
	public function retrieve_product_data( $headers = array(), $body = '', $url = '' ) {
		Supplier_Audit_Logger::set_sync_loop_context( (int) $this->batch_number, (int) $this->page_number );
		Supplier_Audit_Logger::set_audit_connection_context( (int) $this->connection_id );

		$file_sync_details = Setting_Model::get_by_connection_and_operation( $this->connection_id, 'file-sync', array( 'configuration' => true ) );
		$batch_flag        = Setting_Model::get_by_connection_and_operation( $this->connection_id, 'batch', array( 'configuration' => true ) );
		if ( ! empty( $batch_flag ) ) {
			Sync_Details_DAO::get_instance()->set_batch_flag( $batch_flag['is_active'] );
			Sync_Details_DAO::get_instance()->set_batch_size( $batch_flag['configuration']['batch_size'] );
		}
		$file_directory_path = trailingslashit( MOWCPS_PLUGIN_DIR ) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'external-supplier' . DIRECTORY_SEPARATOR . 'api-response';
		$connection_details  = Sync_Details_DAO::get_instance()->get_connection_details( $this->connection_id );

		$connection_type = $connection_details['type'] ?? 'api';

		if ( 1 === $this->batch_number ) { // For normal inbound and first batch we make the API call and if the sync is of batch type we store the data in file.
			if ( Constants::API_CONNECTION === $connection_type ) {
				$connect_external_supplier_handler = new Connect_External_Supplier_Handler( $this->connection_id, $this->supplier_id, $this->page_number );
				$api_response                      = $connect_external_supplier_handler->connect_api_handler( Constants::INBOUND_SYNC );
				if ( isset( $file_sync_details['is_active'] ) && '1' === $file_sync_details['is_active'] ) {
					$connect_external_supplier_handler = new Connect_FTP_Handler( $this->connection_id, $this->supplier_id, $this->page_number );
					$api_response                      = $connect_external_supplier_handler->file_from_api_handler( $this->connection_id, $file_sync_details['configuration']['file_name'], $file_sync_details['configuration']['is_zip'], $api_response );
				}
			} elseif ( Constants::FTP_CONNECTION === $connection_type ) {
				$connect_external_supplier_handler = new Connect_FTP_Handler( $this->connection_id, $this->supplier_id, $this->page_number );
				$api_response                      = $connect_external_supplier_handler->connect_ftp_handler( $this->connection_id, $this->supplier_id );
			} elseif ( Constants::CONNECTION_TYPE_FILE === $connection_type ) {
				$file_directory_path = MOWCPS_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'external-supplier' . DIRECTORY_SEPARATOR . 'file-response';
				$complete_file_name  = $connection_details['configuration']['file_name'] . '.' . $connection_details['configuration']['file_type'];
				if ( $batch_flag['is_active'] ) {
					$batch_size = isset( $batch_flag['configuration']['batch_size'] ) ? (int) $batch_flag['configuration']['batch_size'] : false;
					if ( false !== $batch_size && ! empty( $connection_details['configuration']['use_first_row_as_header'] ) ) {
						$batch_size = $batch_size + 1; // Read header row + batch_size data rows.
					}
					$end_line = $batch_size;
				} else {
					$end_line = false;
				}

				$api_response = Read_File_Data::read_data_from_file(
					$file_directory_path,
					$complete_file_name,
					0,
					$end_line,
					$connection_details['configuration']['use_first_row_as_header'],
					$connection_details['configuration']['delimiter']
				);
			}

			if ( Constants::CONNECTION_TYPE_FILE !== $connection_type ) {
				$api_response = Sync_Utils::get_main_response_array( $api_response, $this->connection_id );
			}

			update_option( 'mowcps_' . $this->connection_id . '_api_response_count', count( $api_response ) );
			update_option( 'mowcps_' . $this->connection_id . '_current_api_response_count', count( $api_response ) );
			// filter product data.
			$preview_filter = Preview_Filters_Model::get_by_supplier_id( $this->supplier_id );
			if ( $preview_filter && $preview_filter['is_active'] && $this->schedule_id === null ) {
				if ( '1' === $connection_details['is_primary'] || Constants::CONNECTION_TYPE_FILE === $connection_type ) {
					$api_response = Preview_Filter_Service::filter_products( $api_response, $this->supplier_id, $this->connection_id, $preview_filter );
				}
			} elseif ( has_filter( Plugin_Filters::FILTER_PRODUCT_DATA_FILTER ) ) {
				$api_response = apply_filters(
					Plugin_Filters::FILTER_PRODUCT_DATA_FILTER,
					$api_response,
					$this->supplier_id,
					$this->connection_id,
				);
			}
			if ( has_filter( Plugin_Filters::FILTER_LIMIT__PRODUCTS_FOR_SANDBOX ) && '1' === $connection_details['is_primary'] ) {
				$api_response = apply_filters(
					Plugin_Filters::FILTER_LIMIT__PRODUCTS_FOR_SANDBOX,
					array(
						'event_specifier'     => Plugin_Filters::EVENT_LIMIT_PRIMARY_API_PRODUCTS,
						'api_response'        => $api_response,
						'connection_id'       => $this->connection_id,
						'supplier_id'         => $this->supplier_id,
						'page_number'         => $this->page_number,
						'additional_info'     => array(
							'connection_type' => $connection_type,
						),
					)
				);
			}
			update_option( 'mowcps_' . $this->connection_id . '_filter_api_response_count', count( $api_response ) );

			if ( Constants::CONNECTION_TYPE_FILE === $connection_type ) {
				$batch_flag['is_active'] = false;
			}

			// Store data in batch-wise files if batch processing is enabled.
			if ( true === ( isset( $batch_flag['is_active'] ) && '1' === $batch_flag['is_active'] ) && ( ! isset( $file_sync_details['configuration']['file_in_batch'] ) || Constants::CONFIGURATION_ACTIVE !== $file_sync_details['configuration']['file_in_batch'] ) ) {
				Sync_Utils::store_data_in_batch_files( $api_response, $file_directory_path, $this->connection_id, $batch_flag['configuration']['batch_size'], $this->supplier_id, 1 );
				$batch_size     = isset( $batch_flag['configuration']['batch_size'] ) ? (int) $batch_flag['configuration']['batch_size'] : 100;
				$batch_response = array_slice( $api_response, 0, $batch_size );
				unset( $api_response );
				$api_response = $batch_response;
			}
			$this->audit_snapshot_after_fetch();
		} elseif ( $this->batch_number > 1 && Constants::CONNECTION_TYPE_FILE === $connection_type ) {
			// For batch_number > 1 and file connection type, read data from file for the specific batch
			$file_directory_path = MOWCPS_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'external-supplier' . DIRECTORY_SEPARATOR . 'file-response';
			$complete_file_name  = $connection_details['configuration']['file_name'] . '.' . $connection_details['configuration']['file_type'];

			$batch_size = isset( $batch_flag['configuration']['batch_size'] ) ? (int) $batch_flag['configuration']['batch_size'] : 0;

			// Calculate line range accounting for header row to ensure consistent batch sizes.
			// Batch 1 reads: lines 1 to (batch_size + 1) if header enabled, or 1 to batch_size if not.
			// Batch 2+ should start after Batch 1's range.
			$has_header = ! empty( $connection_details['configuration']['use_first_row_as_header'] );

			if ( $has_header ) {
				// With header: Batch 1 ends at line (batch_size + 1), so Batch 2+ starts at (batch_size + 2).
				// Formula: start_line = (batch_number - 1) * batch_size + batch_number
				// end_line = batch_number * batch_size + batch_number - 1
				// Example (batch_size=100): Batch 2 reads lines 102-201, Batch 3 reads lines 203-302
				$start_line = ( ( $this->batch_number - 1 ) * $batch_size ) + $this->batch_number;
				$end_line   = ( $this->batch_number * $batch_size ) + $this->batch_number - 1;
			} else {
				// No header: standard calculation.
				$start_line = ( ( $this->batch_number - 1 ) * $batch_size ) + 1;
				$end_line   = $this->batch_number * $batch_size;
			}

			$api_response = Read_File_Data::read_data_from_file(
				$file_directory_path,
				$complete_file_name,
				$start_line,
				$end_line,
				$connection_details['configuration']['use_first_row_as_header'],
				$connection_details['configuration']['delimiter']
			);

			update_option( 'mowcps_' . $this->connection_id . '_api_response_count', count( $api_response ) );

			// Filter product data, update filtered count option, and append audit_context (see filter_and_update_product_data).
			$api_response = $this->filter_and_update_product_data( $api_response, $connection_details, $connection_type );
		} elseif ( ! empty( $file_sync_details['configuration']['file_in_batch'] ) && Constants::CONFIGURATION_ACTIVE === $file_sync_details['configuration']['file_in_batch'] ) { // For 2nd batch onwards we fetch data from file.
			$connect_external_supplier_handler = new FTP_Service();
			$api_response                      = $connect_external_supplier_handler->read_file_in_batch( $this->connection_id, $this->batch_number, $file_sync_details['configuration']['file_name'], $file_sync_details['configuration']['delimiter'] ?? '' );
			// filter product data.
			$preview_filter = Preview_Filters_Model::get_by_supplier_id( $this->supplier_id );
			if ( $preview_filter && $preview_filter['is_active'] ) {
				$api_response = Preview_Filter_Service::filter_products( $api_response, $this->supplier_id, $this->connection_id, $preview_filter );
			} elseif ( has_filter( Plugin_Filters::FILTER_PRODUCT_DATA_FILTER ) ) {
				$api_response = apply_filters(
					Plugin_Filters::FILTER_PRODUCT_DATA_FILTER,
					$api_response,
					$this->supplier_id,
					$this->connection_id,
				);
			}
			if ( has_filter( Plugin_Filters::FILTER_LIMIT__PRODUCTS_FOR_SANDBOX ) && '1' === $connection_details['is_primary'] ) {
				$api_response = apply_filters(
					Plugin_Filters::FILTER_LIMIT__PRODUCTS_FOR_SANDBOX,
					array(
						'event_specifier'  => Plugin_Filters::EVENT_LIMIT_PRIMARY_API_PRODUCTS,
						'api_response'      => $api_response,
						'connection_id' => $this->connection_id,
						'supplier_id'   => $this->supplier_id,
						'page_number'   => $this->page_number,
						'additional_info' => array(),
					)
				);
			}
			update_option( 'mowcps_' . $this->connection_id . '_filter_api_response_count', count( $api_response ) );
			$this->audit_snapshot_after_fetch();
		} else {
			// Retrieve data from batch-wise file for current batch.
			$api_response       = Sync_Utils::retrieve_data_from_batch_file( $file_directory_path, $this->connection_id, $this->batch_number, $this->supplier_id );
			$response_count     = is_array( $api_response ) ? count( $api_response ) : 0;
			$api_response_count = (int) get_option( 'mowcps_' . $this->connection_id . '_current_api_response_count', 0 );
			$batch_size         = isset( $batch_flag['configuration']['batch_size'] ) ? (int) $batch_flag['configuration']['batch_size'] : 0;
			$value              = $batch_size * $this->batch_number;
			$response_count     = 0;
			if ( $api_response_count < $value && count( $api_response ) > 0 ) {
				$response_count = $api_response_count - ( $batch_size * ( $this->batch_number - 1 ) );
			} elseif ( count( $api_response ) > 0 ) {
				$response_count = $api_response_count - $batch_size * $this->batch_number;
			}
			update_option( 'mowcps_' . $this->connection_id . '_api_response_count', $response_count );
			update_option( 'mowcps_' . $this->connection_id . '_filter_api_response_count', $response_count );
			$this->audit_snapshot_after_fetch();
		}
		return $api_response;
	}

	/**
	 * Filter product data and update filtered count option.
	 *
	 * Applies the FILTER_PRODUCT_DATA_FILTER if available and updates
	 * the filtered API response count option.
	 *
	 * @param array $api_response The API response data to filter.
	 * @return array Filtered API response data.
	 */
	protected function filter_and_update_product_data( $api_response, $connection_details, $connection_type ) {
		// Filter product data if filter is available.

		$preview_filter = Preview_Filters_Model::get_by_supplier_id( $this->supplier_id );

		if ( $preview_filter && $preview_filter['is_active'] ) {
			if ( '1' === $connection_details['is_primary'] || Constants::CONNECTION_TYPE_FILE === $connection_type ) {
				$api_response = Preview_Filter_Service::filter_products( $api_response, $this->supplier_id, $this->connection_id, $preview_filter );
			}
		} elseif ( has_filter( Plugin_Filters::FILTER_PRODUCT_DATA_FILTER ) ) {
			$api_response = apply_filters(
				Plugin_Filters::FILTER_PRODUCT_DATA_FILTER,
				$api_response,
				$this->supplier_id,
				$this->connection_id,
			);
		}
		if ( has_filter( Plugin_Filters::FILTER_LIMIT__PRODUCTS_FOR_SANDBOX ) && '1' === $connection_details['is_primary'] ) {
			$api_response = apply_filters(
				Plugin_Filters::FILTER_LIMIT__PRODUCTS_FOR_SANDBOX,
				array(
					'event_specifier'  => Plugin_Filters::EVENT_LIMIT_PRIMARY_API_PRODUCTS,
					'api_response'      => $api_response,
					'connection_id' => $this->connection_id,
					'supplier_id'   => $this->supplier_id,
					'page_number'   => $this->page_number,
					'additional_info' => array(),
				)
			);
		}
		$filtered_count = is_array( $api_response ) ? count( $api_response ) : 0;
		update_option( 'mowcps_' . $this->connection_id . '_filter_api_response_count', $filtered_count );

		if ( Supplier_Audit_Logger::is_enabled( (int) $this->supplier_id ) ) {
			$this->audit_snapshot_after_fetch();
		}

		return $api_response;
	}

	/**
	 * Prepare unsynced data.
	 *
	 * @return string
	 */
	public function prepare_unsynced_data_and_sync_products( $filtered_product_data = array() ) {
		$unsynced_data = Product_Data_Services::filter_unsynced_data( $this->supplier_id, $this->connection_id, $filtered_product_data, $this->batch_number );

		$sync_end_status = $this->sync_products( $unsynced_data );
		if ( $sync_end_status !== Constants::SYNC_PAUSED && $sync_end_status !== Constants::SYNC_ABORTED ) {
			$next_process_flag = $this->next_process( $sync_end_status );
			$this->initiate_next_process( $next_process_flag );
			$connection_sync_type = Sync_Details_DAO::get_instance()->get_connection_handler_type( $this->connection_id );
			$sync_type            = Sync_Details_DAO::get_instance()->get_connection_sync_type( $this->connection_id );

			if ( Constants::PROCESS_COMPLETED === $next_process_flag && 'outbound' !== $sync_type ) {
				$priority_queue = Sync_Details_DAO::get_instance()->get_supplier_priority_queue();
				if ( count( $priority_queue ) >= 1 ) {
					$sync_end_status = Constants::CONNECTION_SYNC_COMPLETED;
				}
			}
		}
		return $sync_end_status;
	}

	/**
	 * Initiate next process on the basis of next_process returned flag.
	 *
	 * @param string $next_process_flag Flag for the next sync process.
	 * @return void
	 */
	public function initiate_next_process( $next_process_flag ) {
		$schedule_id    = Sync_Details_DAO::get_instance()->get_schedule_id();
		$priority_queue = Sync_Details_DAO::get_instance()->get_supplier_priority_queue();

		// Apply supplier-specific batch completion logic.
		if ( has_filter( Plugin_Filters::FILTER_SUPPLIER_PROCESS_COMPLETION ) ) {
			$batch_completion_request = apply_filters(
				Plugin_Filters::FILTER_SUPPLIER_PROCESS_COMPLETION,
				array(
					'event_specifier'      => Plugin_Filters::EVENT_SUPPLIER_BATCH_COMPLETION,
					'next_process_flag'    => $next_process_flag,
					'last_batch_analytics' => Product_Sync_Audit_Model::get_sync_audit( $this->supplier_id, $this->schedule_id, $this->connection_id, $this->batch_number, true ),
					'supplier_id'          => $this->supplier_id,
					'batch_number'         => $this->batch_number,
				)
			);

			$next_process_flag = $batch_completion_request['next_process_flag'];
		}

		switch ( $next_process_flag ) {
			case Constants::PROCESS_COMPLETED:
				Cron_Manager::enqueue_product_batch_sync(
					$this->supplier_id,
					$schedule_id,
					$this->connection_id,
					$priority_queue,
					array(
						'batch_number'   => 1,
						'page_number'    => 1,
						'cron_initiator' => Constants::VENDOR_AVAILABLE_PRODUCTS,
						'sync_type'      => 'connection_sync',
					)
				);
				break;
			case Constants::INITIATE_BATCH_SYNC:
				Cron_Manager::enqueue_product_batch_sync(
					$this->supplier_id,
					$schedule_id,
					$this->connection_id,
					$priority_queue,
					array(
						'batch_number'   => $this->batch_number + 1,
						'page_number'    => $this->page_number,
						'cron_initiator' => Constants::VENDOR_AVAILABLE_PRODUCTS,
						'sync_type'      => 'batch_sync',
					)
				);
				break;
			case Constants::INITIATE_PAGE_SYNC:
				Cron_Manager::enqueue_product_batch_sync(
					$this->supplier_id,
					$schedule_id,
					$this->connection_id,
					$priority_queue,
					array(
						'batch_number'   => $this->batch_number,
						'page_number'    => $this->page_number + 1,
						'cron_initiator' => Constants::VENDOR_AVAILABLE_PRODUCTS,
						'sync_type'      => 'page_sync',
					)
				);
				break;
			case Constants::DISCARD_PRODUCTS:
				Cron_Manager::enqueue_product_batch_sync(
					$this->supplier_id,
					$schedule_id,
					$this->connection_id,
					$priority_queue,
					array(
						'batch_number'   => $this->batch_number + 1,
						'page_number'    => $this->page_number + 1,
						'cron_initiator' => Constants::VENDOR_UNAVAILABLE_PRODUCTS,
						'sync_type'      => 'leftover_sync',
					)
				);
				break;
			case Constants::BATCH_COMPLETED_FOR_PAGE:
				if ( Constants::SYNC_RUNNING_STATUS_ON === Product_Sync_Audit_Model::get_sync_running_status( $this->supplier_id, $this->schedule_id ) ) {
					$this->prepare_unsynced_data_and_sync_products();
				}
				break;
			case Constants::SYNC_PAUSED:
				break;
			case Constants::COMPLETED_SYNC_RUNNING_STATUS:
				break;
			default:
				Cron_Manager::enqueue_product_batch_sync(
					$this->supplier_id,
					$schedule_id,
					$this->connection_id,
					$priority_queue,
					array(
						'batch_number'   => $this->batch_number,
						'page_number'    => $this->page_number,
						'cron_initiator' => Constants::VENDOR_AVAILABLE_PRODUCTS,
					)
				);
				break;
		}
	}

	/**
	 * Provides details of the next process to run after current sync.
	 *
	 * @param string $sync_end_status End status of the sync.
	 * @return string
	 */
	protected function next_process( $sync_end_status ) {

		$batch_completed = '/^Batch .+ completed$/';
		$page_completed  = '/^Page .+ completed$/';
		$set_leftover    = '/^Discarding leftover products$/';
		$sync_paused     = '/^Sync Paused$/';
		$batch_for_page  = '/^Batch .+ Completed for Page .+$/';
		$next_connection = '/^Connection Sync Completed$/';

		if ( preg_match( $batch_completed, $sync_end_status ) ) {
			return Constants::INITIATE_BATCH_SYNC;
		} elseif ( preg_match( $page_completed, $sync_end_status ) ) {
			return Constants::INITIATE_PAGE_SYNC;
		} elseif ( preg_match( $set_leftover, $sync_end_status ) ) {
			return Constants::DISCARD_PRODUCTS;
		} elseif ( preg_match( $sync_paused, $sync_end_status ) ) {
			return Constants::SYNC_PAUSED;
		} elseif ( preg_match( $batch_for_page, $sync_end_status ) ) {
			return Constants::BATCH_COMPLETED_FOR_PAGE;
		} elseif ( preg_match( $next_connection, $sync_end_status ) ) {
			return Constants::INITIATE_NEXT_CONNECTION_SYNC;
		} else {
			$sync_type = Sync_Details_DAO::get_instance()->get_connection_sync_type( $this->connection_id );
			if ( 'outbound' === $sync_type ) {
				return Constants::COMPLETED_SYNC_RUNNING_STATUS;
			}
			return Constants::PROCESS_COMPLETED;
		}
	}

	/**
	 * Checks if a sync process is currently running for the given supplier.
	 *
	 * @param int $supplier_id The ID of the supplier.
	 * @return string
	 */
	public static function sync_running_status( $supplier_id, $schedule_id ) {

		$sync_status = Product_Sync_Audit_Model::get_sync_running_status( $supplier_id, $schedule_id );

		if ( Constants::SYNC_RUNNING_STATUS_OFF === $sync_status ) {
			// TODO: Need to check this message is required or not.
			Model_Utils::update_option( 'mowcps_message', "Sync connection {$supplier_id} process started." );
			Model_Utils::update_option( 'mowcps_message_flag', 1 );
			Product_Sync_Audit_Model::update_sync_running_status( $supplier_id, $schedule_id, Constants::SYNC_RUNNING_STATUS_ON );
			Model_Utils::update_option( 'mowcps_' . $supplier_id . '_' . $schedule_id . '_sync_running_status', Constants::STARTED_SYNC_RUNNING_STATUS );
			return Constants::SYNC_RUNNING_STATUS_OFF;
		}
		return Constants::SYNC_RUNNING_STATUS_ON;
	}

	/**
	 * Get analytics for the current batch.
	 *
	 * @return Sync_Analytics
	 */
	protected function get_current_batch_analytics() {
		$current_number = $this->batch_number > $this->page_number ? $this->batch_number : $this->page_number;

		$batch_analytics = Model_Utils::get_sync_analytics_object( $this->supplier_id, $this->schedule_id, $this->connection_id, $current_number );

		return $batch_analytics;
	}

	/**
	 * Get priority queue for the supplier.
	 *
	 * Checks if the priority queue exists in the DAO, otherwise retrieves it from the Supplier Model and stores it in the DAO.
	 *
	 * @param int $supplier_id Supplier ID.
	 * @param int $schedule_id Schedule ID.
	 *
	 * @return array Priority queue.
	 */
	public function get_priority_queue( $supplier_id, $schedule_id ) {
		$priority_queue = Sync_Details_DAO::get_instance()->get_supplier_priority_queue();

		if ( ! empty( $priority_queue ) ) {
			return $priority_queue;
		}

		$priority_queue = Sync_Queue_Model::get_priority_queue( $supplier_id, $schedule_id );
		$priority_queue = $this->transform_priority_queue( $priority_queue );
		Sync_Details_DAO::get_instance()->set_priority_queue( $priority_queue );

		return $priority_queue;
	}

	/**
	 * Transform the priority queue.
	 *
	 * @param array $priority_queue The priority queue to transform.
	 * @return array The transformed priority queue.
	 */
	public static function transform_priority_queue( array $priority_queue ): array {
		$transformed = array();
		foreach ( $priority_queue as $item ) {
			$connection_id = $item['connection_id'];

			$sync_type_setting = Setting_Model::get_by_connection_and_operation( $connection_id, Constants::OUTBOUND );
			if ( empty( $sync_type_setting ) || ( isset( $sync_type_setting['is_active'] ) && $sync_type_setting['is_active'] !== '1' ) ) {
				$sync_type = Constants::INBOUND;
			} else {
				$sync_type = Constants::OUTBOUND;
			}
			$handler_type_setting = Setting_Model::get_by_connection_and_operation( $connection_id, Constants::MULTI_THREADING );
			if ( empty( $handler_type_setting ) || ( isset( $handler_type_setting['is_active'] ) && $handler_type_setting['is_active'] !== '1' ) ) {
				$handler_type = Constants::INTERDEPENDENT;
			} else {
				$handler_type = Constants::INDEPENDENT;
			}
			$transformed[ $connection_id ] = array(
				'sync_type'    => $sync_type,
				'handler_type' => $handler_type,
			);
		}
		return $transformed;
	}
}
