<?php
/**
 * 2007-2021 PrestaShop
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to http://www.prestashop.com for more information.
 *
 * @author    PrestaShop SA <contact@prestashop.com>
 * @copyright 2007-2021 PrestaShop SA
 * @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License
 *            (AFL 3.0) International Registered Trademark & Property of
 *            PrestaShop SA
 */

namespace Prestashop\Module\AddonPayments\Helpers;

use Address;
use CardAttempts;
use Cart;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\CardHolderAccountInfo;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\Enum\AccountAgeEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\Enum\BillAddressMatchEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\Enum\ShippingMethodEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\Enum\ShippingNameIndEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\Enum\SuspiciousAccountActivityIndEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\Enum\TransactionTypeEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\RiskValidation;
use ComerciaGlobalPayments\AddonPayments\SDK\_3DSecure\v2\ShippingAddress;
use Context;
use Country;
use Customer;
use DateInterval;
use DateTime;
use Prestashop\Module\AddonPayments\_3DSecure\V2\AccountAge;
use Prestashop\Module\AddonPayments\_3DSecure\V2\DeliveryTImeFrame;
use Prestashop\Module\AddonPayments\_3DSecure\V2\PreOrderPurchase;
use Prestashop\Module\AddonPayments\_3DSecure\V2\ReorderItems;
use Prestashop\Module\AddonPayments\_3DSecure\V2\ShippingAddressUsage;
use Prestashop\Module\AddonPayments\_3DSecure\V2\ShippingMethod;
use Transactions;

/**
 * Implements 3DSecureV2 helper methods.
 *
 * @TODO Need some refactoring when working with orders instead of carts.
 */
trait _3DSecureV2Trait
{
    public function getRiskValidation(): ?RiskValidation
    {
        if (null === $this->getCart()) {
            return null;
        }

        return (new RiskValidation())
            // @TODO: Implement sort of method to get the challenge value.
            ->setDeliveryTimeframe((new DeliveryTImeFrame($this->getCart()))())
            ->setPreOrderDate((new PreOrderPurchase($this->getCart()))())
            ->setReorderItems((new ReorderItems($this->getCart()))())
            ->setShippingMethod((new ShippingMethod($this->getCart()))())
            // @TODO: Get the transaction type some how.
            ->setTransactionTypeId((string)new TransactionTypeEnum());
    }

    protected function getCart(): Cart
    {
        return $this->getContext()->cart;
    }

    protected function getContext(): Context
    {
        return $this->context ?? Context::getContext();
    }

    public function getCardHolderAccountInfo(): ?CardHolderAccountInfo
    {
        $customer = $this->getCustomer();
        $info = new CardHolderAccountInfo();

        if (!$customer->isLogged()) {
            $info->setAccountAge((string)new AccountAgeEnum(AccountAgeEnum::NO_ACCOUNT));

            return $info;
        }

        $inputFormat = 'Y-m-d H:i:s';
        $outputFormat = 'Y-m-d';
        $info->setAccountAge((new AccountAge($customer))())
            ->setAccountCreationDate($this->formatDate($customer->date_add, $inputFormat, $outputFormat))
            ->setAccountChangeDate($this->formatDate($customer->date_upd, $inputFormat, $outputFormat))
            ->setPasswordChangeDate($this->formatDate($customer->last_passwd_gen, $inputFormat, $outputFormat))
            // @TODO: Implement a method to calculate payment solution account date.
            ->setProvisioningAttemptsDay(
                CardAttempts::getAttemptsByUserBetweenDates(
                    $customer->id,
                    new DateTime(),
                    (new DateTime())->sub(new DateInterval('P1D'))
                )
            )
            ->setPurchasesLast6Months(
                CardAttempts::getAttemptsByUserBetweenDates(
                    $customer->id,
                    new DateTime(),
                    (new DateTime())->sub(new DateInterval('P6M'))
                )
            )
            ->setShippingAddressUsage((new ShippingAddressUsage($this->getCart()))());

        if (null !== $info->getShippingAddressUsage()) {
            $info->setShippingAddressUsage(
                $this->formatDate($info->getShippingAddressUsage(), $inputFormat, $outputFormat)
            );
        }

        $info->setShippingNameInd($this->getShippingAddressNameInd())
            ->setSuspiciousAccountActivityInd((string)new SuspiciousAccountActivityIndEnum())
            ->setTxnActivityDay(
                Transactions::getTransactionsAttemptsByUserBetweenDates(
                    $customer->id,
                    (new DateTime()),
                    (new DateTime())->sub(new DateInterval('P1D'))
                )
            )
            ->setTxnActivityYear(
                Transactions::getTransactionsAttemptsByUserBetweenDates(
                    $customer->id,
                    (new DateTime()),
                    (new DateTime())->sub(new DateInterval('P1Y'))
                )
            );

        return $info;
    }

    protected function getCustomer(): Customer
    {
        return new Customer($this->getCart()->id_customer);
    }

    protected function formatDate(string $date, string $inputFormat, string $outputFormat): string
    {
        return (DateTime::createFromFormat(
            $inputFormat,
            $date
        ))->format(
            $outputFormat
        );
    }

    protected function getShippingAddressNameInd(): string
    {
        $customer = $this->getCustomer();
        $deliveryAddress = new Address($this->getCart()->id_address_delivery);

        if (null !== $customer
            && null !== $deliveryAddress
            && $customer->firstname === $deliveryAddress->firstname
            && $customer->lastname === $deliveryAddress->lastname
        ) {
            return (string)new ShippingNameIndEnum(ShippingNameIndEnum::IDENTICAL);
        }

        return (string)new ShippingNameIndEnum(ShippingNameIndEnum::DIFFERENT);
    }

    public function getShippingAddress(): ?ShippingAddress
    {
        $idAddressDelivery = $this->getCart()->id_address_delivery;

        if (!$idAddressDelivery || null === ($deliveryAddress = new Address($idAddressDelivery))) {
            return null;
        }

        $shippingAddress = (new ShippingAddress())
            ->setAddress1($deliveryAddress->address1)
            ->setAddress2($deliveryAddress->address2)
            ->setCity($deliveryAddress->city)
            ->setZipCode($deliveryAddress->postcode)
            ->setCountry((new Country($deliveryAddress->id_country))->iso_code)
            ->setPhone($deliveryAddress->phone);

        $shippingMethod = (new ShippingMethod($this->getCart()))();
        $deliveryOption = $this->getCart()->getDeliveryOption(null, true);

        if (ShippingMethodEnum::DIGITAL_GOODS === $shippingMethod
            || empty($deliveryOption)
        ) {
            $shippingAddress->setDeliveryEmail($this->getCustomer()->email);
        }

        $sameAddress = ShippingMethodEnum::BILLING_ADDRESS === $shippingMethod;
        $shippingAddress->setBillAddressMatchYN((string)new BillAddressMatchEnum(
            $sameAddress
                ? BillAddressMatchEnum::YES
                : BillAddressMatchEnum::NO
        ));

        return $shippingAddress;
    }
}
