<?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 common\models;

use common\helpers\GlobalParam;
use Yii;
use common\components\ActiveRecordCommon;
use common\models\Producer;
use common\models\PointSale;
use common\models\UserPointSale;
use common\models\Order;
use common\models\ProductOrder;
use common\models\User;

/**
 * This is the model class for table "commande_auto".
 *
 * @property integer $id
 * @property integer $id_user
 * @property integer $id_producer
 * @property integer $id_point_sale
 * @property string $date_begin
 * @property string $date_end
 * @property integer $monday
 * @property integer $tuesday
 * @property integer $wednesday
 * @property integer $thursday
 * @property integer $friday
 * @property integer $saturday
 * @property integer $sunday
 * @property integer $week_frequency
 * @property string $username
 * @property string $auto_payment
 * @property string $comment
 */
class Subscription extends ActiveRecordCommon
{
        /**
         * @inheritdoc
         */
        public static function tableName()
        {
                return 'subscription';
        }

        /**
         * @inheritdoc
         */
        public function rules()
        {
                return [
                        [['id_producer', 'id_point_sale'], 'required'],
                        [['id_user', 'id_producer', 'id_point_sale', 'monday', 'tuesday',
                                'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'week_frequency'], 'integer'],
                        [['auto_payment'], 'boolean'],
                        [['date_begin', 'date_end', 'username', 'comment'], 'safe'],
                ];
        }

        /**
         * @inheritdoc
         */
        public function attributeLabels()
        {
                return [
                        'id' => 'ID',
                        'id_user' => 'Utilisateur',
                        'id_producer' => 'Etablissement',
                        'id_point_sale' => 'Point de vente',
                        'date_begin' => 'Date de début',
                        'date_end' => 'Date de fin',
                        'monday' => 'Lundi',
                        'tuesday' => 'Mardi',
                        'wednesday' => 'Mercredi',
                        'thursday' => 'Jeudi',
                        'friday' => 'Vendredi',
                        'saturday' => 'Samedi',
                        'sunday' => 'Dimanche',
                        'week_frequency' => 'Périodicité',
                        'auto_payment' => 'Paiement automatique',
                        'comment' => 'Commentaire'
                ];
        }

        /*
         * Relations
         */

        public function getUser()
        {
                return $this->hasOne(User::className(), ['id' => 'id_user']);
        }

        public function getProducer()
        {
                return $this->hasOne(
                        Producer::className(),
                        ['id' => 'id_producer']
                );
        }

        public function getPointSale()
        {
                return $this->hasOne(
                        PointSale::className(),
                        ['id' => 'id_point_sale']
                );
        }

        public function getProductSubscription()
        {
                return $this->hasMany(
                        ProductSubscription::className(),
                        ['id_subscription' => 'id']
                )->with('product');
        }

        /**
         * Retourne les options de base nécessaires à la fonction de recherche.
         *
         * @return array
         */
        public static function defaultOptionsSearch()
        {
                return [
                        'with' => ['producer'],
                        'join_with' => ['user', 'productSubscription', 'productSubscription.product', 'pointSale'],
                        'orderby' => 'user.name ASC',
                        'attribute_id_producer' => 'subscription.id_producer'
                ];
        }

        /**
         * Ajoute la commande pour une date donnée.
         *
         * @param string $date
         */
        public function add($date)
        {
                // distribution
                $distribution = Distribution::searchOne([
                        'distribution.date' => date('Y-m-d', strtotime($date))
                ]);

                if ($distribution && $distribution->active && count($this->productSubscription) && $this->id_point_sale) {
                        // commande
                        $order = new Order;
                        if (strlen($this->username)) {
                                $order->username = $this->username;
                                $order->id_user = 0;
                        } else {
                                $order->id_user = $this->id_user;
                        }
                        $order->date = date('Y-m-d H:i:s');
                        $order->origin = Order::ORIGIN_AUTO;
                        $order->id_point_sale = $this->id_point_sale;
                        $order->id_distribution = $distribution->id;
                        $order->id_subscription = $this->id;
                        $order->status = 'tmp-order' ;
                        if (strlen($this->comment)) {
                                $order->comment = $this->comment;
                        }

                        $pointSale = PointSale::findOne($this->id_point_sale);

                        if($pointSale) {
                                $creditFunctioning = $pointSale->getCreditFunctioning();

                                $order->auto_payment = 0;
                                if ($order->id_user && Producer::getConfig('credit') && $pointSale->credit) {
                                        if ($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL) {
                                                $order->auto_payment = $this->auto_payment;
                                        } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY) {
                                                $order->auto_payment = 1;
                                        } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER) {
                                                $user = User::findOne($order->id_user);
                                                $userProducer = UserProducer::searchOne([
                                                        'id_user' => $order->id_user,
                                                        'id_producer' => $distribution->id_producer
                                                ]);
                                                if ($userProducer) {
                                                        $order->auto_payment = $userProducer->credit_active;
                                                }
                                        }
                                }

                                $order->tiller_synchronization = $order->auto_payment ;

                                $userPointSale = UserPointSale::searchOne([
                                        'id_point_sale' => $this->id_point_sale,
                                        'id_user' => $this->id_user
                                ]);

                                if ($userPointSale && strlen($userPointSale->comment)) {
                                        $order->comment_point_sale = $userPointSale->comment;
                                }

                                $order->save();

                                // liaison utilisateur / point de vente
                                if ($order->id_user) {
                                        $pointSale = PointSale::findOne($this->id_point_sale);
                                        $pointSale->linkUser($order->id_user);
                                }

                                // produits
                                $amountTotal = 0;
                                $productsAdd = false;
                                foreach ($this->productSubscription as $productSubscription) {
                                        $productOrder = new ProductOrder;
                                        $productOrder->id_order = $order->id;
                                        $productOrder->id_product = $productSubscription->product->id;
                                        $productOrder->quantity = $productSubscription->quantity;
                                        $productOrder->price = $productSubscription->product->getPrice([
                                                'id_user' => $this->id_user,
                                                'id_point_sale' => $this->id_point_sale
                                        ]);
                                        $productOrder->unit = $productSubscription->product->unit;
                                        $productOrder->step = $productSubscription->product->step;
                                        $productOrder->id_tax_rate = $productSubscription->product->taxRate->id;
                                        $productOrder->save();
                                        $productsAdd = true;
                                }

                                if (!$productsAdd) {
                                        $order->delete();
                                }

                                $order->initReference() ;
                        }
                }
        }

