<?php

namespace Easymailing\App\Core\Provider\Woocommerce\Hooks;

use Easymailing\App\Api\Client;
use Easymailing\App\Core\Log\LogManager;
use Easymailing\App\Core\Provider\Woocommerce\Batch\CategoryBatchOperations;
use Easymailing\App\Core\Provider\Woocommerce\Batch\CustomerBatchOperations;
use Easymailing\App\Core\Provider\Woocommerce\Batch\OrderBatchOperations;
use Easymailing\App\Core\Provider\Woocommerce\Batch\ProductBatchOperations;
use Easymailing\App\Core\Provider\Woocommerce\Batch\StoresBatchOperations;
use Easymailing\App\Core\Provider\Woocommerce\Model\BatchSynchronizationPhase;
use Easymailing\App\Core\Provider\Woocommerce\Model\BatchSynchronizationStatus;
use Easymailing\App\Core\Provider\Woocommerce\Model\LogType;
use Easymailing\App\Core\Provider\Woocommerce\Service\BatchEnqueueManager;
use Easymailing\App\Core\Provider\Woocommerce\Service\BatchResultProcessManager;
use Easymailing\App\Core\Provider\Woocommerce\Service\MetadataHelper;

class SynchronizationHook
{

    private $apiClient;
    private $orderBatchOperations;
    private $storesBatchOperations;
    private $customerBatchOperations;
    private $categoryBatchOperations;
    private $productBatchOperations;
    private $batchEnqueueManager;
    private $batchResultProcessManager;
    private $logManager;

    public function __construct(
		Client $apiClient,
		OrderBatchOperations $orderBatchOperations,
		StoresBatchOperations $storesBatchOperations,
		CustomerBatchOperations $customerBatchOperations,
		CategoryBatchOperations $categoryBatchOperations,
	    ProductBatchOperations $productBatchOperations,
	    BatchEnqueueManager $batchEnqueueManager,
	    BatchResultProcessManager $batchResultProcessManager,
		LogManager $logManager
    )
    {
       $this->apiClient = $apiClient;
	   $this->orderBatchOperations = $orderBatchOperations;
	   $this->storesBatchOperations = $storesBatchOperations;
	   $this->customerBatchOperations = $customerBatchOperations;
	   $this->categoryBatchOperations = $categoryBatchOperations;
	   $this->productBatchOperations = $productBatchOperations;
	   $this->batchEnqueueManager = $batchEnqueueManager;
	   $this->batchResultProcessManager = $batchResultProcessManager;
	   $this->logManager = $logManager;
    }

    public function startSynchronizationInBackground(): void
    {
        if ($this->isSynchronizationAborted()) {
            $this->logManager->createLog(LogType::BATCH, "Start skipped: synchronization aborted");
            update_option(MetadataHelper::getBackgroundTaskId(), null);
            return;
        }

        $phase = $this->determineCurrentPhase();

        update_option(MetadataHelper::getBatchSynchronizationStatus(), BatchSynchronizationStatus::IN_PROGRESS);
        update_option(MetadataHelper::getBatchSynchronizationPhase(), $phase);

        $this->logManager->createLog(LogType::BATCH, sprintf("Start synchronization phase %d", $phase));

        $batchOperations = $this->getBatchOperationsForPhase($phase);

        $this->logManager->createLog(LogType::BATCH, sprintf("Phase %d batch operations: %d", $phase, count($batchOperations)));

        if (empty($batchOperations)) {
            $this->logManager->createLog(LogType::BATCH, sprintf("Phase %d has no operations, advancing", $phase));
            update_option(MetadataHelper::getBackgroundTaskId(), null);
            $this->advanceToNextPhase($phase);
            return;
        }

        $chunks = array_chunk($batchOperations, 450);

        $this->logManager->createLog(LogType::BATCH, sprintf("Phase %d chunks: %d", $phase, count($chunks)));

        $this->saveProgress($phase, count($batchOperations), 0, 0);

        try {
	        $iris = [];
            foreach ($chunks as $chunk) {
                $bachOperation = $this->apiClient->createBatchOperation(['operations' => $chunk]);
				$iris[] = $bachOperation->id;
            }

	        update_option(MetadataHelper::getApiBatchOperationsIris(), json_encode($iris));
			$this->batchEnqueueManager->enqueueCheckBackgroundTask();

        } catch (\Exception $e) {
			$this->logManager->createLog(LogType::BATCH, "Batch Operations Error: " . $e->getMessage());
	        update_option(MetadataHelper::getBatchSynchronizationStatus(), BatchSynchronizationStatus::ERROR);
        }

		$this->logManager->createLog(LogType::BATCH, sprintf("End synchronization phase %d", $phase));
	    update_option(MetadataHelper::getBackgroundTaskId(), null);
    }

