<?php
/**
 * Singleton class for managing sync-related data with lazy loading.
 *
 * @package    ProductSync
 * @subpackage SyncDetailsDAO
 * @author     miniOrange <info@miniorange.com>
 * @link       https://miniorange.com
 */

namespace MOWCPS\Common\Models;

use MOWCPS\Common\Utils\Model_Utils;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Handlers\SyncAnalytics\Sync_Analytics;
use MOWCPS\Common\Models\Authorization_Model;
use MOWCPS\Common\Constants\Plugin_Filters;

/**
 * Sync Details DAO - Singleton class for managing sync-related data.
 *
 * This class provides a centralized way to access and manage all sync-related
 * information including configurations, analytics, status, and other sync data.
 * It implements lazy loading to only fetch data when needed, reducing database calls.
 * TODO: Move this to repository or another appropriate place. This should not be a model.
 */
class Sync_Details_DAO {

	/**
	 * Single instance of the class.
	 *
	 * @var Sync_Details_DAO|null
	 */
	private static $instance = null;

	/**
	 * Cache for supplier id.
	 *
	 * @var string
	 */
	private $supplier_id = null;

	/**
	 * Cache for connection id.
	 *
	 * @var string
	 */
	public $connection_id = null;

	/**
	 * Cache for sync configurations.
	 *
	 * @var array
	 */
	private $sync_configs = array();

	/**
	 * Cache for sync analytics objects.
	 *
	 * @var array
	 */
	private $sync_analytics = array();

	/**
	 * Cache for sync running status.
	 *
	 * @var string
	 */
	private $supplier_sync_status_message = 'Sync yet to be initialized';

	/**
	 * Cache for supplier sync running status.
	 *
	 * @var string
	 */
	private $supplier_sync_running_status = array();

	/**
	 * Cache for advance configurations.
	 *
	 * @var array
	 */
	private $advance_configs = array();

	/**
	 * Cache for user details.
	 *
	 * @var array|null
	 */
	private $user_details = null;

	/**
	 * Cache for all connections.
	 *
	 * @var array|null
	 */
	private $all_connections = null;

	/**
	 * Cache for connection details.
	 *
	 * @var array
	 */
	private $connection_details = array();

	/**
	 * Cache for supplier details.
	 *
	 * @var array
	 */
	private $supplier_details = array();

	/**
	 * Schedule ID for the sync process.
	 *
	 * @var int
	 */
	private $schedule_id;

	/**
	 * Supplier-level sync analytics instance.
	 *
	 * @var Sync_Analytics|null
	 */
	private $supplier_sync_analytics = null;

	/**
	 * Connection-level sync analytics instance.
	 *
	 * @var Sync_Analytics|null
	 */
	private $connection_sync_analytics = null;

	/**
	 * Batch-level sync analytics instance.
	 *
	 * @var Sync_Analytics|null
	 */
	private $batch_sync_analytics = null;

	/**
	 * Tracks sync running status for each connection.
	 *
	 * @var array
	 */
	private $connections_sync_running_status;

	/**
	 * Cache for authorization details.
	 *
	 * @var array
	 */
	private $authorization_details = array();

	/**
	 * Outbound sync status.
	 *
	 * @var boolean
	 */
	private $outbound_sync_status;

	/**
	 * Private constructor to prevent direct instantiation.
	 */
	private function __construct() {
		// Private constructor to enforce singleton pattern.
	}

	/**
	 * Set supplier ID.
	 *
	 * @param int $supplier_id The supplier ID to set.
	 * @return Sync_Details_DAO Instance of the current class for method chaining.
	 */
	public function set_supplier_id( $supplier_id ) {
		$this->supplier_id = $supplier_id;
		return $this;
	}

	/**
	 * Get supplier id.
	 *
	 * @return int
	 */
	public function get_supplier_id() {
		return $this->supplier_id;
	}

	/**
	 * Set supplier ID.
	 *
	 * @param int $connection_id The supplier ID to set.
	 * @return Sync_Details_DAO Instance of the current class for method chaining.
	 */
	public function set_connection_id( $connection_id ) {
		$this->connection_id = $connection_id;
		return $this;
	}

	/**
	 * Get supplier id.
	 *
	 * @return int
	 */
	public function get_connection_id() {
		return $this->connection_id;
	}

	/**
	 * Get the singleton instance.
	 *
	 * @static
	 * @return Sync_Details_DAO
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Prevent cloning of the instance.
	 */
	private function __clone() {
		// Prevent cloning.
	}

	/**
	 * Get sync configuration for a specific connection.
	 *
	 * @param string $connection_name The connection name.
	 * @return array|null The sync configuration or null if not found.
	 */
	public function get_sync_config( $connection_name ) {
		if ( ! isset( $this->sync_configs[ $connection_name ] ) ) {
			$config                                 = Model_Utils::get_option( 'mowcps_config' );
			$this->sync_configs[ $connection_name ] = isset( $config[ $connection_name ] ) ? $config[ $connection_name ] : null;
		}
		return $this->sync_configs[ $connection_name ];
	}