        /**
         * Ajoute les commandes pour une date donnée à partir des abonnements.
         *
         * @param string $date
         * @param boolean $force
         */
        public static function addAll($date, $force = false)
        {
                $distribution = Distribution::searchOne([
                        'date' => date('Y-m-d', strtotime($date)),
                        'id_producer' => GlobalParam::getCurrentProducerId(),
                ]);

                if ($distribution) {
                        $arrayOrdersDistribution = Order::searchAll([
                                Order::tableName() . '.id_distribution' => $distribution->id
                        ]);
                        $arraySubscriptions = self::searchByDate($date);
                        foreach ($arraySubscriptions as $subscription) {
                                if (!$subscription->hasOrderAlreadyExist($arrayOrdersDistribution)) {
                                        $subscription->add($date);
                                }
                        }

                }
        }

        /**
         * Informe s'il existe une commande correspond à l'abonnement courant.
         *
         * @param array $arrayOrders
         * @return boolean
         */
        public function hasOrderAlreadyExist($arrayOrders)
        {
                if (is_array($arrayOrders) && count($arrayOrders) > 0) {
                        foreach ($arrayOrders as $order) {
                                if ((($order->id_user > 0 && $order->id_user == $this->id_user) ||
                                                (!$order->id_user && $order->username == $this->username)) &&
                                        $order->id_point_sale == $this->id_point_sale) {
                                        return true;
                                }
                        }
                }

                return false;
        }

        /**
         * Retourne les abonnements pour une date donnée.
         *
         * @param string $date
         * @return array
         */
        public static function searchByDate($date)
        {
                $date = date('Y-m-d', strtotime($date));

                $subscriptions = Subscription::searchAll();

                $arrSubscriptions = [];

                foreach ($subscriptions as $s) {
                        if ($date >= $s->date_begin &&
                                (!$s->date_end || $date <= $s->date_end) &&
                                $s->matchWith($date)) {
                                $arrSubscriptions[] = $s;
                        }
                }

                return $arrSubscriptions;
        }

