<?php

/**
 * Copyright distrib (2018)
 *
 * contact@opendistrib.net
 *
 * Ce logiciel est un programme informatique servant à aider les producteurs
 * à distribuer leur production en circuits courts.
 *
 * Ce logiciel est régi par la licence CeCILL soumise au droit français et
 * respectant les principes de diffusion des logiciels libres. Vous pouvez
 * utiliser, modifier et/ou redistribuer ce programme sous les conditions
 * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
 * sur le site "http://www.cecill.info".
 *
 * En contrepartie de l'accessibilité au code source et des droits de copie,
 * de modification et de redistribution accordés par cette licence, il n'est
 * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
 * seule une responsabilité restreinte pèse sur l'auteur du programme,  le
 * titulaire des droits patrimoniaux et les concédants successifs.
 *
 * A cet égard  l'attention de l'utilisateur est attirée sur les risques
 * associés au chargement,  à l'utilisation,  à la modification et/ou au
 * développement et à la reproduction du logiciel par l'utilisateur étant
 * donné sa spécificité de logiciel libre, qui peut le rendre complexe à
 * manipuler et qui le réserve donc à des développeurs et des professionnels
 * avertis possédant  des  connaissances  informatiques approfondies.  Les
 * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
 * logiciel à leurs besoins dans des conditions permettant d'assurer la
 * sécurité de leurs systèmes et ou de leurs données et, plus généralement,
 * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
 *
 * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
 * pris connaissance de la licence CeCILL, et que vous en avez accepté les
 * termes.
 */

namespace producer\controllers;

use common\helpers\Debug;
use common\helpers\GlobalParam;
use common\models\ProductDistribution;
use common\models\User;
use common\models\Producer;
use common\models\Order;
use common\models\UserPointSale;
use common\models\Product;
use DateTime;

class OrderController extends ProducerBaseController
{
        var $enableCsrfValidation = false;

        public function behaviors()
        {
                return [
                        'access' => [
                                'class' => AccessControl::className(),
                                'rules' => [
                                        [
                                                'allow' => true,
                                                'roles' => ['@'],
                                        ]
                                ],
                        ],
                ];
        }

        public function actionOrder($id = 0, $date = '')
        {
                $params = [];

                if ($id) {
                        $order = Order::searchOne([
                                'id' => $id
                        ]);
                        if ($order) {
                                if ($order->getState() == Order::STATE_OPEN) {
                                        $params['order'] = $order;
                                }
                        }
                }

                if (strlen($date)) {
                        $distribution = Distribution::searchOne([
                                'date' => $date,
                                'id_producer' => GlobalParam::getCurrentProducerId()
                        ]);
                        if ($distribution) {
                                $params['date'] = $date;
                        }
                }

                return $this->render('order', $params);
        }

        /**
         * Affiche l'historique des commandes de l'utilisateur
         *
         * @return ProducerView
         */
        public function actionHistory($type = 'incoming')
        {
                $query = Order::find()
                        ->with('productOrder', 'pointSale', 'creditHistory')
                        ->joinWith('distribution', 'distribution.producer')
                        ->where([
                                'id_user' => Yii::$app->user->id,
                                'distribution.id_producer' => GlobalParam::getCurrentProducerId()
                        ])
                        ->params([':date_today' => date('Y-m-d')]);

                $queryIncoming = clone $query;
                $queryIncoming->andWhere('distribution.date >= :date_today')->orderBy('distribution.date ASC');

                $queryPassed = clone $query;
                $queryPassed->andWhere('distribution.date < :date_today')->orderBy('distribution.date DESC');

                $dataProviderOrders = new ActiveDataProvider([
                        'query' => ($type == 'incoming') ? $queryIncoming : $queryPassed,
                        'pagination' => [
                                'pageSize' => 10,
                        ],
                ]);

                return $this->render('history', [
                        'dataProviderOrders' => $dataProviderOrders,
                        'orderOk' => Yii::$app->getRequest()->get('orderOk', false),
                        'cancelOk' => Yii::$app->getRequest()->get('cancelOk', false),
                        'type' => $type,
                        'countIncoming' => $queryIncoming->count(),
                        'countPassed' => $queryPassed->count(),
                ]);
        }

        /**
         * Supprime un producteur.
         *
         * @param integer $id
         */
        public function actionRemoveProducer($id = 0)
        {
                $userProducer = UserProducer::find()
                        ->where(['id_producer' => $id, 'id_user' => User::getCurrentId()])
                        ->one();

                $userProducer->active = 0;
                $userProducer->save();

                $this->redirect(['order/index']);
        }

