<?php

declare(strict_types=1);

namespace ComerciaGlobalPayments\AddonPayments\SDK\Request;

use ComerciaGlobalPayments\AddonPayments\SDK\Request\Enum as _Enum;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

/**
 * Class Charge.
 *
 * Maps data for 'charge' request.
 *
 * @see https://api-developer.easypaymentgateway.com/EPGJS/#operation/Charge
 *
 * @SuppressWarnings(PHPMD.StaticAccess)
 * @SuppressWarnings(PHPMD.LongVariableNames)
 */
class Charge extends Abstract3DSecureV2Request
{

    public const PREPAY_TOKEN_HEADER = 'prepayToken';

    public const FLOW_PARAM_KEY = 'flow';

    /**
     * @var string
     * @Assert\NotNull()
     */
    private $merchantId;

    /**
     * @var string
     * @Assert\NotNull()
     */
    private $customerId;

    /**
     * @var string
     * @Assert\NotNull()
     * @Assert\Country()
     */
    private $country;

    /**
     * @var string
     * @Assert\NotNull()
     * @Assert\Currency()
     */
    private $currency;

    /**
     * @var float
     * @Assert\NotNull()
     * @Assert\Type(
     *     type="double",
     *     message="The value {{ value }} is not a valid {{ type }}."
     * )
     * @Assert\GreaterThan(
     *     value=0
     * )
     */
    private $amount;

    /**
     * @var string
     * @Assert\NotNull()
     */
    private $merchantTransactionId;

    /**
     * @var string
     * @Assert\NotNull()
     * @Assert\Choice(
     *     callback="getOperationChoices",
     *     multiple=false,
     *     strict=true,
     *     message="The value you selected is not a valid operation",
     *     multipleMessage="One or more of the given values is not a valid operation"
     * )
     */
    private $operationType;

    /**
     * @var int
     * @Assert\NotNull()
     * @Assert\Type(
     *     type="integer",
     *     message="The value {{ value }} is not a valid {{ type }}."
     * )
     */
    private $productId;

    /**
     * @var string
     * @Assert\NotNull()
     * @Assert\Url()
     */
    private $statusURL;

    /**
     * @var string
     * @Assert\Url()
     */
    private $successURL;

    /**
     * @var string
     * @Assert\Url()
     */
    private $cancelURL;

    /**
     * @var string
     * @Assert\Url()
     */
    private $errorURL;

    /**
     * @var string
     * @Assert\Url()
     */
    private $awaitingURL;

    /**
     * @var array
     */
    private $merchantParams = [];

    /**
     * @var mixed[]
     */
    private $additionalData = [];

    /**
     * @return bool
     */
    private $autoCapture;

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

    private $customerCountry;

    private $customerEmail;

    private $firstName;

    private $lastName;

    private $language;

    private $ipAddress;

    private $dob;

    private $rememberMe;

    private $account;

    private $paysolExtendedData;

    private $apiVersion;

    public static function excludedProperties(): array
    {
        return array_merge(parent::excludedProperties(), [
            'additionalData',
            'merchantParams',
        ]);
    }

    public function serializeSpecialProperties(): array
    {
        return array_merge(parent::serializeSpecialProperties(), [
            'merchantParams' => [$this, 'flatArray'],
        ]);
    }

    public function jsonSerialize(): array
    {
        return array_merge(
            parent::jsonSerialize(),
            $this->specialProperties(),
            $this->additionalData
        );
    }

    public function setMerchantId(string $merchantId): self
    {
        $this->merchantId = $merchantId;

        return $this;
    }

    public function getMerchantId(): string
    {
        return $this->merchantId;
    }

    public function setPrepayToken(string $token): self
    {
        $this->addHeader(self::PREPAY_TOKEN_HEADER, $token);

        return $this;
    }

    public function setCustomerId(string $customerId): self
    {
        $this->customerId = $customerId;

        return $this;
    }

    public function setCountry(string $country): self
    {
        $this->country = $country;

        return $this;
    }

    public function setCurrency(string $currency): self
    {
        $this->currency = $currency;

        return $this;
    }

    public function setAmount(float $amount): self
    {
        $this->amount = $amount;

        return $this;
    }

    public function setMerchantTransactionId(string $merchantTransactionId
    ): self {
        $this->merchantTransactionId = $merchantTransactionId;

        return $this;
    }

    public function setOperationType(string $operationType): self
    {
        $this->operationType = (string)(new _Enum\ChargeTypeEnum(
            $operationType
        ));

        return $this;
    }

    public function setProductId(int $productId): self
    {
        $this->productId = $productId;

        return $this;
    }

    public function setStatusUrl(string $statusUrl): self
    {
        $this->statusURL = $statusUrl;

        return $this;
    }

    public function setSuccessUrl(string $successUrl): self
    {
        $this->successURL = $successUrl;

        return $this;
    }

    public function setCancelUrl(string $cancelUrl): self
    {
        $this->cancelURL = $cancelUrl;

        return $this;
    }

    public function setErrorUrl(string $errorUrl): self
    {
        $this->errorURL = $errorUrl;

        return $this;
    }

    public function setAdditionalData(array $additionalData): self
    {
        $this->additionalData = $additionalData;

        return $this;
    }

    public function setAutoCapture(bool $autoCapture): self
    {
        $this->autoCapture = $autoCapture;

        return $this;
    }

    public function getCustomerId(): string
    {
        return $this->customerId;
    }

    public function getCountry(): string
    {
        return $this->country;
    }

    public function getCurrency(): string
    {
        return $this->currency;
    }