	/**
	 * Set sync configuration for a specific connection.
	 *
	 * @param string $connection_name The connection name.
	 * @param array  $config The configuration to set.
	 * @return void
	 */
	public function set_sync_config( $connection_name, $config ) {
		$this->sync_configs[ $connection_name ] = $config;
		$all_configs                            = Model_Utils::get_option( 'mowcps_config' );
		$all_configs                            = is_array( $all_configs ) ? $all_configs : array();
		$all_configs[ $connection_name ]        = $config;
		Model_Utils::update_option( 'mowcps_config', $all_configs );
	}

	/**
	 * Get all sync configurations.
	 *
	 * @return array All sync configurations.
	 */
	public function get_all_sync_configs() {
		if ( empty( $this->sync_configs ) ) {
			$config             = Model_Utils::get_option( 'mowcps_config' );
			$this->sync_configs = is_array( $config ) ? $config : array();
		}
		return $this->sync_configs;
	}

	/**
	 * Get advance configuration for a connection.
	 *
	 * @param string $connection_name The connection name.
	 * @return array|null The advance configuration or null if not found.
	 */
	public function get_advance_config( $connection_name ) {
		if ( ! isset( $this->advance_configs[ $connection_name ] ) ) {
			$config                                    = Model_Utils::get_option( 'mowcps_adv_config' );
			$this->advance_configs[ $connection_name ] = isset( $config[ $connection_name ] ) ? $config[ $connection_name ] : null;
		}
		return $this->advance_configs[ $connection_name ];
	}

	/**
	 * Set advance configuration for a connection.
	 *
	 * @param string $connection_name The connection name.
	 * @param array  $config The configuration to set.
	 * @return void
	 */
	public function set_advance_config( $connection_name, $config ) {
		$this->advance_configs[ $connection_name ] = $config;
		$all_configs                               = Model_Utils::get_option( 'MOWCPS_adv_config' );
		$all_configs                               = is_array( $all_configs ) ? $all_configs : array();
		$all_configs[ $connection_name ]           = $config;
		Model_Utils::update_option( 'MOWCPS_adv_config', $all_configs );
	}

	/**
	 * Get user details.
	 *
	 * @return array|null The user details or null if not found.
	 */
	public function get_user_details() {
		if ( null === $this->user_details ) {
			$option_name        = base64_encode( 'mowcps_user_details' );
			$this->user_details = Model_Utils::get_option( $option_name );
		}
		return $this->user_details;
	}

	/**
	 * Set user details.
	 *
	 * @param array $user_details The user details to set.
	 * @return void
	 */
	public function set_user_details( $user_details ) {
		$this->user_details = $user_details;
		$option_name        = base64_encode( 'mowcps_user_details' );
		Model_Utils::update_option( $option_name, $user_details );
	}

	/**
	 * Get all connections.
	 *
	 * @return array All connections.
	 */
	public function get_all_connections() {
		if ( null === $this->all_connections ) {
			$config                = Model_Utils::get_option( 'MOWCPS_config' );
			$this->all_connections = is_array( $config ) ? array_keys( $config ) : array();
		}
		return $this->all_connections;
	}

	/**
	 * Get authorization details by supplier ID.
	 *
	 * @param int $supplier_id The supplier ID.
	 * @return array|null The authorization details or null if not found.
	 */
	public function get_authorization_details( $supplier_id ) {
		if ( ! isset( $this->authorization_details[ $supplier_id ] ) ) {
			$this->authorization_details[ $supplier_id ] = Authorization_Model::get_by_supplier_id( $supplier_id );
		}
		return $this->authorization_details[ $supplier_id ];
	}

	/**
	 * Get connection details by ID.
	 *
	 * @param int $connection_id The connection ID.
	 * @return array|null The connection details or null if not found.
	 */
	public function get_connection_details( $connection_id ) {
		if ( ! isset( $this->connection_details[ $connection_id ] ) ) {
			$this->connection_details[ $connection_id ] = Connection_Model::get_by_id( $connection_id );
		}
		return $this->connection_details[ $connection_id ];
	}

	/**
	 * Get supplier details by ID.
	 *
	 * @param int $supplier_id The supplier ID.
	 * @return array|null The supplier details or null if not found.
	 */
	public function get_supplier_details( $supplier_id ) {
		if ( ! isset( $this->supplier_details[ $supplier_id ] ) ) {
			$this->supplier_details[ $supplier_id ] = Supplier_Model::get_by_id( $supplier_id );
		}
		return $this->supplier_details[ $supplier_id ];
	}

