<?php
/*************************************************************************************/
/*      This file is part of the Thelia package.                                     */
/*                                                                                   */
/*      Copyright (c) OpenStudio                                                     */
/*      email : dev@thelia.net                                                       */
/*      web : http://www.thelia.net                                                  */
/*                                                                                   */
/*      For the full copyright and license information, please view the LICENSE.txt  */
/*      file that was distributed with this source code.                             */
/*************************************************************************************/

namespace Thelia\Action;

use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Thelia\Core\Event\Order\GetStockUpdateOperationOnOrderStatusChangeEvent;
use Thelia\Core\Event\Order\OrderAddressEvent;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\Order\OrderManualEvent;
use Thelia\Core\Event\Order\OrderPaymentEvent;
use Thelia\Core\Event\Product\VirtualProductOrderHandleEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Security\SecurityContext;
use Thelia\Core\Security\User\UserInterface;
use Thelia\Exception\TheliaProcessException;
use Thelia\Log\Tlog;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\AddressQuery;
use Thelia\Model\Cart as CartModel;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Currency as CurrencyModel;
use Thelia\Model\Lang as LangModel;
use Thelia\Model\Map\OrderTableMap;
use Thelia\Model\ModuleQuery;
use Thelia\Model\Order as ModelOrder;
use Thelia\Model\Order as OrderModel;
use Thelia\Model\OrderAddress;
use Thelia\Model\OrderAddressQuery;
use Thelia\Model\OrderProduct;
use Thelia\Model\OrderProductAttributeCombination;
use Thelia\Model\OrderProductTax;
use Thelia\Model\OrderStatusQuery;
use Thelia\Model\OrderVersionQuery;
use Thelia\Model\ProductI18n;
use Thelia\Model\ProductSaleElements;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\TaxRuleI18n;
use Thelia\Tools\I18n;

/**
 *
 * Class Order
 * @package Thelia\Action
 * @author Etienne Roudeix <eroudeix@openstudio.fr>
 * @author Franck Allimant <thelia@cqfdev.fr>
 */
class Order extends BaseAction implements EventSubscriberInterface
{
    /** @var RequestStack */
    protected $requestStack;

    /** @var MailerFactory */
    protected $mailer;

    /** @var SecurityContext */
    protected $securityContext;

    public function __construct(RequestStack $requestStack, MailerFactory $mailer, SecurityContext $securityContext)
    {
        $this->requestStack = $requestStack;
        $this->mailer = $mailer;
        $this->securityContext = $securityContext;
    }

    /**
     * @param \Thelia\Core\Event\Order\OrderEvent $event
     */
    public function setDeliveryAddress(OrderEvent $event)
    {
        $order = $event->getOrder();

        $order->setChoosenDeliveryAddress($event->getDeliveryAddress());

        $event->setOrder($order);
    }

    /**
     * @param \Thelia\Core\Event\Order\OrderEvent $event
     */
    public function setDeliveryModule(OrderEvent $event)
    {
        $order = $event->getOrder();

        $deliveryModuleId = $event->getDeliveryModule();

        $order->setDeliveryModuleId($deliveryModuleId);

        // Reset postage cost if the delivery module had been removed
        if ($deliveryModuleId <= 0) {
            $order->setPostage(0);
            $order->setPostageTax(0);
            $order->setPostageTaxRuleTitle(null);
        }

        $event->setOrder($order);
    }

    /**
     * @param \Thelia\Core\Event\Order\OrderEvent $event
     */
    public function setPostage(OrderEvent $event)
    {
        $order = $event->getOrder();

        $order->setPostage($event->getPostage());
        $order->setPostageTax($event->getPostageTax());
        $order->setPostageTaxRuleTitle($event->getPostageTaxRuleTitle());

        $event->setOrder($order);
    }

    /**
     * @param \Thelia\Core\Event\Order\OrderEvent $event
     */
    public function setInvoiceAddress(OrderEvent $event)
    {
        $order = $event->getOrder();

        $order->setChoosenInvoiceAddress($event->getInvoiceAddress());

        $event->setOrder($order);
    }

    /**
     * @param \Thelia\Core\Event\Order\OrderEvent $event
     */
    public function setPaymentModule(OrderEvent $event)
    {
        $order = $event->getOrder();

        $order->setPaymentModuleId($event->getPaymentModule());

        $event->setOrder($order);
    }