        /**
         * Crée une commande.
         *
         * @return mixed
         */
        public function actionAjaxProcess()
        {
                \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
                $order = new Order;
                $idProducer = $this->getProducer()->id;

                $posts = Yii::$app->request->post();

                if ($idProducer) {
                        $this->_verifyProducerActive($idProducer);
                }

                if ($order->load($posts)) {
                        $order = Order::find()
                                ->where('id_distribution = :id_distribution')
                                ->andWhere('id_user = :id_user')
                                ->params([
                                        ':id_distribution' => $posts['Order']['id_distribution'],
                                        ':id_user' => User::getCurrentId()
                                ])
                                ->one();

                        if (!$order) {
                                $order = new Order;
                                $order->load(Yii::$app->request->post());
                                $order->id_user = User::getCurrentId();
                                $order->status = 'tmp-order';
                                $order->date = date('Y-m-d H:i:s');
                                $order->origin = Order::ORIGIN_USER;
                        }

                        $errors = $this->processForm($order);

                        if (count($errors)) {
                                return ['status' => 'error', 'errors' => $errors];
                        }
                }

                return ['status' => 'success', 'idOrder' => $order->id];
        }

        /**
         * Vérifie si un producteur est actif.
         *
         * @param integer $idProducer
         * @throws NotFoundHttpException
         */
        public function _verifyProducerActive($idProducer)
        {
                $producer = Producer::findOne($idProducer);
                if ($producer && !$producer->active) {
                        throw new NotFoundHttpException('Ce producteur est actuellement hors ligne.');
                }
        }