	/**
	 * Get sync status message for a connection.
	 *
	 * @param string $connection_name The connection name.
	 * @return string The sync status message.
	 */
	public function get_sync_status_message( $connection_name ) {
		$analytics = $this->get_sync_analytics( $connection_name );
		return $analytics->sync_status_message;
	}

	/**
	 * Set the priority queue.
	 *
	 * @param array $priority_queue The priority queue.
	 * @return $this
	 */
	public function set_priority_queue( $priority_queue ) {
		$this->supplier_details['priority_queue'] = $priority_queue;
		return $this;
	}

	/**
	 * Get the priority queue.
	 *
	 * @return array The priority queue.
	 */
	public function get_supplier_priority_queue() {
		return isset( $this->supplier_details['priority_queue'] ) ? $this->supplier_details['priority_queue'] : array();
	}

	/**
	 * Update the supplier priority queue by removing the first element.
	 *
	 * @return array The updated priority queue.
	 */
	public function update_supplier_priority_queue() {
		if ( ! empty( $this->supplier_details['priority_queue'] ) ) {
			$first_key = array_key_first( $this->supplier_details['priority_queue'] );
			unset( $this->supplier_details['priority_queue'][ $first_key ] );
		}
		return $this->supplier_details['priority_queue'];
	}

	/**
	 * Set connection details.
	 *
	 * @param int   $connection_id The connection ID.
	 * @param array $details The connection details to set.
	 * @return void
	 */
	public function set_connection_details( $connection_id, $details ) {
		$this->connection_details[ $connection_id ] = $details;
	}

	/**
	 * Get the sync structure for a given connection.
	 *
	 * @param int $connection_id Connection ID.
	 *
	 * @return string Sync structure type.
	 */
	public function get_sync_structure( $connection_id ) {
		/**
		 * Filter for getting sync structure for a given connection.
		 *
		 * @param string $connection_id Connection ID.
		 * @return string Sync structure type.
		 * @since 1.0.0
		 */
		if ( has_filter( Plugin_Filters::FILTER_SYNC_STRUCTURE_HOOK ) ) {
			$product_type = apply_filters( Plugin_Filters::FILTER_SYNC_STRUCTURE_HOOK, $connection_id );
			return $product_type;
		}
		return Constants::SIMPLE_PRODUCT_SYNC;
	}

	/**
	 * Set the schedule ID.
	 *
	 * @param int $schedule_id Schedule ID.
	 *
	 * @return self
	 */
	public function set_schedule_id( $schedule_id ) {
		$this->schedule_id = $schedule_id;
		return $this;
	}

	/**
	 * Get the schedule ID.
	 *
	 * @return int Schedule ID.
	 */
	public function get_schedule_id() {
		return $this->schedule_id;
	}

	/**
	 * Set supplier analytics object.
	 *
	 * @param Sync_Analytics $analytics_object Supplier sync analytics object.
	 *
	 * @return void
	 */
	public function set_supplier_analytics( $analytics_object ) {
		$this->supplier_sync_analytics = $analytics_object;
	}

	/**
	 * Set connection analytics object.
	 *
	 * @param Sync_Analytics $analytics_object Connection sync analytics object.
	 *
	 * @return void
	 */
	public function set_connection_analytics( $analytics_object ) {
		$this->connection_sync_analytics = $analytics_object;
	}

	/**
	 * Set batch analytics object.
	 *
	 * @param Sync_Analytics $analytics_object Batch sync analytics object.
	 *
	 * @return void
	 */
	public function set_batch_analytics( $analytics_object ) {
		$this->batch_sync_analytics = $analytics_object;
	}

	/**
	 * Get batch analytics object.
	 *
	 * @return Sync_Analytics|null Batch sync analytics object or null if not set.
	 */
	public function get_batch_analytics() {
		return $this->batch_sync_analytics;
	}

	/**
	 * Get supplier analytics object.
	 *
	 * @return Sync_Analytics|null Supplier sync analytics object or null if not set.
	 */
	public function get_supplier_analytics( $supplier_id, $schedule_id ) {
		if ( null === $this->supplier_sync_analytics ) {
			$supplier_sync_analytics = new Sync_Analytics( $supplier_id, $schedule_id );
			$supplier_sync_analytics->set_analytics_object( Product_Sync_Audit_Model::get_sync_audit( $supplier_id, $schedule_id ) );
			$this->set_supplier_analytics( $supplier_sync_analytics );
		}
		return $this->supplier_sync_analytics;
	}