    public function getAmount(): float
    {
        return $this->amount;
    }

    public function getMerchantTransactionId(): string
    {
        return $this->merchantTransactionId;
    }

    public function getOperationType(): string
    {
        return $this->operationType;
    }

    public function getProductId(): int
    {
        return $this->productId;
    }

    public function getStatusUrl(): string
    {
        return $this->statusURL;
    }

    public function getSuccessUrl(): string
    {
        return $this->successURL;
    }

    public function getCancelUrl(): string
    {
        return $this->cancelURL;
    }

    public function getErrorUrl(): string
    {
        return $this->errorURL;
    }

    public function addData(string $key, $value): self
    {
        $this->additionalData[$key] = $value;

        return $this;
    }

    public function getAutoCapture(): bool
    {
        return $this->autoCapture;
    }

    /**
     * @return array
     */
    public function getAdditionalData(): array
    {
        return $this->additionalData;
    }

    public function addMerchantParam(string $key, $value): self
    {
        $this->merchantParams[$key] = $value;

        return $this;
    }

    public function getMerchantParams(): array
    {
        return $this->merchantParams;
    }

    /**
     * Mark operation to be authorized/capture where possible.
     */
    public function settle(): self
    {
        return $this->addMerchantParam(
            self::FLOW_PARAM_KEY,
            (string)(new _Enum\FlowEnum())
        );
    }

    /**
     * Mark operation to be authorized only (no capture).
     */
    public function preAuthorize(): self
    {
        return $this->addMerchantParam(
            self::FLOW_PARAM_KEY,
            (string)(new _Enum\FlowEnum(_Enum\FlowEnum::PRE_AUTHORIZE))
        );
    }

    /**
     * @return string[]
     */
    public function getOperationChoices(): array
    {
        return array_values((new _Enum\ChargeTypeEnum())->getConstList());
    }

    public function hasPrepayToken(): bool
    {
        $headers = $this->getAdditionalHeaders();

        return \array_key_exists(self::PREPAY_TOKEN_HEADER, $headers)
               && !empty($headers[self::PREPAY_TOKEN_HEADER]);
    }

    /**
     * @Assert\Callback()
     */
    public function validate(ExecutionContextInterface $context, $payload): void
    {
        if (!$this->hasPrepayToken()) {
            $context->buildViolation('The authorization token is missing')
                    ->atPath('additionalHeaders')
                    ->addViolation();
        }
    }

    public function setpaymentSolution(string $paymentSolution)
    {
        $this->paymentSolution = $paymentSolution;

        return $this;
    }

    public function setCustomerCountry(string $country)
    {
        $this->customerCountry = $country;

        return $this;
    }

    public function setCustomerEmail(string $email)
    {
        $this->customerEmail = $email;

        return $this;
    }


    public function setpaysolExtendedData($paysolExtendedData)
    {
        $this->paysolExtendedData = $paysolExtendedData;

        return $this;
    }

    /**
     * @param mixed $firstName
     */
    public function setFirstName($firstName): Charge
    {
        $this->firstName = $firstName;

        return $this;
    }

    /**
     * @param mixed $lastName
     */
    public function setLastName($lastName): Charge
    {
        $this->lastName = $lastName;

        return $this;
    }

    /**
     * @param mixed $language
     */
    public function setLanguage($language): Charge
    {
        $this->language = $language;

        return $this;
    }

    /**
     * @param mixed $ipAddress
     */
    public function setIpAddress($ipAddress): Charge
    {
        $this->ipAddress = $ipAddress;

        return $this;
    }

    /**
     * @param mixed $dob
     */
    public function setDob($dob): Charge
    {
        $this->dob = $dob;

        return $this;
    }

    /**
     * @param mixed $rememberMe
     */
    public function setRememberMe($rememberMe): Charge
    {
        $this->rememberMe = $rememberMe;

        return $this;
    }

    /**
     * @param mixed $account
     */
    public function setAccount($account): Charge
    {
        $this->account = $account;

        return $this;
    }


    function setAwaitingURL(string $url) {
        $this->awaitingURL = $url;
        return $this;
    }

     /**
     * @return string
     */
    public function getAwaitingURL(): string
    {
        return $this->awaitingURL;
    }

    /**
     * @return string
     */
    public function getPaymentSolution(): string
    {
        return $this->paymentSolution;
    }

    /**
     * @return mixed
     */
    public function getCustomerCountry()
    {
        return $this->customerCountry;
    }

    /**
     * @return mixed
     */
    public function getCustomerEmail()
    {
        return $this->customerEmail;
    }

    /**
     * @return mixed
     */
    public function getFirstName()
    {
        return $this->firstName;
    }

    /**
     * @return mixed
     */
    public function getLastName()
    {
        return $this->lastName;
    }

    /**
     * @return mixed
     */
    public function getLanguage()
    {
        return $this->language;
    }

    /**
     * @return mixed
     */
    public function getIpAddress()
    {
        return $this->ipAddress;
    }

    /**
     * @return mixed
     */
    public function getDob()
    {
        return $this->dob;
    }

    /**
     * @return mixed
     */
    public function getRememberMe()
    {
        return $this->rememberMe;
    }

    /**
     * @return mixed
     */
    public function getAccount()
    {
        return $this->account;
    }

    /**
     * @return mixed
     */
    public function getPaysolExtendedData()
    {
        return $this->paysolExtendedData;
    }

    public function setApiVersion($version)
    {
        $this->apiVersion = $version;
        return $this;
    }

}