	public function checkSynchronizationInBackground(): void
	{
		if ($this->isSynchronizationAborted()) {
			$this->logManager->createLog(LogType::BATCH, "Check skipped: synchronization aborted");
			update_option(MetadataHelper::getCheckBackgroundTaskId(), null);
			return;
		}

		$data = get_option(MetadataHelper::getApiBatchOperationsIris());
		$iris = $data ? json_decode($data) : [];

		if (empty($iris)) {
			// Sin IRIs no hay nada que esperar; avanzamos por si la fase actual quedó
			// sin encolar batches (caso fase vacía) o para cerrar el ciclo.
			$this->logManager->createLog(LogType::BATCH, "No batch IRIs found in check, advancing phase");
			$this->advanceToNextPhase($this->determineCurrentPhase());
			return;
		}

		$batchOperations = [];
		$lastDate = null;
		$totalSum = 0;
		$finishedSum = 0;
		$erroredSum = 0;
		$allFinished = true;

		foreach($iris as $iri) {
			$batchOperation = $this->apiClient->getBatchOperation($iri);
			$batchOperations[] = $batchOperation;

			$totalSum    += (int) $batchOperation->total;
			$finishedSum += (int) $batchOperation->finished;
			$erroredSum  += (int) $batchOperation->errored;

			$this->logManager->createLog(LogType::BATCH, sprintf("Check Batch Operation. Id: %s, status: %s, total: %s, finished: %s, errored: %s",
				$batchOperation->id,
				$batchOperation->status,
				$batchOperation->total,
				$batchOperation->finished,
				$batchOperation->errored
			));

			if($batchOperation->status !== 'finished'){
				$allFinished = false;
			}

			if($batchOperation->finishedAt > $lastDate){
				$lastDate = $batchOperation->finishedAt;
			}
		}

		$this->saveProgress($this->determineCurrentPhase(), $totalSum, $finishedSum, $erroredSum);

		if (!$allFinished) {
			$this->batchEnqueueManager->enqueueCheckBackgroundTask();
			return;
		}

		$this->batchResultProcessManager->updateSynchronizationStatus($batchOperations);

		update_option(MetadataHelper::getCheckBackgroundTaskId(), null);
		update_option(MetadataHelper::getApiBatchOperationsIris(), null);

		if ($lastDate) {
			update_option(MetadataHelper::getLastBatchSynchronization(), $lastDate);
		}

		$this->advanceToNextPhase($this->determineCurrentPhase());
	}

	private function determineCurrentPhase(): int
	{
		$phase = get_option(MetadataHelper::getBatchSynchronizationPhase());

		if ($phase === false || $phase === null || $phase === '') {
			return BatchSynchronizationPhase::STORES_CATEGORIES_CUSTOMERS;
		}

		$phase = (int) $phase;

		// NOT_STARTED o un ciclo previamente terminado → reiniciamos.
		if ($phase <= BatchSynchronizationPhase::NOT_STARTED || $phase >= BatchSynchronizationPhase::DONE) {
			return BatchSynchronizationPhase::STORES_CATEGORIES_CUSTOMERS;
		}

		return $phase;
	}

	private function getBatchOperationsForPhase(int $phase): array
	{
		switch ($phase) {
			case BatchSynchronizationPhase::STORES_CATEGORIES_CUSTOMERS:
				$storeOps = $this->storesBatchOperations->getStoresBatchOperations();
				$this->logManager->createLog(LogType::BATCH, sprintf("Store batch operations: %d", count($storeOps)));

				$categoryOps = $this->categoryBatchOperations->getCategoriesBatchOperations();
				$this->logManager->createLog(LogType::BATCH, sprintf("Category batch operations: %d", count($categoryOps)));

				$customerOps = $this->customerBatchOperations->getCustomerBatchOperations();
				$this->logManager->createLog(LogType::BATCH, sprintf("Customer batch operations: %d", count($customerOps)));

				return array_merge($storeOps, $categoryOps, $customerOps);

			case BatchSynchronizationPhase::PRODUCTS:
				$productOps = $this->productBatchOperations->getProductBatchOperations();
				$this->logManager->createLog(LogType::BATCH, sprintf("Product batch operations: %d", count($productOps)));
				return $productOps;

			case BatchSynchronizationPhase::VARIANTS:
				$variantOps = $this->productBatchOperations->getVariantBatchOperations();
				$this->logManager->createLog(LogType::BATCH, sprintf("Variant batch operations: %d", count($variantOps)));
				return $variantOps;

			case BatchSynchronizationPhase::ORDERS:
				$orderOps = $this->orderBatchOperations->getOrderBatchOperations();
				$this->logManager->createLog(LogType::BATCH, sprintf("Order batch operations: %d", count($orderOps)));
				return $orderOps;
		}

		return [];
	}

	private function advanceToNextPhase(int $currentPhase): void
	{
		$nextPhase = $currentPhase + 1;

		if ($nextPhase >= BatchSynchronizationPhase::DONE) {
			$this->finishSynchronization();
			return;
		}

		update_option(MetadataHelper::getBatchSynchronizationPhase(), $nextPhase);
		$this->logManager->createLog(LogType::BATCH, sprintf("Advancing from phase %d to phase %d", $currentPhase, $nextPhase));
		$this->batchEnqueueManager->enqueue();
	}

	private function finishSynchronization(): void
	{
		update_option(MetadataHelper::getBatchSynchronizationStatus(), BatchSynchronizationStatus::SUCCESS);
		update_option(MetadataHelper::getBatchSynchronizationPhase(), BatchSynchronizationPhase::DONE);
		update_option(MetadataHelper::getBatchSynchronizationProgress(), null);
		delete_option(MetadataHelper::getRetryOnlyMode());
		$this->logManager->createLog(LogType::BATCH, "Synchronization completed (all phases done)");
	}

	private function saveProgress(int $phase, int $total, int $finished, int $errored): void
	{
		update_option(MetadataHelper::getBatchSynchronizationProgress(), json_encode([
			'phase'     => $phase,
			'total'     => $total,
			'finished'  => $finished,
			'errored'   => $errored,
			'updatedAt' => time(),
		]));
	}

	private function isSynchronizationAborted(): bool
	{
		$status = get_option(MetadataHelper::getBatchSynchronizationStatus());
		return $status === BatchSynchronizationStatus::CANCELED || $status === BatchSynchronizationStatus::ERROR;
	}
}