	/**
	 * Get connection analytics object.
	 *
	 * @return Sync_Analytics|null Connection sync analytics object or null if not set.
	 */
	public function get_connection_analytics( $supplier_id, $schedule_id, $connection_id ) {
		if ( null === $this->connection_sync_analytics ) {
			$connection_sync_analytics = new Sync_Analytics( $supplier_id, $schedule_id, $connection_id );
			$analytics_data            = Product_Sync_Audit_Model::get_sync_audit( $supplier_id, $schedule_id, $connection_id );
			if ( ! $analytics_data ) {
				Product_Sync_Audit_Model::create(
					array(
						'supplier_id'   => $supplier_id,
						'schedule_id'   => $schedule_id,
						'connection_id' => $connection_id,
					)
				);
				$analytics_data = Product_Sync_Audit_Model::get_sync_audit( $supplier_id, $schedule_id, $connection_id );
			}
			$connection_sync_analytics->set_analytics_object( $analytics_data );
			$this->set_connection_analytics( $connection_sync_analytics );
		}
		return $this->connection_sync_analytics;
	}

	/**
	 * Get API details for a given connection.
	 *
	 * @param int $connection_id Connection ID.
	 *
	 * @return array API details.
	 */
	public function get_api_details( $connection_id ) {
		return $this->connection_details[ $connection_id ]['configuration'];
	}

	/**
	 * Get connection sync type.
	 *
	 * @param int $connection_id Connection ID.
	 *
	 * @return string Sync type.
	 */
	public function get_connection_sync_type( $connection_id ) {
		if ( isset( $this->supplier_details['priority_queue'][ $connection_id ]['sync_type'] ) ) {
			return $this->supplier_details['priority_queue'][ $connection_id ]['sync_type'];
		} else {
			$this->get_supplier_details( $this->supplier_id );
			return isset( $this->supplier_details['priority_queue'][ $connection_id ]['sync_type'] ) ? $this->supplier_details['priority_queue'][ $connection_id ]['sync_type'] : Constants::INBOUND;
		}
		return Constants::INBOUND;
	}

	/**
	 * Get connection handler type.
	 *
	 * @param int $connection_id Connection ID.
	 *
	 * @return string Handler type.
	 */
	public function get_connection_handler_type( $connection_id ) {
		return isset( $this->supplier_details['priority_queue'][ $connection_id ]['handler_type'] ) ? $this->supplier_details['priority_queue'][ $connection_id ]['handler_type'] : Constants::INTERDEPENDENT;
	}

	/**
	 * Set batch flag.
	 *
	 * @param string $flag Batch flag value.
	 */
	public function set_batch_flag( $flag ) {
		$this->connection_details['batch_flag'] = ( '1' === $flag );
	}

	/**
	 * Get batch flag.
	 *
	 * @return string Batch flag value.
	 */
	public function get_batch_flag() {
		return isset( $this->connection_details['batch_flag'] ) ? $this->connection_details['batch_flag'] : false;
	}

	/**
	 * Set page flag.
	 *
	 * @param string $flag Page flag value.
	 */
	public function set_page_flag( $flag ) {
		$this->connection_details['page_flag'] = $flag;
	}

	/**
	 * Get page flag.
	 *
	 * @return string Page flag value.
	 */
	public function get_page_flag() {
		return isset( $this->connection_details['page_flag'] ) ? $this->connection_details['page_flag'] : false;
	}

	/**
	 * Set batch size.
	 *
	 * @param int $batch_size Batch size value.
	 */
	public function set_batch_size( $batch_size ) {
		$this->connection_details['batch_size'] = $batch_size;
	}

	/**
	 * Get batch size.
	 *
	 * @return int Batch size value.
	 */
	public function get_batch_size() {
		return isset( $this->connection_details['batch_size'] ) ? $this->connection_details['batch_size'] : 0;
	}

	/**
	 * Set supplier app type.
	 *
	 * @param string $app_type Supplier app type.
	 *
	 * @return void
	 */
	public function set_supplier_app_type( $app_type ) {
		$this->supplier_details['app_type'] = $app_type;
	}

	/**
	 * Get supplier app type.
	 *
	 * @return string Supplier app type.
	 */
	public function get_supplier_app_type() {
		return isset( $this->supplier_details['app_type'] ) ? $this->supplier_details['app_type'] : Constants::DEFAULT_APP;
	}

	/**
	 * Set outbound sync status.
	 *
	 * @param int     $connection_id Connection ID.
	 * @param boolean $status        Outbound sync status.
	 *
	 * @return void
	 */
	public function set_outbound_sync_status( $connection_id, $status ) {
		$this->outbound_sync_status[ $connection_id ] = $status;
	}

	/**
	 * Get outbound sync status.
	 *
	 * @param int $connection_id Connection ID.
	 *
	 * @return boolean Outbound sync status.
	 */
	public function get_outbound_sync_status( $connection_id ) {
		return isset( $this->outbound_sync_status[ $connection_id ] ) ? $this->outbound_sync_status[ $connection_id ] : false;
	}
}