        /**
         * Traite le formulaire de création/modification de commande.
         *
         * @param Commande $order
         */
        public function processForm($order)
        {
                $posts = Yii::$app->request->post();
                $productsArray = [];
                $totalQuantity = 0;
                $producer = $this->getProducer();

                foreach ($posts['products'] as $key => $quantity) {
                        $product = Product::find()->where(['id' => (int)$key])->one();
                        $totalQuantity += $quantity;
                        if ($product && $quantity) {
                                $productsArray[] = $product;
                        }
                }

                // date
                $errorDate = false;
                if (isset($order->id_distribution)) {
                        // date de commande
                        $distribution = Distribution::find()->where(['id' => $order->id_distribution])->one();

                        if ($order->getState() != Order::STATE_OPEN) {
                                $errorDate = true;
                        }
                }

                // point de vente
                $errorPointSale = false;
                if (isset($distribution) && $distribution) {

                        $pointSaleDistribution = PointSaleDistribution::searchOne([
                                'id_distribution' => $distribution->id,
                                'id_point_sale' => $posts['Order']['id_point_sale']
                        ]);

                        if (!$pointSaleDistribution || !$pointSaleDistribution->delivery) {
                                $errorPointSale = true;
                        }

                        $pointSale = PointSale::findOne($posts['Order']['id_point_sale']);

                        if ($pointSale) {
                                if (strlen($pointSale->code) && !$pointSale->validateCode($posts['code_point_sale'])) {
                                        $errorPointSale = true;
                                }
                        } else {
                                $errorPointSale = true;
                        }

                        $userPointSale = UserPointSale::searchOne([
                                'id_user' => User::getCurrentId(),
                                'id_point_sale' => $pointSale->id
                        ]);

                        if ($pointSale->restricted_access && !$userPointSale) {
                                $errorPointSale = true;
                        }
                }

                $errors = [];

                if ($order->validate() && count($productsArray) && !$errorDate && !$errorPointSale) {

                        $userProducer = UserProducer::searchOne([
                                'id_producer' => $order->distribution->id_producer,
                                'id_user' => User::getCurrentId()
                        ]);

                        // gestion point de vente
                        $pointSale = PointSale::searchOne([
                                'id' => $order->id_point_sale
                        ]);

                        $order->comment_point_sale = ($pointSale && strlen($pointSale->getComment())) ?
                                $pointSale->getComment() : '';

                        // la commande est automatiquement réactivée lors d'une modification
                        $order->date_delete = null;

                        // sauvegarde de la commande
                        $order->save();

                        $order->changeOrderStatus('new-order', 'user');
                        // ajout de l'utilisateur à l'établissement
                        Producer::addUser(User::getCurrentId(), $distribution->id_producer);

                        // suppression de tous les enregistrements ProductOrder
                        if (!is_null($order)) {
                                ProductOrder::deleteAll(['id_order' => $order->id]);

                                $stepsArray = [];
                                if (isset($order->productOrder)) {
                                        foreach ($order->productOrder as $productOrder) {
                                                $unitsArray[$productOrder->id_product] = $productOrder->unit;
                                        }
                                }
                        }

                        // produits dispos
                        $availableProducts = ProductDistribution::searchByDistribution($distribution->id);

                        // sauvegarde des produits
                        foreach ($productsArray as $product) {
                                if (isset($availableProducts[$product->id])) {
                                        $productOrder = new ProductOrder();
                                        $productOrder->id_order = $order->id;
                                        $productOrder->id_product = $product->id;

                                        $productOrder->price = $product->price;
                                        $productOrder->id_tax_rate = $product->taxRate->id;

                                        $unit = (!is_null($order) && isset($unitsArray[$product->id])) ? $unitsArray[$product->id] : $product->unit;
                                        $coefficient = Product::$unitsArray[$unit]['coefficient'];
                                        $quantity = ((float)$posts['products'][$product->id]) / $coefficient;
                                        if ($availableProducts[$product->id]['quantity_max'] && $quantity > $availableProducts[$product->id]['quantity_remaining']) {
                                                $quantity = $availableProducts[$product->id]['quantity_remaining'];
                                        }

                                        $productOrder->quantity = $quantity;
                                        $productOrder->unit = $product->unit;
                                        $productOrder->step = $product->step;
                                        $productOrder->save();
                                }
                        }

                        // lien utilisateur / point de vente
                        $pointSale->linkUser(User::getCurrentId());

                        // credit
                        $credit = Producer::getConfig('credit');
                        $creditLimit = Producer::getConfig('credit_limit');
                        $creditFunctioning = $pointSale->getCreditFunctioning();
                        $creditUser = Yii::$app->user->identity->getCredit($distribution->id_producer);
                        $order = Order::searchOne([
                                'id' => $order->id
                        ]);
                        $amountPaid = $order->getAmount(Order::AMOUNT_PAID);
                        $amountRemaining = $order->getAmount(Order::AMOUNT_REMAINING);

                        if ($credit && $pointSale->credit &&
                                (($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL && $posts['use_credit']) ||
                                        $creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY ||
                                        ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER && $userProducer->credit_active)
                                )) {
                                $order->changeOrderStatus('waiting-paiement-by-credit', 'user');

                                // à payer
                                if ($order->getPaymentStatus() == Order::PAYMENT_UNPAID) {

                                        if (!is_null($creditLimit) && $amountRemaining > $creditUser - $creditLimit) {
                                                $amountRemaining = $creditUser - $creditLimit;
                                        }

                                        if ($amountRemaining > 0) {
                                                $order->saveCreditHistory(
                                                        CreditHistory::TYPE_PAYMENT,
                                                        $amountRemaining,
                                                        $distribution->id_producer,
                                                        User::getCurrentId(),
                                                        User::getCurrentId()
                                                );
                                                $order->changeOrderStatus('paid-by-credit', 'user');
                                        }else{
                                                $order->changeOrderStatus('waiting-paiement-on-delivery', 'user');
                                        }
                                } // surplus à rembourser
                                elseif ($order->getPaymentStatus() == Order::PAYMENT_SURPLUS) {
                                        $amountSurplus = $order->getAmount(Order::AMOUNT_SURPLUS);
                                        $order->saveCreditHistory(
                                                CreditHistory::TYPE_REFUND,
                                                $amountSurplus,
                                                $distribution->id_producer,
                                                User::getCurrentId(),
                                                User::getCurrentId()
                                        );
                                }
                        }
                        else{
                                $order->changeOrderStatus('waiting-paiement-on-delivery', 'user');
                        }

                        $order->setTillerSynchronization() ;
                }


                if (!count($productsArray)) {
                        $errors[] = "Vous n'avez choisi aucun produit";
                }
                if ($errorDate) {
                        $errors[] = "Vous ne pouvez pas commander pour cette date.";
                }
                if ($errorPointSale) {
                        $errors[] = "Point de vente invalide.";
                }