    /**
     * @param EventDispatcherInterface $dispatcher
     * @param ModelOrder $sessionOrder
     * @param CurrencyModel $currency
     * @param LangModel $lang
     * @param CartModel $cart
     * @param UserInterface $customer
     * @param bool $unusedArgument deprecated argument. Will be removed in 2.5
     * @param bool $useOrderDefinedAddresses if true, the delivery and invoice OrderAddresses will be used instead of creating new OrderAdresses using Order::getChoosenXXXAddress()
     * @return ModelOrder
     * @throws \Exception
     * @throws \Propel\Runtime\Exception\PropelException
     */
    protected function createOrder(
        EventDispatcherInterface $dispatcher,
        ModelOrder $sessionOrder,
        CurrencyModel $currency,
        LangModel $lang,
        CartModel $cart,
        UserInterface $customer,
        $unusedArgument = null,
        $useOrderDefinedAddresses = false
    ) {
        $con = Propel::getConnection(
            OrderTableMap::DATABASE_NAME
        );

        $con->beginTransaction();

        $placedOrder = $sessionOrder->copy();

        // Be sure to create a brand new order, as copy raises the modified flag for all fields
        // and will also copy order reference and id.
        $placedOrder->setId(null)->setRef(null)->setNew(true);

        // Dates should be marked as not updated so that Propel will update them.
        $placedOrder->resetModified(OrderTableMap::COL_CREATED_AT);
        $placedOrder->resetModified(OrderTableMap::COL_UPDATED_AT);
        $placedOrder->resetModified(OrderTableMap::COL_VERSION_CREATED_AT);

        $placedOrder->setDispatcher($dispatcher);

        $cartItems = $cart->getCartItems();

        /* fulfill order */
        $placedOrder->setCustomerId($customer->getId());
        $placedOrder->setCurrencyId($currency->getId());
        $placedOrder->setCurrencyRate($currency->getRate());
        $placedOrder->setLangId($lang->getId());

        if ($useOrderDefinedAddresses) {
            $taxCountry =
                OrderAddressQuery::create()
                    ->findPk($placedOrder->getDeliveryOrderAddressId())
                    ->getCountry()
            ;
        } else {
            $deliveryAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenDeliveryAddress());
            $invoiceAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenInvoiceAddress());

            /* hard save the delivery and invoice addresses */
            $deliveryOrderAddress = new OrderAddress();
            $deliveryOrderAddress
                ->setCustomerTitleId($deliveryAddress->getTitleId())
                ->setCompany($deliveryAddress->getCompany())
                ->setFirstname($deliveryAddress->getFirstname())
                ->setLastname($deliveryAddress->getLastname())
                ->setAddress1($deliveryAddress->getAddress1())
                ->setAddress2($deliveryAddress->getAddress2())
                ->setAddress3($deliveryAddress->getAddress3())
                ->setZipcode($deliveryAddress->getZipcode())
                ->setCity($deliveryAddress->getCity())
                ->setPhone($deliveryAddress->getPhone())
                ->setCellphone($deliveryAddress->getCellphone())
                ->setCountryId($deliveryAddress->getCountryId())
                ->setStateId($deliveryAddress->getStateId())
                ->save($con);

            $invoiceOrderAddress = new OrderAddress();
            $invoiceOrderAddress
                ->setCustomerTitleId($invoiceAddress->getTitleId())
                ->setCompany($invoiceAddress->getCompany())
                ->setFirstname($invoiceAddress->getFirstname())
                ->setLastname($invoiceAddress->getLastname())
                ->setAddress1($invoiceAddress->getAddress1())
                ->setAddress2($invoiceAddress->getAddress2())
                ->setAddress3($invoiceAddress->getAddress3())
                ->setZipcode($invoiceAddress->getZipcode())
                ->setCity($invoiceAddress->getCity())
                ->setPhone($invoiceAddress->getPhone())
                ->setCellphone($invoiceAddress->getCellphone())
                ->setCountryId($invoiceAddress->getCountryId())
                ->setStateId($deliveryAddress->getStateId())
                ->save($con);

