<?php
/**
 * CleverReach WooCommerce Integration.
 *
 * @package CleverReach
 */

namespace CleverReach\WooCommerce\ViewModel\Dashboard;

use CleverReach\WooCommerce\Components\Repositories\Archive_Repository;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Group\Contracts\GroupService;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\InitialSynchronization\Tasks\Composite\InitialSyncTask;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Receiver\Tasks\Composite\ReceiverSyncTask;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Receiver\Tasks\Composite\SubscribeReceiverTask;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Receiver\Tasks\Composite\UnsubscribeReceiverTask;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Stats\Contracts\SnapshotService;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Stats\Entity\Stats;
use CleverReach\WooCommerce\IntegrationCore\BusinessLogic\Stats\Http\Proxy;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\Exceptions\BaseException;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\Logger\LogContextData;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\Logger\Logger;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\Exceptions\RepositoryClassException;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\Exceptions\RepositoryNotRegisteredException;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\Interfaces\QueueItemRepository;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\QueryFilter\Operators;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\QueryFilter\QueryFilter;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ORM\RepositoryRegistry;
use CleverReach\WooCommerce\IntegrationCore\Infrastructure\ServiceRegister;

/**
 * Class Receiver_Statistics
 *
 * @package CleverReach\WooCommerce\ViewModel\Dashboard
 */
class Receiver_Statistics {


	/**
	 * Snapshots
	 *
	 * @var Stats[]
	 */
	private $snapshots;

	/**
	 * ReceiverStatistics constructor.
	 */
	public function __construct() {
		$this->snapshots = $this->get_snapshot_service()->getSnapshots();
	}

	/**
	 * Gets last sync time.
	 *
	 * @return false|string
	 */
	public function get_last_sync_time() {
		try {
			return $this->get_latest_sync_task_time();
		} catch ( QueryFilterInvalidParamException  $e ) {
			Logger::logError(
				"Unable to return last sync time: {$e->getMessage()}",
				'Integration',
				array( new LogContextData( 'trace', $e->getTraceAsString() ) )
			);
		} catch ( RepositoryClassException  $e ) {
			Logger::logError(
				"Unable to return last sync time: {$e->getMessage()}",
				'Integration',
				array( new LogContextData( 'trace', $e->getTraceAsString() ) )
			);
		} catch ( RepositoryNotRegisteredException  $e ) {
			Logger::logError(
				"Unable to return last sync time: {$e->getMessage()}",
				'Integration',
				array( new LogContextData( 'trace', $e->getTraceAsString() ) )
			);
		}

		return '';
	}

	/**
	 * Retrieves number of customers
	 *
	 * @return int
	 */
	public function get_customers() {
		try {
			$stats     = $this->get_stats_proxy()->getStats( $this->get_group_service()->getId() );
			$customers = $stats->getTotalReceiverCount();
		} catch ( BaseException $e ) {
			$customers = 0;
		}

		return $customers;
	}

	/**
	 * Retrieves number of subscribed users
	 *
	 * @return int
	 */
	public function get_subscribed() {
		$latest_snapshot = end( $this->snapshots );

		return $latest_snapshot ? $latest_snapshot->getSubscribed() : 0;
	}


	/**
	 * Retrieves number of unsubscribed users
	 *
	 * @return int
	 */
	public function get_unsubscribed() {
		$latest_snapshot = end( $this->snapshots );

		return $latest_snapshot ? $latest_snapshot->getUnsubscribed() : 0;
	}

	/**
	 * Retrieves snapshot service
	 *
	 * @return SnapshotService
	 */
	private function get_snapshot_service() {
		/**
		 * Snapshot service.
		 *
		 * @var SnapshotService $snapshot_service
		 */
		$snapshot_service = ServiceRegister::getService( SnapshotService::CLASS_NAME );

		return $snapshot_service;
	}


	/**
	 * Retrieves group service
	 *
	 * @return GroupService
	 */
	protected function get_group_service() {
		/**
		 * Group service.
		 *
		 * @var GroupService $group_service
		 */
		$group_service = ServiceRegister::getService( GroupService::CLASS_NAME );

		return $group_service;
	}

	/**
	 * Retrieves stats proxy
	 *
	 * @return Proxy
	 */
	private function get_stats_proxy() {
		/**
		 * Proxy
		 *
		 * @var Proxy $proxy
		 */
		$proxy = ServiceRegister::getService( Proxy::CLASS_NAME );

		return $proxy;
	}

	/**
	 * Fetch latest sync task time
	 *
	 * @return string
	 *
	 * @throws QueryFilterInvalidParamException
	 * @throws RepositoryClassException
	 * @throws RepositoryNotRegisteredException
	 */
	private function get_latest_sync_task_time()
	{
		$filter = (new QueryFilter())
			->where('taskType', Operators::IN, [
				SubscribeReceiverTask::getClassName(),
				UnsubscribeReceiverTask::getClassName(),
				ReceiverSyncTask::getClassName(),
				InitialSyncTask::getClassName(),
			])
			->where('status', Operators::EQUALS, 'completed')
			->orderBy('queueTime', 'DESC');

		$storage_task  = $this->get_storage()->selectOne($filter);
		$archived_task = $this->get_archived_repository()->selectOne($filter);

		$timestamps = array_filter([
			$storage_task ? $storage_task->getFinishTimestamp() : null,
			$archived_task ? $archived_task->getFinishTimestamp() : null,
		]);

		if (empty($timestamps)) {
			return '';
		}
		$latest = max($timestamps);

		return gmdate('d-m-Y H:i', $latest);
	}

	/**
	 * Gets task storage.
	 *
	 * @return QueueItemRepository
	 *
	 * @throws RepositoryClassException When repository instance is not QueueItemRepository.
	 * @throws RepositoryNotRegisteredException When repository is not registered.
	 */
	private function get_storage() {
		return RepositoryRegistry::getQueueItemRepository();
	}

	/**
	 * Gets archived storage.
	 *
	 * @return Archive_Repository
	 *
	 * @throws RepositoryClassException
	 * @throws RepositoryNotRegisteredException
	 */
	private function get_archived_repository()
	{
		return RepositoryRegistry::getArchivedQueueItemRepository();
	}
}