        /**
         * Valide le fait qu'un abonnement est bien compatible avec une date donnée.
         *
         * @param string $date
         * @return boolean
         */
        public function matchWith($date)
        {
                $arrayDays = [
                        1 => 'monday',
                        2 => 'tuesday',
                        3 => 'wednesday',
                        4 => 'thursday',
                        5 => 'friday',
                        6 => 'saturday',
                        7 => 'sunday'
                ];

                $nbDays = (strtotime($date) - strtotime($this->date_begin)) / (24 * 60 * 60);
                if (round($nbDays) % ($this->week_frequency * 7) < 7) {
                        $numDay = date('N', strtotime($date));
                        $day = $arrayDays[$numDay];
                        if ($this->$day) {
                                return true;
                        }
                }

                return false;
        }

        /**
         * Recherche les distributions futures où l'abonnement peut s'appliquer.
         *
         * @return array
         */
        public function searchMatchedIncomingDistributions()
        {
                $producer = GlobalParam::getCurrentProducer();
                $params = [
                        ':date_earliest_order' => date('Y-m-d'),
                        ':date_begin' => date('Y-m-d', strtotime($this->date_begin)),
                        ':id_producer' => GlobalParam::getCurrentProducerId()
                ];

                $incomingDistributions = Distribution::find()
                        ->where('id_producer = :id_producer')
                        ->andWhere('date >= :date_begin')
                        ->andWhere('date >= :date_earliest_order');

                if ($this->date_end) {
                        $incomingDistributions->andWhere('date < :date_end');
                        $params[':date_end'] = date('Y-m-d', strtotime($this->date_end));
                }

                $incomingDistributions->orderBy('date ASC');

                $incomingDistributions->params($params);
                $incomingDistributionsArray = $incomingDistributions->all();

                $incomingDistributions = Distribution::filterDistributionsByDateDelay($incomingDistributionsArray) ;

                $matchedIncomingDistributionsArray = [];
                foreach ($incomingDistributionsArray as $incomingDistribution) {
                        if ($this->matchWith($incomingDistribution->date)) {
                                $matchedIncomingDistributionsArray[] = $incomingDistribution;
                        }
                }

                return $matchedIncomingDistributionsArray;
        }

        public function deleteOrdersIncomingDistributions()
        {
                $params = [
                        ':id_producer' => GlobalParam::getCurrentProducerId(),
                        ':date_today' => date('Y-m-d'),
                        ':date_begin' => $this->date_begin,
                        ':id_subscription' => $this->id
                ];


                $orderDeadline = Producer::getConfig('order_deadline');
                $hour = date('G');

                if ($hour >= $orderDeadline) {
                        $conditionDistributionDate = 'distribution.date > :date_today';
                } else {
                        $conditionDistributionDate = 'distribution.date >= :date_today';
                }

                $orders = Order::find()
                        ->joinWith('distribution')
                        ->where('distribution.id_producer = :id_producer')
                        ->andWhere($conditionDistributionDate)
                        ->andWhere('distribution.date >= :date_begin')
                        ->andWhere('order.id_subscription = :id_subscription');

                if ($this->date_end) {
                        $orders->andWhere('distribution.date <= :date_end');
                        $params[':date_end'] = $this->date_end;
                }

                $orders->params($params);

                $ordersArray = $orders->all();
                $configCredit = Producer::getConfig('credit');

                if ($ordersArray && count($ordersArray)) {
                        foreach ($ordersArray as $order) {

                                $theOrder = Order::searchOne(['id' => $order->id]);

                                // remboursement de la commande
                                if ($theOrder->id_user && $theOrder->getAmount(Order::AMOUNT_PAID) && $configCredit) {
                                        $theOrder->saveCreditHistory(
                                                CreditHistory::TYPE_REFUND,
                                                $theOrder->getAmount(Order::AMOUNT_PAID),
                                                $theOrder->distribution->id_producer,
                                                $theOrder->id_user,
                                                User::getCurrentId()
                                        );
                                }

                                ProductOrder::deleteAll(['id_order' => $order->id]);
                                $order->delete();
                        }
                }
        }

        public function updateIncomingDistributions($update = false)
        {
                $matchedDistributionsArray = $this->searchMatchedIncomingDistributions();

                if ($update) {
                        $this->deleteOrdersIncomingDistributions();
                }

                if (count($matchedDistributionsArray)) {
                        foreach ($matchedDistributionsArray as $distribution) {
                                $this->add($distribution->date);
                        }
                }
        }

        public function getUsername()
        {
                if($this->user) {
                        return $this->user->getUsername() ;
                }

                return $this->username ;
        }


}
