<?php

declare(strict_types=1);

namespace ComerciaGlobalPayments\AddonPayments\SDK\Response;

use GuzzleHttp\Exception\TransferException;

/**
 * Class Operation.
 *
 * Models a OK response from any payment request.
 *
 * @todo Implement \Iterator interface.
 */
class Operation extends AbstractResponse implements OperationInterface, \Countable
{

    /**
     * @var \ComerciaGlobalPayments\AddonPayments\SDK\Response\Transaction[]
     */
    private $operations;

    /**
     * @var string
     */
    private $status;

    /**
     * Operation constructor.
     *
     * @param \GuzzleHttp\Message\ResponseInterface|\Psr\Http\Message\ResponseInterface $httpResponse
     */
    public function __construct($httpResponse)
    {
        // @TODO: Add a custom exceptions
        $this->httpResponse = $httpResponse;
        if (null === $httpResponse->getBody()) {
            throw new TransferException('The API response is empty');
        }

        $this->responseData = json_decode($httpResponse->getBody()->getContents(), false);

        if (empty($this->responseData) || \JSON_ERROR_NONE !== json_last_error()) {
            throw new TransferException('The API response does not contains a valid JSON data');
        }

        // Check if responde is wrapped.
        if (isset($this->responseData->response)) {
            $this->responseData = $this->responseData->response;
        }

        if(!is_object($this->responseData)) {
           $this->responseData = json_decode(json_encode(simplexml_load_string($this->responseData)), false);
        }

        $property = isset( $this->responseData->operations) ? self::RESPONSE_OPERATIONS_KEY : self::RESPONSE_OPERATIONS_ARRAY_KEY;

        if ( (!\is_array($this->responseData->{$property}) && !is_object($this->responseData->{$property})) || empty($this->responseData->{$property})) {
            throw new TransferException('The API response does not contains operation payload');
        }

        if (is_object($this->responseData->{$property}) && is_array($this->responseData->{$property}->operation)) {
            $this->responseData->{$property} = $this->responseData->{$property}->operation;
        }

        $sorted = 1;
        foreach ($this->responseData->{$property} as $operation) {
            $this->operations[] = new Transaction($operation, $sorted);
            ++$sorted;
        }

        $this->status = $this->responseData->status;
    }

    public function count()
    {
        return \count($this->operations);
    }

    public function getOperations(): array
    {
        return $this->operations;
    }

    public function getStatus(): ?string
    {
        return $this->status;
    }

    public function getTransaction(): Transaction
    {
        // Copy value to avoid messing with array cursor.
        $operations = array_values($this->operations);

        return array_pop($operations);
    }

    public function hasSucceed(): bool
    {
        return self::STATUS_SUCCESS === $this->status && self::STATUS_SUCCESS === $this->getTransaction()->getStatus();
    }

    public function isPending(): bool
    {
        return $this->checkStatus( [self::STATUS_PENDING, self::STATUS_PARTIAL_COMPLETED, self::STATUS_AWAITING_PAYSOL] );
    }

    public function isCancelled(): bool
    {
        return $this->checkStatus( [self::STATUS_CANCELLED] );
    }

    public function hasFailed(): bool
    {
        $statuses = [ self::STATUS_ERROR, self::STATUS_FAIL ];

        return \in_array($this->status, $statuses, true)
               || \in_array($this->getTransaction()->getStatus(), $statuses, true);
    }

    public function needsRedirection(): bool
    {
        $allowed = [
            self::STATUS_REDIRECTED,
            self::STATUS_PENDING,
        ];

        return $this->checkStatus( $allowed )
               && null !== $this->getRedirectionTarget();
    }

    public function getRedirectionTarget(): ?RedirectionTarget
    {
        $transaction = $this->getTransaction();

        if (null === $transaction) {
            return null;
        }

        return $transaction->getRedirectionTarget();
    }

    protected function checkStatus( array $statuses ): bool
    {
        // Copy value to avoid messing with array cursor.
        $operations = array_values($this->operations);
        $last = array_pop($operations);

        return self::STATUS_SUCCESS === $this->status
            && \in_array($last->getStatus(), $statuses, true);
    }
}
