<?php

namespace App\Controller;

use App\Entity\GlobalParameter;
use App\Utils\OperationUtils;
use Doctrine\ORM\EntityManagerInterface;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\GenericProvider;
use Psr\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security as SecurityS;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class HelloAssoApiController extends AbstractController
{
    protected $client;
    protected $em;
    protected $security;
    protected $translator;
    protected $eventDispatcher;
    protected $operationUtils;

    const CASH_OUT_STATE_MONEY_IN = 'MoneyIn';
    const CASH_OUT_STATE_CANT_TRANSFER_RECEIVER_FULL = 'CantTransferReceiverFull';
    const CASH_OUT_STATE_TRANSFERED = 'Transfered';
    const CASH_OUT_STATE_REFUNDED = 'Refunded';
    const CASH_OUT_STATE_REFUNDING = 'Refunding';
    const CASH_OUT_STATE_WAITING_FOR_CASH_OUT_CONFIRMATION = 'WaitingForCashOutConfirmation';
    const CASH_OUT_STATE_CASHED_OUT = 'CashedOut';
    const CASH_OUT_STATE_UNKNOWN = 'Unknown';
    const CASH_OUT_STATE_CONTESTED = 'Contested';

    const PAYMENT_MEANS_NONE = 'None';
    const PAYMENT_MEANS_CARD = 'Card';
    const PAYMENT_MEANS_SEPA = 'Sepa';
    const PAYMENT_MEANS_CHECK = 'Check';

    const STATE_PENDING = 'Pending';
    const STATE_AUTHORIZED = 'Authorized';
    const STATE_REFUSED = 'Refused';
    const STATE_UNKNOWN = 'Unknown';
    const STATE_REGISTERED = 'Registered';
    const STATE_ERROR = 'Error';
    const STATE_REFUNDED = 'Refunded';
    const STATE_REFUNDING = 'Refunding';
    const STATE_WAITING = 'Waiting';
    const STATE_CANCELED = 'Canceled';
    const STATE_CONTESTED = 'Contested';
    const STATE_WAITING_BANK_VALIDATION = 'WaitingBankValidation';
    const STATE_WAITING_BANK_WITHDRAW = 'WaitingBankWithdraw';

    const TYPE_OFFLINE = 'Offline';
    const TYPE_CREDIT = 'Credit';
    const TYPE_DEBIT = 'Debit';

    public function __construct(
        HttpClientInterface $client,
        Security $security,
        EntityManagerInterface $em,
        TranslatorInterface $translator,
        EventDispatcherInterface $eventDispatcher,
        LoggerInterface $logger,
        OperationUtils $operationUtils
    ) {
        $this->client = $client;
        $this->security = $security;
        $this->em = $em;
        $this->translator = $translator;
        $this->eventDispatcher = $eventDispatcher;
        $this->operationUtils = $operationUtils;
    }

    /**
     * Gets allowable values of the enum.
     *
     * @return string[]
     */
    public static function getCashOutStateAllowableValues()
    {
        return [
            self::CASH_OUT_STATE_MONEY_IN,
            self::CASH_OUT_STATE_CANT_TRANSFER_RECEIVER_FULL,
            self::CASH_OUT_STATE_TRANSFERED,
            self::CASH_OUT_STATE_REFUNDED,
            self::CASH_OUT_STATE_REFUNDING,
            self::CASH_OUT_STATE_WAITING_FOR_CASH_OUT_CONFIRMATION,
            self::CASH_OUT_STATE_CASHED_OUT,
            self::CASH_OUT_STATE_UNKNOWN,
            self::CASH_OUT_STATE_CONTESTED,
        ];
    }

    /**
     * Gets allowable values of the enum.
     *
     * @return string[]
     */
    public static function getPaymentMeansAllowableValues()
    {
        return [
            self::PAYMENT_MEANS_NONE,
            self::PAYMENT_MEANS_CARD,
            self::PAYMENT_MEANS_SEPA,
            self::PAYMENT_MEANS_CHECK,
        ];
    }

    /**
     * Gets allowable values of the enum.
     *
     * @return string[]
     */
    public static function getStateAllowableValues()
    {
        return [
            self::STATE_PENDING,
            self::STATE_AUTHORIZED,
            self::STATE_REFUSED,
            self::STATE_UNKNOWN,
            self::STATE_REGISTERED,
            self::STATE_ERROR,
            self::STATE_REFUNDED,
            self::STATE_REFUNDING,
            self::STATE_WAITING,
            self::STATE_CANCELED,
            self::STATE_CONTESTED,
            self::STATE_WAITING_BANK_VALIDATION,
            self::STATE_WAITING_BANK_WITHDRAW,
        ];
    }

    /**
     * Gets allowable values of the enum.
     *
     * @return string[]
     */
    public static function getTypeAllowableValues()
    {
        return [
            self::TYPE_OFFLINE,
            self::TYPE_CREDIT,
            self::TYPE_DEBIT,
        ];
    }

    /**
     * @Route("/helloasso/sync/orders", name="helloasso_api_synchro")
     * @SecurityS("(is_granted('ROLE_SUPER_ADMIN') or is_granted('ROLE_ADMIN_SIEGE') or is_granted('ROLE_TRESORIER'))")
     */
    public function synchroItems(int $pageIndex = 1, int $pageSize = 100, Request $request)
    {
        $accessToken = $this->authorize();
        $cache = new FilesystemAdapter();

        // API url to get all payments (> 0)
        // https://api.helloasso.com/v5/organizations/federation-kohinos/payments

        // To get all order (>= 0) with free cotisation or example !
        $response = $this->client->request('GET', 'https://api.helloasso.com/v5/organizations/federation-kohinos/items?pageSize=' . $pageSize . '&pageIndex=' . $pageIndex, [
            'auth_bearer' => $accessToken->getToken(),
        ]);
        $data = $response->toArray();

        return new JsonResponse(['data' => $data]);
    }

    public function authorize()
    {
        $helloClientId = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::HELLOASSO_CLIENTID);
        $helloClientSecret = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::HELLOASSO_CLIENTSECRET);
        if (empty($helloClientId) || empty($helloClientSecret)) {
            throw new \Exception('NO HELLOASSO ID AND SECRET CONFIGURED ! ');
        }
        $cache = new FilesystemAdapter();

        $provider = new GenericProvider([
            'clientId' => $helloClientId,                                   // The client ID assigned to you by the provider
            'clientSecret' => $helloClientSecret,                           // The client password assigned to you by the provider
            // 'redirectUri'             => 'https://kohinos.test',         // Necessary ?! not sure
            'urlAuthorize' => 'https://auth.helloasso.com/authorize',       // Url for authorization
            'urlAccessToken' => 'https://api.helloasso.com/oauth2/token',   // Url to get an access token
            'urlResourceOwnerDetails' => 'https://api.helloasso.com',       // API base endpoint url of Helloasso
        ]);

        $value = $cache->get('helloasso_oauth_token2', function (ItemInterface $item) use ($provider) {
            try {
                // Try to get an access token using the client credentials grant.
                $accessToken = $provider->getAccessToken('client_credentials');
            } catch (IdentityProviderException $e) {
                // Failed to get the access token
                exit($e->getMessage());
            }
            $item->expiresAfter($accessToken->getExpires() - time());

            return $accessToken;
        });

        // Item expiresAfter does'nt work ! (don't know why...) So if access token has been expired, get a new one with refresh_token !
        if ($value->hasExpired()) {
            $cache->delete('helloasso_oauth_token2');
            $value = $cache->get('helloasso_oauth_token2', function (ItemInterface $item) use ($provider, $value) {
                try {
                    $newValue = $provider->getAccessToken('refresh_token', [
                        'refresh_token' => $value->getRefreshToken(),
                    ]);
                } catch (IdentityProviderException $e) {
                    // Failed to get the access token
                    exit($e->getMessage());
                }
                $item->expiresAfter($newValue->getExpires() - time());

                return $newValue;
            });
        }

        return $value;
    }
}
