<?php

namespace EdgeBox\SyncCore\V2\Syndication;

use EdgeBox\SyncCore\Exception\InternalContentSyncError;
use EdgeBox\SyncCore\Interfaces\IApplicationInterface;
use EdgeBox\SyncCore\Interfaces\Syndication\IPullAll;
use EdgeBox\SyncCore\V2\Raw\Model\CreateTaskGroupDto;
use EdgeBox\SyncCore\V2\Raw\Model\EntityTypeVersionReference;
use EdgeBox\SyncCore\V2\Raw\Model\MigrationSummary;
use EdgeBox\SyncCore\V2\Raw\Model\SyndicationStatus;
use EdgeBox\SyncCore\V2\Raw\Model\TaskGroupEntity;
use EdgeBox\SyncCore\V2\Raw\Model\TaskGroupType;
use EdgeBox\SyncCore\V2\SerializableWithSyncCoreReference;
use EdgeBox\SyncCore\V2\SyncCore;

class PullAll extends SerializableWithSyncCoreReference implements IPullAll
{
    /**
     * @var string
     */
    protected $namespaceMachineName;

    /**
     * @var string
     */
    protected $machineName;

    /**
     * @var string
     */
    protected $versionId;

    /**
     * @var string
     */
    protected $flow;

    /**
     * @var bool
     */
    protected $pullAll;

    /**
     * @var null|string
     */
    protected $migrationId;

    /**
     * @var null|TaskGroupEntity
     */
    protected $dto;

    /**
     * @var null|MigrationSummary
     */
    protected $summaryDto;

    /**
     * PullAll constructor.
     */
    public function __construct(SyncCore $core, string $flow_machine_name, string $namespaceMachineName, string $machineName, string $versionId)
    {
        parent::__construct($core);

        $this->flow = $flow_machine_name;
        $this->namespaceMachineName = $namespaceMachineName;
        $this->machineName = $machineName;
        $this->versionId = $versionId;
    }

    public function fromPool(string $pool_id)
    {
        throw new InternalContentSyncError("The Sync Core v2 doesn't distinguish between pools for Pull All operations.");
    }

    public function force(bool $set)
    {
        $this->pullAll = $set;

        return $this;
    }

    // TODO: Drupal: Use this to check if it's done as we now have a status property.
    public function hasFinished()
    {
        return SyndicationStatus::_400_FINISHED === $this->getDto()->getStatus();
    }

    public function hasFailed()
    {
        return SyndicationStatus::_500_FAILED === $this->getDto()->getStatus();
    }

    public function wasAborted()
    {
        return SyndicationStatus::_600_ABORTED === $this->getDto()->getStatus();
    }

    public function total($clearCache = false)
    {
        $summary = $this->getSummaryDto(!$clearCache);
        $total = 0;
        foreach ($summary->getByStatus() as $status) {
            $total += $status->getCount();
        }

        return $total;
    }

    public function progress($fromCache = false)
    {
        if (!$this->migrationId) {
            throw new InternalContentSyncError("Can't get syndication progress before executing the pull all operation.");
        }

        $summary = $this->getSummaryDto(!$fromCache);
        $total = 0;
        foreach ($summary->getByStatus() as $statusSummary) {
            /**
             * @var string $status
             */
            $status = $statusSummary->getStatus();
            if (!in_array($status, [SyndicationStatus::_100_INITIALIZING, SyndicationStatus::_200_RUNNING, SyndicationStatus::_300_RETRYING])) {
                $total += $statusSummary->getCount();
            }
        }

        return $total;
    }

    public function getSourceName()
    {
        return $this->flow;
    }

    public function getTypeMachineName()
    {
        return $this->namespaceMachineName;
    }

    public function getBundleMachineName()
    {
        return $this->machineName;
    }

    public function execute()
    {
        $migrationDto = new CreateTaskGroupDto();

        /**
         * @var TaskGroupType $type
         */
        $type = $this->pullAll
        ? TaskGroupType::PULL_ALL
        : TaskGroupType::PULL_CHANGED;
        $migrationDto->setType($type);
        $migrationDto->setInitialSetup(false);
        $migrationDto->setFlowMachineName($this->flow);
        $entityType = new EntityTypeVersionReference();
        $entityType->setNamespaceMachineName($this->namespaceMachineName);
        $entityType->setMachineName($this->machineName);
        $entityType->setVersionId($this->versionId);
        $migrationDto->setEntityTypeReference($entityType);

        $request = $this->core->getClient()->migrationControllerCreateRequest(createTaskGroupDto: $migrationDto);
        $response = $this->core->sendToSyncCoreAndExpect($request, TaskGroupEntity::class, IApplicationInterface::SYNC_CORE_PERMISSIONS_CONFIGURATION, false, SyncCore::PULL_RETRY_COUNT);
        $this->dto = $response;

        $this->migrationId = $this->dto->getId();

        return $this;
    }

    /**
     * @return string
     */
    public function serialize()
    {
        return serialize([
            'core' => $this->serializeSyncCore(),
            'namespaceMachineName' => $this->namespaceMachineName,
            'machineName' => $this->machineName,
            'migrationId' => $this->migrationId,
        ]);
    }

    /**
     * @param string $serialized
     */
    public function unserialize($serialized)
    {
        $data = unserialize($serialized);

        $this->unserializeSyncCore($data['core']);

        $this->namespaceMachineName = $data['namespaceMachineName'];
        $this->machineName = $data['machineName'];
        $this->migrationId = $data['migrationId'];
    }

    protected function getSummaryDto($clearCache = false)
    {
        if ($this->summaryDto && !$clearCache) {
            return $this->summaryDto;
        }

        $request = $this->core->getClient()->migrationControllerSummaryRequest(id: $this->migrationId);
        $response = $this->core->sendToSyncCoreAndExpect($request, MigrationSummary::class, IApplicationInterface::SYNC_CORE_PERMISSIONS_CONFIGURATION, false, SyncCore::UPDATES_GET_RETRY_COUNT);

        return $this->summaryDto = $response;
    }

    protected function getDto()
    {
        if ($this->dto) {
            return $this->dto;
        }

        $request = $this->core->getClient()->migrationControllerItemRequest(id: $this->migrationId);
        $response = $this->core->sendToSyncCoreAndExpect($request, TaskGroupEntity::class, IApplicationInterface::SYNC_CORE_PERMISSIONS_CONFIGURATION, false, SyncCore::UPDATES_GET_RETRY_COUNT);

        return $this->dto = $response;
    }
}