                return $errors;
        }

        /**
         * Annule une commande.
         *
         * @param integer $id
         * @throws \yii\web\NotFoundHttpException
         * @throws UserException
         */
        public function actionCancel($id)
        {
                $order = Order::searchOne([
                        'id' => $id
                ]);

                if (!$order) {
                        throw new \yii\web\NotFoundHttpException('Commande introuvable');
                }

                if ($order->getState() != Order::STATE_OPEN) {
                        throw new UserException('Vous ne pouvez plus annuler cette commande.');
                }

                if ($order && User::getCurrentId() == $order->id_user) {
                        $order->delete();
                        $order->changeOrderStatus('canceled', 'user');
                        Yii::$app->session->setFlash('success', 'Votre commande a bien été annulée.');
                }

                $this->redirect(Yii::$app->urlManager->createUrl(['order/history']));
        }

        /**
         * Vérifie le code saisi pour un point de vente.
         *
         * @param integer $idPointSale
         * @param string $code
         * @return boolean
         */
        public function actionAjaxValidateCodePointSale($idPointSale, $code)
        {
                \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

                $pointSale = PointSale::findOne($idPointSale);
                if ($pointSale) {
                        if ($pointSale->validateCode($code)) {
                                return 1;
                        }
                }
                return 0;
        }

        public function actionAjaxInfos($date = '')
        {
                \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

                $json = [];

                $format = 'Y-m-d';
                $dateObject = DateTime::createFromFormat($format, $date);

                // Producteur
                $producer = Producer::searchOne([
                        'id' => $this->getProducer()->id
                ]);
                $json['producer'] = [
                        'order_infos' => $producer->order_infos,
                        'credit' => $producer->credit,
                        'credit_functioning' => $producer->credit_functioning,
                        'use_credit_checked_default' => $producer->use_credit_checked_default,
                        'credit_limit' => is_numeric($producer->credit_limit) ? $producer->credit_limit : null
                ];

                // Distributions
                $dateMini = date('Y-m-d') ;

                $distributionsArray = Distribution::searchAll([
                        'active' => 1
                ], [
                        'conditions' => ['date > :date'],
                        'params' => [':date' => $dateMini],
                ]);
                $distributionsArray = Distribution::filterDistributionsByDateDelay($distributionsArray) ;
                $json['distributions'] = $distributionsArray;

                // Commandes de l'utilisateur
                $ordersUserArray = Order::searchAll([
                        'id_user' => User::getCurrentId()
                ], [
                        'conditions' => [
                                'distribution.date > :date'
                        ],
                        'params' => [
                                ':date' => $dateMini
                        ]
                ]);
                if (is_array($ordersUserArray) && count($ordersUserArray)) {
                        foreach ($ordersUserArray as &$order) {
                                $order = array_merge($order->getAttributes(), [
                                        'amount_total' => $order->getAmountWithTax(Order::AMOUNT_TOTAL),
                                        'date_distribution' => $order->distribution->date,
                                        'pointSale' => $order->pointSale->getAttributes()
                                ]);
                        }
                        $json['orders'] = $ordersUserArray;
                }

                // User
                $userProducer = UserProducer::searchOne([
                        'id_producer' => $producer->id,
                        'id_user' => User::getCurrentId()
                ]);

                $json['user'] = [
                        'credit' => $userProducer->credit,
                        'credit_active' => $userProducer->credit_active,
                ];

                if ($dateObject && $dateObject->format($format) === $date) {

                        // Commande de l'utilisateur
                        $orderUser = Order::searchOne([
                                'distribution.date' => $date,
                                'id_user' => User::getCurrentId(),
                        ]);

                        if ($orderUser) {
                                $json['order'] = array_merge($orderUser->getAttributes(), [
                                        'amount_total' => $orderUser->getAmountWithTax(Order::AMOUNT_TOTAL),
                                        'amount_paid' => $orderUser->getAmount(Order::AMOUNT_PAID),
                                ]);
                        }

                        // distribution
                        $distribution = Distribution::initDistribution($date);
                        $json['distribution'] = $distribution;

                        $pointsSaleArray = PointSale::find()
                                ->joinWith(['pointSaleDistribution' => function ($query) use ($distribution) {
                                        $query->where(['id_distribution' => $distribution->id]);
                                }
                                ])
                                ->with(['userPointSale' => function ($query) {
                                        $query->onCondition(['id_user' => User::getCurrentId()]);
                                }])
                                ->where(['id_producer' => $distribution->id_producer])
                                ->andWhere('restricted_access = 0 OR (restricted_access = 1 AND (SELECT COUNT(*) FROM user_point_sale WHERE point_sale.id = user_point_sale.id_point_sale AND user_point_sale.id_user = :id_user) > 0)')
                                ->params([':id_user' => User::getCurrentId()])
                                ->all();

                        $creditFunctioningProducer = Producer::getConfig('credit_functioning');

                        foreach ($pointsSaleArray as &$pointSale) {
                                $pointSale = array_merge($pointSale->getAttributes(), [
                                        'pointSaleDistribution' => [
                                                'id_distribution' => $pointSale->pointSaleDistribution[0]->id_distribution,
                                                'id_point_sale' => $pointSale->pointSaleDistribution[0]->id_point_sale,
                                                'delivery' => $pointSale->pointSaleDistribution[0]->delivery
                                        ],
                                        'userPointSale' => ($pointSale->userPointSale ? $pointSale->userPointSale[0] : '')
                                ]);
                                if ($pointSale['code'] && strlen($pointSale['code'])) {
                                        $pointSale['code'] = '***';
                                }
                                if (!strlen($pointSale['credit_functioning'])) {
                                        $pointSale['credit_functioning'] = $creditFunctioningProducer;
                                }
                        }

                        $favoritePointSale = User::getCurrent()->getFavoritePointSale();

                        if ($favoritePointSale) {
                                for ($i = 0; $i < count($pointsSaleArray); $i++) {
                                        if ($pointsSaleArray[$i]['id'] == $favoritePointSale->id) {
                                                $theFavoritePointSale = $pointsSaleArray[$i];
                                                unset($pointsSaleArray[$i]);
                                        }
                                }

                                if (isset($theFavoritePointSale)) {
                                        $pointsSaleArray = array_reverse($pointsSaleArray, false);
                                        $pointsSaleArray[] = $theFavoritePointSale;
                                        $pointsSaleArray = array_reverse($pointsSaleArray, false);
                                }
                        }

                        $json['points_sale'] = $pointsSaleArray;

                        // Commandes totales
                        $ordersArray = Order::searchAll([
                                'distribution.date' => $date,
                        ]);

                        // Produits
                        if (Producer::getConfig('option_allow_user_gift')) {
                                $productsArray = Product::find()
                                        ->orWhere(['id_producer' => $this->getProducer()->id,])
                                        //->orWhere(['id_producer' => 0,]) // produit "Don";
                                ;
                        } else {
                                $productsArray = Product::find()
                                        ->where(['id_producer' => $this->getProducer()->id,]);
                        }

                        $productsArray = $productsArray->joinWith(['productDistribution' => function ($query) use ($distribution) {
                                $query->andOnCondition('product_distribution.id_distribution = ' . $distribution->id);
                        }])
                                ->orderBy('product_distribution.active DESC, order ASC')
                                ->all();

                        $indexProduct = 0;
                        foreach ($productsArray as &$product) {

                                $product = array_merge(
                                        $product->getAttributes(),
                                        [
                                                'price_with_tax' => $product->getPriceWithTax(),
                                                'productDistribution' => $product['productDistribution']
                                        ]
                                );


                                $coefficient_unit = Product::$unitsArray[$product['unit']]['coefficient'];

                                if (is_null($product['photo'])) {
                                        $product['photo'] = '';
                                }

                                $product['quantity_max'] = $product['productDistribution'][0]['quantity_max'];
                                $quantityOrder = Order::getProductQuantity($product['id'], $ordersArray);
                                $product['quantity_ordered'] = $quantityOrder;
                                $product['quantity_remaining'] = $product['quantity_max'] - $quantityOrder;

                                if ($orderUser) {
                                        $quantityOrderUser = Order::getProductQuantity($product['id'], [$orderUser], true);
                                        $product['quantity_ordered'] = $quantityOrder;
                                        $product['quantity_remaining'] = $product['quantity_max'] - $quantityOrder + $quantityOrderUser;
                                        $product['quantity_form'] = $quantityOrderUser * $coefficient_unit;
                                        foreach ($orderUser->productOrder as $productOrder) {
                                                if ($productOrder->id_product == $product['id']) {
                                                        $product['wording_unit'] = Product::strUnit($productOrder->unit, 'wording_unit', true);
                                                        $product['step'] = $productOrder->step;
                                                }
                                        }
                                } else {
                                        $product['quantity_form'] = 0;
                                        $product['wording_unit'] = Product::strUnit($product['unit'], 'wording_unit', true);
                                }
                                $product['coefficient_unit'] = $coefficient_unit;

                                if ($product['quantity_remaining'] < 0) $product['quantity_remaining'] = 0;
                                $product['index'] = $indexProduct++;
                        }

                        $json['products'] = $productsArray;
                }

                return $json;
        }

        public function actionConfirm($idOrder)
        {
                $order = Order::searchOne(['id' => $idOrder]);

                if (!$order || $order->id_user != User::getCurrentId()) {
                        throw new \yii\base\UserException('Commande introuvable.');
                }

                return $this->render('confirm', [
                        'order' => $order
                ]);
        }

}
        