            $placedOrder->setDeliveryOrderAddressId($deliveryOrderAddress->getId());
            $placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());

            $taxCountry = $deliveryAddress->getCountry();
        }

        $placedOrder->setStatusId(
            OrderStatusQuery::getNotPaidStatus()->getId()
        );

        $placedOrder->setCartId($cart->getId());

        /* memorize discount */
        $placedOrder->setDiscount(
            $cart->getDiscount()
        );

        $placedOrder->save($con);

        $manageStock = $placedOrder->isStockManagedOnOrderCreation($dispatcher);

        /* fulfill order_products and decrease stock */

        foreach ($cartItems as $cartItem) {
            $product = $cartItem->getProduct();

            /* get translation */
            /** @var ProductI18n $productI18n */
            $productI18n = I18n::forceI18nRetrieving($lang->getLocale(), 'Product', $product->getId());

            $pse = $cartItem->getProductSaleElements();

            // get the virtual document path
            $virtualDocumentEvent = new VirtualProductOrderHandleEvent($placedOrder, $pse->getId());
            // essentially used for virtual product. modules that handles virtual product can
            // allow the use of stock even for virtual products
            $useStock = true;
            $virtual = 0;

            // if the product is virtual, dispatch an event to collect information
            if ($product->getVirtual() === 1) {
                $dispatcher->dispatch(TheliaEvents::VIRTUAL_PRODUCT_ORDER_HANDLE, $virtualDocumentEvent);
                $useStock = $virtualDocumentEvent->isUseStock();
                $virtual = $virtualDocumentEvent->isVirtual() ? 1 : 0;
            }

            /* check still in stock */
            if ($cartItem->getQuantity() > $pse->getQuantity()
                && true === ConfigQuery::checkAvailableStock()
                && $useStock) {
                throw new TheliaProcessException("Not enough stock", TheliaProcessException::CART_ITEM_NOT_ENOUGH_STOCK, $cartItem);
            }

            if ($useStock && $manageStock) {
                /* decrease stock for non virtual product */
                $allowNegativeStock = \intval(ConfigQuery::read('allow_negative_stock', 0));
                $newStock = $pse->getQuantity() - $cartItem->getQuantity();
                //Forbid negative stock
                if ($newStock < 0 && 0 === $allowNegativeStock) {
                    $newStock = 0;
                }
                $pse->setQuantity(
                    $newStock
                );

                $pse->save($con);
            }

            /* get tax */
            /** @var TaxRuleI18n $taxRuleI18n */
            $taxRuleI18n = I18n::forceI18nRetrieving($lang->getLocale(), 'TaxRule', $product->getTaxRuleId());

            $taxDetail = $product->getTaxRule()->getTaxDetail(
                $product,
                $taxCountry,
                $cartItem->getPrice(),
                $cartItem->getPromoPrice(),
                $lang->getLocale()
            );

            $orderProduct = new OrderProduct();
            $orderProduct
                ->setOrderId($placedOrder->getId())
                ->setProductRef($product->getRef())
                ->setProductSaleElementsRef($pse->getRef())
                ->setProductSaleElementsId($pse->getId())
                ->setTitle($productI18n->getTitle())
                ->setChapo($productI18n->getChapo())
                ->setDescription($productI18n->getDescription())
                ->setPostscriptum($productI18n->getPostscriptum())
                ->setVirtual($virtual)
                ->setVirtualDocument($virtualDocumentEvent->getPath())
                ->setQuantity($cartItem->getQuantity())
                ->setPrice($cartItem->getPrice())
                ->setPromoPrice($cartItem->getPromoPrice())
                ->setWasNew($pse->getNewness())
                ->setWasInPromo($cartItem->getPromo())
                ->setWeight($pse->getWeight())
                ->setTaxRuleTitle($taxRuleI18n->getTitle())
                ->setTaxRuleDescription($taxRuleI18n->getDescription())
                ->setEanCode($pse->getEanCode())
                ->setCartItemId($cartItem->getId())
                ->setDispatcher($dispatcher)
                ->save($con)
            ;

            /* fulfill order_product_tax */
            /** @var OrderProductTax $tax */
            foreach ($taxDetail as $tax) {
                $tax->setOrderProductId($orderProduct->getId());
                $tax->save($con);
            }

            /* fulfill order_attribute_combination and decrease stock */
            foreach ($pse->getAttributeCombinations() as $attributeCombination) {
                /** @var \Thelia\Model\Attribute $attribute */
                $attribute = I18n::forceI18nRetrieving($lang->getLocale(), 'Attribute', $attributeCombination->getAttributeId());

                /** @var \Thelia\Model\AttributeAv $attributeAv */
                $attributeAv = I18n::forceI18nRetrieving($lang->getLocale(), 'AttributeAv', $attributeCombination->getAttributeAvId());

                $orderAttributeCombination = new OrderProductAttributeCombination();
                $orderAttributeCombination
                    ->setOrderProductId($orderProduct->getId())
                    ->setAttributeTitle($attribute->getTitle())
                    ->setAttributeChapo($attribute->getChapo())
                    ->setAttributeDescription($attribute->getDescription())
                    ->setAttributePostscriptum($attribute->getPostscriptum())
                    ->setAttributeAvTitle($attributeAv->getTitle())
                    ->setAttributeAvChapo($attributeAv->getChapo())
                    ->setAttributeAvDescription($attributeAv->getDescription())
                    ->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
                    ->save($con);
            }
        }

        $con->commit();

        return $placedOrder;
    }

    /**
     * Create an order outside of the front-office context, e.g. manually from the back-office.
     * @param OrderManualEvent $event
     * @param $eventName
     * @param EventDispatcherInterface $dispatcher
     * @throws \Exception
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function createManual(OrderManualEvent $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        $event->setPlacedOrder(
            $this->createOrder(
                $dispatcher,
                $event->getOrder(),
                $event->getCurrency(),
                $event->getLang(),
                $event->getCart(),
                $event->getCustomer(),
                null,
                $event->getUseOrderDefinedAddresses()
            )
        );

        $event->setOrder(new OrderModel());
    }

    /**
     * @param OrderEvent $event
     *
     * @throws \Thelia\Exception\TheliaProcessException
     * @param $eventName
     * @param EventDispatcherInterface $dispatcher
     * @throws \Exception
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function create(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        $session = $this->getSession();

        $order = $event->getOrder();
        $paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId());

        $placedOrder = $this->createOrder(
            $dispatcher,
            $event->getOrder(),
            $session->getCurrency(),
            $session->getLang(),
            $session->getSessionCart($dispatcher),
            $this->securityContext->getCustomerUser()
        );

        $dispatcher->dispatch(TheliaEvents::ORDER_BEFORE_PAYMENT, new OrderEvent($placedOrder));

        /* but memorize placed order */
        $event->setOrder(new OrderModel());
        $event->setPlacedOrder($placedOrder);

        /* call pay method */
        $payEvent = new OrderPaymentEvent($placedOrder);

        $dispatcher->dispatch(TheliaEvents::MODULE_PAY, $payEvent);

        if ($payEvent->hasResponse()) {
            $event->setResponse($payEvent->getResponse());
        }
    }

    /**
     * @param OrderEvent $event
     * @param $eventName
     * @param EventDispatcherInterface $dispatcher
     */
    public function orderBeforePayment(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        $dispatcher->dispatch(TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL, clone $event);

        $dispatcher->dispatch(TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL, clone $event);
    }

    /**
     * Clear the cart and the order in the customer session once the order is placed,
     * and the payment performed.
     *
     * @param OrderEvent $event
     * @param $eventName
     * @param EventDispatcherInterface $dispatcher
     */
    public function orderCartClear(/** @noinspection PhpUnusedParameterInspection */ OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        // Empty cart and clear current order
        $session = $this->getSession();

        $session->clearSessionCart($dispatcher);

        $session->setOrder(new OrderModel());
    }

    /**
     * @param OrderEvent $event
     *
     * @throws \Exception if the message cannot be loaded.
     */
    public function sendConfirmationEmail(OrderEvent $event)
    {
        $this->mailer->sendEmailToCustomer(
            'order_confirmation',
            $event->getOrder()->getCustomer(),
            [
                'order_id' => $event->getOrder()->getId(),
                'order_ref' => $event->getOrder()->getRef()
            ]
        );
    }

    /**
     * @param OrderEvent $event
     *
     * @throws \Exception if the message cannot be loaded.
     */
    public function sendNotificationEmail(OrderEvent $event)
    {
        $this->mailer->sendEmailToShopManagers(
            'order_notification',
            [
                'order_id' => $event->getOrder()->getId(),
                'order_ref' => $event->getOrder()->getRef()
            ]
        );
    }

    /**
     * @param OrderEvent $event
     * @param $eventName
     * @param EventDispatcherInterface $dispatcher
     * @throws \Exception
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function updateStatus(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        $order = $event->getOrder();
        $newStatus = $event->getStatus();

        $con = Propel::getConnection(OrderTableMap::DATABASE_NAME);

        // Prevent partial stock update on status change.
        $con->beginTransaction();

        try {
            $this->updateQuantity($order, $newStatus, $dispatcher);

            $order->setStatusId($newStatus)->save();

            $event->setOrder($order);

            $con->commit();
        } catch (\Exception $ex) {
            $con->rollBack();

            throw $ex;
        }
    }

    /**
     * Check if a stock update is required on order products for a given order status change, and compute if
     * the stock should be decreased or increased.
     *
     * @param GetStockUpdateOperationOnOrderStatusChangeEvent $event
     * @param $eventName
     * @param EventDispatcherInterface $dispatcher
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function getStockUpdateOnOrderStatusChange(GetStockUpdateOperationOnOrderStatusChangeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        // The order
        $order = $event->getOrder();

        // The new order status
        $newStatus = $event->getNewOrderStatus();

        if ($newStatus->getId() !== $order->getStatusId()) {
            // We have to change the stock in the following cases :
            // 1) The order is currently paid, and will become unpaid (get products back in stock unconditionnaly)
            // 2) The order is currently unpaid, and will become paid (remove products from stock, except if was done at order creation $manageStockOnCreation == false)
            // 3) The order is currently NOT PAID, and will become canceled or the like (get products back in stock if it was done at order creation $manageStockOnCreation == true)

            // We consider the ManageStockOnCreation flag only if the order status as not yet changed.
            // Count distinct order statuses (e.g. NOT_PAID to something else) in the order version table.
            if (OrderVersionQuery::create()->groupByStatusId()->filterById($order->getId())->count() > 1) {
                // A status change occured. Ignore $manageStockOnCreation
                $manageStockOnCreation = false;
            } else {
                // A status has not yet occured. Consider the ManageStockOnCreation flag
                $manageStockOnCreation = $order->isStockManagedOnOrderCreation($dispatcher);
            }

            if (($order->isPaid(false) && $newStatus->isNotPaid(false)) // Case 1
                ||
                ($order->isNotPaid(true) && $newStatus->isNotPaid(false) && $manageStockOnCreation === true) // Case 3
            ) {
                $event->setOperation($event::INCREASE_STOCK);
            }

            if ($order->isNotPaid(false) // Case 2
                &&
                $newStatus->isPaid(false)
                &&
                $manageStockOnCreation === false) {
                $event->setOperation($event::DECREASE_STOCK);
            }

            Tlog::getInstance()->addInfo(
                "Checking stock operation for status change of order : " . $order->getRef()
                . ", version: " . $order->getVersion()
                . ", manageStockOnCreation: " . ($manageStockOnCreation ? 0 : 1)
                . ", paid:" . ($order->isPaid(false) ? 1 : 0)
                . ", is not paid:" . ($order->isNotPaid(false) ? 1 : 0)
                . ", new status paid:" . ($newStatus->isPaid(false) ? 1 : 0)
                . ", new status is not paid:" . ($newStatus->isNotPaid(false) ? 1 : 0)
                . " = operation: " . $event->getOperation()
            );
        }
    }

    /**
     * Update order products stock after an order status change
     *
     * @param OrderModel $order
     * @param int $newStatus the new status ID
     * @throws \Exception
     * @throws \Propel\Runtime\Exception\PropelException
     */
    protected function updateQuantity(ModelOrder $order, $newStatus, EventDispatcherInterface $dispatcher)
    {
        if ($newStatus !== $order->getStatusId()) {
            if (null !== $newStatusModel = OrderStatusQuery::create()->findPk($newStatus)) {
                $operationEvent = new GetStockUpdateOperationOnOrderStatusChangeEvent($order, $newStatusModel);

                $dispatcher->dispatch(
                    TheliaEvents::ORDER_GET_STOCK_UPDATE_OPERATION_ON_ORDER_STATUS_CHANGE,
                    $operationEvent
                );

                if ($operationEvent->getOperation() !== $operationEvent::DO_NOTHING) {
                    $orderProductList = $order->getOrderProducts();

                    /** @var OrderProduct $orderProduct */
                    foreach ($orderProductList as $orderProduct) {
                        $productSaleElementsId = $orderProduct->getProductSaleElementsId();

                        /** @var ProductSaleElements $productSaleElements */
                        if (null !== $productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId)) {
                            $offset = 0;

                            if ($operationEvent->getOperation() == $operationEvent::INCREASE_STOCK) {
                                $offset = $orderProduct->getQuantity();
                            } elseif ($operationEvent->getOperation() == $operationEvent::DECREASE_STOCK) {
                                /* Check if we have enough stock */
                                if ($orderProduct->getQuantity() > $productSaleElements->getQuantity() && true === ConfigQuery::checkAvailableStock()) {
                                    throw new TheliaProcessException($productSaleElements->getRef() . " : Not enough stock 2");
                                }

                                $offset = -$orderProduct->getQuantity();
                            }

                            Tlog::getInstance()->addError("Product stock: " . $productSaleElements->getQuantity() . " -> " . ($productSaleElements->getQuantity() + $offset));

                            $productSaleElements
                                ->setQuantity($productSaleElements->getQuantity() + $offset)
                                ->save();
                        }
                    }
                }
            }
        }
    }

    /**
     * @param OrderEvent $event
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function updateDeliveryRef(OrderEvent $event)
    {
        $order = $event->getOrder();

        $order->setDeliveryRef($event->getDeliveryRef())->save();

        $event->setOrder($order);
    }

    /**
     * @param OrderEvent $event
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function updateTransactionRef(OrderEvent $event)
    {
        $order = $event->getOrder();

        $order->setTransactionRef($event->getTransactionRef())->save();

        $event->setOrder($order);
    }

    /**
     * @param OrderAddressEvent $event
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function updateAddress(OrderAddressEvent $event)
    {
        $orderAddress = $event->getOrderAddress();

        $orderAddress
            ->setCustomerTitleId($event->getTitle())
            ->setCompany($event->getCompany())
            ->setFirstname($event->getFirstname())
            ->setLastname($event->getLastname())
            ->setAddress1($event->getAddress1())
            ->setAddress2($event->getAddress2())
            ->setAddress3($event->getAddress3())
            ->setZipcode($event->getZipcode())
            ->setCity($event->getCity())
            ->setCountryId($event->getCountry())
            ->setStateId($event->getState())
            ->setPhone($event->getPhone())
            ->setCellphone($event->getCellphone())
            ->save()
        ;

        $event->setOrderAddress($orderAddress);
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return array(
            TheliaEvents::ORDER_SET_DELIVERY_ADDRESS => [ "setDeliveryAddress", 128 ],
            TheliaEvents::ORDER_SET_DELIVERY_MODULE => [ "setDeliveryModule", 128 ],
            TheliaEvents::ORDER_SET_POSTAGE => [ "setPostage", 128 ],
            TheliaEvents::ORDER_SET_INVOICE_ADDRESS => [ "setInvoiceAddress", 128 ],
            TheliaEvents::ORDER_SET_PAYMENT_MODULE => [ "setPaymentModule", 128 ],
            TheliaEvents::ORDER_PAY => [ "create", 128 ],
            TheliaEvents::ORDER_CART_CLEAR => [ "orderCartClear", 128 ],
            TheliaEvents::ORDER_BEFORE_PAYMENT => [ "orderBeforePayment", 128 ],
            TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL => [ "sendConfirmationEmail", 128 ],
            TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL => [ "sendNotificationEmail", 128 ],
            TheliaEvents::ORDER_UPDATE_STATUS => [ "updateStatus", 128 ],
            TheliaEvents::ORDER_UPDATE_DELIVERY_REF => [ "updateDeliveryRef", 128 ],
            TheliaEvents::ORDER_UPDATE_TRANSACTION_REF => [ "updateTransactionRef", 128 ],
            TheliaEvents::ORDER_UPDATE_ADDRESS => [ "updateAddress", 128 ],
            TheliaEvents::ORDER_CREATE_MANUAL => [ "createManual", 128 ],
            TheliaEvents::ORDER_GET_STOCK_UPDATE_OPERATION_ON_ORDER_STATUS_CHANGE => [ "getStockUpdateOnOrderStatusChange", 128 ],
        );
    }

    /**
     * Returns the session from the current request
     *
     * @return \Thelia\Core\HttpFoundation\Session\Session
     */
    protected function getSession()
    {
        /** @var Request $request */
        $request = $this->requestStack->getCurrentRequest();

        return $request->getSession();
    }
}
