<?php

namespace App\Controller;

use App\Entity\Adherent;
use App\Entity\Cotisation;
use App\Entity\GlobalParameter;
use App\Entity\HelloAsso;
use App\Entity\Prestataire;
use App\Entity\User;
use App\Enum\HelloassoStateEnum;
use App\Utils\HelloassoUtils;
use App\Utils\OperationUtils;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class HelloAssoCallbackController extends AbstractController
{
    protected $em;
    protected $logger;
    protected $security;
    protected $translator;
    protected $eventDispatcher;
    protected $operationUtils;
    protected $client;
    protected $helloassoUtils;

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

    /**
     * @Route("/helloasso/callback", methods="POST", name="hello_asso_callback")
     */
    public function callback(Request $request): Response
    {
        // Make sure that the client is providing something that we can consume
        $consumes = ['application/json', 'text/json'];
        if (!static::isContentTypeAllowed($request, $consumes)) {
            // We can't consume the content that the client is sending us
            return new Response('', 415);
        }

        $arrayResult = json_decode($request->getContent(), true);
        $flux = null;

        // eventType : Order / Payment / Form
        if ('Order' == $arrayResult['eventType']) {
            $data = $arrayResult['data'];
            foreach ($data['items'] as $items) {
                $itemId = $items['id'];

                // state : "Waiting", "Processed", "Registered", "Deleted", "Refunded", "Unknown", "Canceled", "Contested"
                $itemState = $items['state'];
                $payments = isset($data['payments']) ? $data['payments'] : null;
                $statePayment = 'NONE';
                $paymentReceiptUrl = '';
                if (null != $payments) {
                    $statePayment = isset($payments[0]['state']) ? $payments[0]['state'] : 'NONE';
                    $paymentReceiptUrl = isset($payments[0]['paymentReceiptUrl']) ? $payments[0]['paymentReceiptUrl'] : '';
                }

                // Create HelloAsso entity
                $helloasso = new HelloAsso();
                $helloasso->setHistorical(false);
                $helloasso->setData($arrayResult);

                $helloasso->setHelloassoid((int) $itemId);
                $itemMontant = floatval($items['amount']) / 100;
                $helloasso->setAmount($itemMontant);

                $payer = $data['payer'];
                $payerCompany = isset($payer['company']) ? $payer['company'] : '';
                $helloasso->setUserfirstname($payerCompany);
                $payerEmail = $payer['email'];
                $helloasso->setPayeremail($payerEmail);
                $payerFirstName = $payer['firstName'];
                $helloasso->setPayerfirstname($payerFirstName);
                $payerLastName = $payer['lastName'];
                $helloasso->setPayerlastname($payerLastName);

                // type : "Donation", "Payment", "Registration", "Membership", "MonthlyDonation", "MonthlyPayment", "OfflineDonation", "Contribution", "Bonus"
                $itemType = $items['type'];
                $helloasso->setType($itemType);

                $userFirstname = $payer['firstName'];
                $userLastname = $payer['lastName'];
                $userEmail = $payer['email'];
                $userAdress = '';
                $userZipcode = '';
                $userCity = '';
                if ('Membership' == $itemType) {
                    $customFields = isset($items['customFields']) ? $items['customFields'] : [];
                    foreach ($customFields as $customField) {
                        if ('Email' == $customField['name']) {
                            $userEmail = $customField['answer'] ? $customField['answer'] : $userEmail;
                            $helloasso->setUseremail($userEmail);
                        } elseif ('Adresse' == $customField['name']) {
                            $userAdress = $customField['answer'];
                        } elseif ('Code Postal' == $customField['name']) {
                            $userZipcode = $customField['answer'];
                        } elseif ('Ville' == $customField['name']) {
                            $userCity = $customField['answer'];
                        }
                    }
                    $user = $items['user'];
                    $userFirstname = $user['firstName'] ? $user['firstName'] : $userFirstname;
                    $helloasso->setUserfirstname($userFirstname);
                    $userLastname = $user['lastName'] ? $user['lastName'] : $userLastname;
                    $helloasso->setUserlastname($userLastname);
                }

                $helloasso->setState($itemState);
                $helloasso->setStatePayment($statePayment);
                $helloasso->setPaymentReceiptUrl($paymentReceiptUrl);
                // priceCategory : "Fixed", "Pwyw", "Free" (Free, Fixed or Pay what you want)
                // $itemState = $items['priceCategory'];
                $helloasso->setOperationState(HelloassoStateEnum::HELLOASSO_STATE_START);
                try {
                    if (!empty($payerCompany)) {
                        $prestataire = $this->findPrestataireWithData($payerCompany, $userFirstname, $userLastname, $userEmail, $payer, $userAdress, $userZipcode, $userCity);
                    }
                    if (empty($prestataire)) {
                        $adherent = $this->findAdherentWithData($userFirstname, $userLastname, $userEmail, $payer, $userAdress, $userZipcode, $userCity);
                        if (empty($adherent)) {
                            $prestataire = $this->findPrestataireWithData($payerCompany, $userFirstname, $userLastname, $userEmail, $payer, $userAdress, $userZipcode, $userCity);
                            if (empty($prestataire)) {
                                $helloasso->setOperationState(HelloassoStateEnum::HELLOASSO_STATE_ERROR_NOT_FOUND);
                                $helloasso->setErrors('Adherent or Prestataire not found !');
                            } else {
                                $helloasso->setPrestataire($prestataire);
                            }
                        } else {
                            $helloasso->setAdherent($adherent);
                        }
                    } else {
                        $helloasso->setPrestataire($prestataire);
                    }
                } catch (\Exception $e) {
                    $helloasso->setErrors($e->getMessage());
                    $response = new Response('Erreur : ' . $e->getMessage(), 500);
                }
                $this->em->persist($helloasso);
                $this->em->flush();
                try {
                    // Si le paiement est autorisé et qu'on a trouvé le prestataire ou l'adhérent, on crée le flux et on execute les opérations
                    if ('Processed' == $itemState && ('Authorized' == $statePayment || 'Processed' == $statePayment) && null != $helloasso->getPrestataire() || null != $helloasso->getAdherent()) {
                        if ('Membership' == $itemType) {
                            // COTISATION
                            $flux = $this->helloassoUtils->addCotisation($helloasso);
                            $helloasso->setFlux($flux);
                        } elseif ('Payment' == $itemType) {
                            // ACHAT EMLC
                            $flux = $this->helloassoUtils->addAchatEmlc($helloasso);
                            $helloasso->setFlux($flux);
                        } elseif ('Donation' == $itemType) {
                            // DON
                            $don = $this->helloassoUtils->addDonation($helloasso);
                            if (null != $flux) {
                                // ASSOCIATE DON WITH OTHER FLUX
                                $flux->setDon($don);
                                $this->em->persist($flux);
                            }
                            $helloasso->setFlux($don);
                        }
                        $helloasso->setOperationState(HelloassoStateEnum::HELLOASSO_STATE_OK);
                    } else {
                        $helloasso->setOperationState(HelloassoStateEnum::HELLOASSO_STATE_OK);
                    }
                    $this->em->persist($helloasso);
                    $this->em->flush();
                } catch (\Exception $e) {
                    $helloasso->setErrors($e->getMessage());
                    $helloasso->setOperationState(HelloassoStateEnum::HELLOASSO_STATE_ERROR_FLUX);
                    $this->em->persist($helloasso);
                    $this->em->flush();
                    $response = new Response('Erreur : ' . $e->getMessage(), 500);
                }
            }
        }
        $response = new Response('OK', 200);

        return $response;
    }

    private function findPrestataireWithData(string $raison, ?string $userFirstname, ?string $userLastname, ?string $userEmail, array $payer, ?string $userAdress, ?string $userZipcode, ?string $userCity): ?Prestataire
    {
        //@ TODO
        $presta = null;
        if (!empty($raison)) {
            $presta = $this->em->getRepository(Prestataire::class)->findBy(['raison' => $raison]);
            if (!empty($presta) && 1 == count($presta)) {
                return $presta[0];
            }
        }
        if (!empty($userEmail)) {
            $user = $this->em->getRepository(User::class)->findBy(['email' => $userEmail]);
            if (!empty($user)) {
                $prestas = $this->em->getRepository(Prestataire::class)->findByData(['user' => $user]);
                if (!empty($presta) && 1 == count($presta)) {
                    return $presta[0];
                } else {
                    foreach ($prestas as $presta) {
                        if ($presta->getResponsable() == $userFirstname
                         || $presta->getResponsable() == $userLastname
                         || $presta->getResponsable() == $userFirstname . ' ' . $userLastname
                         || $presta->getResponsable() == $userLastname . ' ' . $userFirstname) {
                            return $presta;
                        }
                    }
                }
            }
        }

        return $presta;
    }

    private function findAdherentWithData(string $userFirstname, string $userLastname, ?string $userEmail, array $payer, ?string $userAdress, ?string $userZipcode, ?string $userCity): ?Adherent
    {
        $adh = null;
        if (!empty($userEmail)) {
            $adh = $this->em->getRepository(Adherent::class)->findByData(['email' => $userEmail]);
        }
        if (empty($adh)) {
            $adh = $this->em->getRepository(Adherent::class)->findByData(['firstname' => $userFirstname, 'lastname' => $userLastname]);
        }
        if (empty($adh) && !empty($payer['email'])) {
            $adh = $this->em->getRepository(Adherent::class)->findByEmail($payer['email']);
        }

        return $adh;
    }

    private function sendMail($name, $from, $to, $message)
    {
        if (empty($to)) {
            $to = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_CONTACT_EMAIL);
        }
        $subject = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL) . " : HelloAsso : Reception d'un paiement";
        $mail = (new \Swift_Message($subject))
            ->setFrom($from)
            ->setTo($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_CONTACT_EMAIL))
            ->setBody(
                $this->templating->render(
                    '@kohinos/email/contact.html.twig',
                    [
                        'subject' => $subject,
                        'from' => $from,
                        'name' => $name,
                        'message' => $message,
                    ]
                ),
                'text/html'
            );
        $this->mailer->send($mail);
    }

    /**
     * Checks whether Content-Type request header presented in supported formats.
     *
     * @param Request $request  request instance
     * @param array   $consumes array of supported content types
     *
     * @return bool returns true if Content-Type supported otherwise false
     */
    public static function isContentTypeAllowed(Request $request, array $consumes = [])
    {
        if (!empty($consumes) && '*/*' !== $consumes[0]) {
            $currentFormat = $request->getContentType();
            foreach ($consumes as $mimeType) {
                // canonize mime type
                if (is_string($mimeType) && false !== $pos = strpos($mimeType, ';')) {
                    $mimeType = trim(substr($mimeType, 0, $pos));
                }

                if (!$format = $request->getFormat($mimeType)) {
                    // add custom format to request
                    $format = $mimeType;
                    $request->setFormat($format, $format);
                    $currentFormat = $request->getContentType();
                }

                if ($format === $currentFormat) {
                    return true;
                }
            }

            return false;
        }

        return true;
    }
}
