<?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 backend\controllers;

use backend\models\CreditForm;
use common\helpers\GlobalParam;
use common\helpers\Mailjet;
use backend\models\MailForm;
use common\helpers\Password;
use common\logic\Order\Order\Model\OrderSearch;
use common\logic\PointSale\PointSale\Model\PointSale;
use common\logic\PointSale\UserPointSale\Model\UserPointSale;
use common\logic\User\CreditHistory\Model\CreditHistory;
use common\logic\User\User\Model\User;
use common\logic\User\User\Model\UserSearch;
use common\logic\User\UserProducer\Model\UserProducer;
use common\logic\User\UserUserGroup\Model\UserUserGroup;
use yii\base\UserException;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use \Yii;
use yii\web\NotFoundHttpException;

/**
 * UserController implements the CRUD actions for User model.
 */
class UserController extends BackendController
{
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::class,
                'actions' => [
                ],
            ],
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                        'matchCallback' => function ($rule, $action) {
                            return $this->getUserManager()->hasAccessBackend();
                        }
                    ]
                ],
            ],
        ];
    }

    /**
     * Liste les utilisateurs.
     */
    public function actionIndex(
        int $idPointSale = 0,
        bool $sectionSubscribers = false,
        bool $sectionInactiveUsers = false)
    {
        $pointSaleManager = $this->getPointSaleManager();

        $searchModel = new UserSearch();
        $dataProvider = $searchModel->search([
            'UserSearch' => array_merge(
                [
                    'id_point_sale' => $idPointSale,
                    'inactive' => (int) $sectionInactiveUsers,
                    'subscribers' => (int) $sectionSubscribers
                ],
                isset(\Yii::$app->request->queryParams['UserSearch']) ?
                    Yii::$app->request->queryParams['UserSearch'] :
                    []
            )
        ]);

        $producer = $this->getProducerCurrent();
        $pointsSaleArray = $pointSaleManager->findPointSales();

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
            'producer' => $producer,
            'idPointSaleActive' => $idPointSale,
            'pointsSaleArray' => $pointsSaleArray,
            'sectionInactiveUsers' => $sectionInactiveUsers,
            'sectionSubscribers' => $sectionSubscribers,
        ]);
    }

    public function initForm($model)
    {
        $userPointSaleManager = $this->getUserPointSaleManager();
        $userUserGroupManager = $this->getUserUserGroupManager();
        $userProducerManager = $this->getUserProducerManager();
        $userGroupManager = $this->getUserGroupManager();

        $producerCurrent = $this->getProducerCurrent();

        if ($model->id) {
            $userPointSaleArray = $userPointSaleManager->findUserPointSalesByUser($model);
            if ($userPointSaleArray && count($userPointSaleArray) > 0) {
                foreach ($userPointSaleArray as $userPointSale) {
                    $model->points_sale[] = $userPointSale->id_point_sale;
                }
            }

            $userUserGroupsArray = $userUserGroupManager->findUserUserGroupsByUser($model);
            if ($userUserGroupsArray && count($userUserGroupsArray) > 0) {
                foreach ($userUserGroupsArray as $userUserGroup) {
                    $model->user_groups[] = $userUserGroup->id_user_group;
                }
            }
            $userProducer = $userProducerManager->findOneUserProducer($model, $producerCurrent);
            $model->product_price_percent = $userProducer->product_price_percent;
        }

        $pointsSaleArray = PointSale::find()
            ->where([
                'id_producer' => GlobalParam::getCurrentProducerId(),
                'status' => 1
            ])
            ->joinWith(['userPointSale' => function ($query) use ($model) {
                if ($model->id) {
                    $query->andOnCondition('user_point_sale.id_user = ' . $model->id);
                }
            }])
            ->all();

        $userGroupsArray = $userGroupManager->findUserGroups();

        return [
            'pointsSaleArray' => $pointsSaleArray,
            'userGroupsArray' => $userGroupsArray,
        ];
    }

    /**
     * Creates a new User model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     */
    public function actionCreate()
    {
        $userManager = $this->getUserManager();
        $producerManager = $this->getProducerManager();

        $producerCurrent = $this->getProducerCurrent();
        $model = $userManager->instanciateUser();

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

        if ($posts && isset($posts['User']['email']) && strlen($posts['User']['email']) > 0) {
            $userExist = $userManager->findOneUserByEmail($posts['User']['email']);
        }

        if ($userExist) {
            $producerManager->addUser($userExist, $producerCurrent);
            $this->processLinkPointSale($userExist);
            $this->processLinkUserGroup($userExist);
            $this->setFlash('success', "L'utilisateur que vous souhaitez créer possède déjà un compte sur la plateforme. Il vient d'être lié à votre établissement.");
        } else {
            if ($model->load(\Yii::$app->request->post()) && $model->validate() && YII_ENV != 'demo') {
                $model->id_producer = 0;
                $password = Password::generate();
                $userManager->setPassword($model, $password);
                $userManager->generateAuthKey($model);
                $model->username = $model->email;
                if (!strlen($model->email)) {
                    $model->username = 'inconnu@opendistrib.net';
                }

                $model->save();

                // liaison etablissement / user
                $useProducer = new  UserProducer();
                $useProducer->id_user = $model->id;
                $useProducer->id_producer = GlobalParam::getCurrentProducerId();
                $useProducer->credit = 0;
                $useProducer->active = 1;
                $useProducer->save();

                $userManager->sendMailWelcome($model, $password);
                $this->processLinkPointSale($model);
                $this->processLinkUserGroup($model);
                $this->processProductPricePercent($model);

                $this->setFlash('success', 'Utilisateur créé.');
                $model = $userManager->instanciateUser();
            }
        }

        return $this->render('create', array_merge($this->initForm($model), [
            'model' => $model,
        ]));

    }

    /**
     * Updates an existing User model.
     * If update is successful, the browser will be redirected to the 'view' page.
     */
    public function actionUpdate($id)
    {
        $userManager = $this->getUserManager();
        $producerManager = $this->getProducerManager();

        $model = $this->findModel($id);

        // Moodification du profil
        $previousMail = $model->email;
        $user = User::find()->with('userProducer')->where(['id' => $model['id']])->one();
        $userBelongToProducer = UserProducer::findOne(['id_user' => $id, 'id_producer' => GlobalParam::getCurrentProducerId()]);
        if ($userBelongToProducer) {
            if ($model->load(\Yii::$app->request->post()) && $model->save()) {

                // on envoie le mail de bienvenue si le mail vient d'être défini
                if (!strlen($previousMail) && strlen($model->email)) {
                    $password = Password::generate();
                    $userManager->setPassword($model, $password);
                    $model->username = $model->email;
                    $userManager->sendMailWelcome($model, $password);
                }
                $this->processLinkPointSale($model);
                $this->processLinkUserGroup($model);
                $this->processProductPricePercent($model);

                $this->setFlash('success', 'Utilisateur modifié.');
            }
        } else {
            throw new UserException("Vous ne pouvez pas modifier cet utilisateur.");
        }

        // Nouveau mot de passe
        $newPassword = Yii::$app->request->post('submit_new_password');
        if ($newPassword) {
            $password = Password::generate();
            $userManager->setPassword($model, $password);
            $model->save();

            $producer = $this->getProducerCurrent();

            Mailjet::sendMail([
                'from_email' => $producerManager->getEmailOpendistrib($producer),
                'from_name' => $producer->name,
                'to_email' => $model->email,
                'to_name' => $userManager->getUsername($user),
                'subject' => '[' . $producer->name . '] Nouveau mot de passe',
                'content_view_text' => '@common/mail/newPasswordUserAdmin-text.php',
                'content_view_html' => '@common/mail/newPasswordUserAdmin-html.php',
                'content_params' => [
                    'user' => $user,
                    'producer' => $producer,
                    'password' => $password,
                ]
            ]);

            $this->setFlash('success', 'Nouveau mot de passe envoyé.');
        }

        return $this->render('update', array_merge($this->initForm($model), [
            'model' => $model,
        ]));
    }

    /**
     * Lie un utilisateur aux points de vente sélectionnés.
     */
    public function processLinkPointSale(User $modelUser)
    {
        $posts = Yii::$app->request->post();
        UserPointSale::deleteAll([
            'id_user' => $modelUser->id
        ]);
        if (is_array($modelUser->points_sale) && count($modelUser->points_sale) > 0) {
            foreach ($modelUser->points_sale as $pointSaleId) {
                $userPointSale = UserPointSale::searchOne([
                    'id_user' => $modelUser->id,
                    'id_point_sale' => $pointSaleId
                ]);
                if (!$userPointSale) {
                    $userPointSale = new UserPointSale;
                    $userPointSale->id_user = $modelUser->id;
                    $userPointSale->id_point_sale = $pointSaleId;
                    $userPointSale->comment = isset($posts['User']['comment_point_sale_' . $pointSaleId]) ? $posts['User']['comment_point_sale_' . $pointSaleId] : '';
                    $userPointSale->save();
                }
            }
        }
    }

    /**
     * Lie un utilisateur aux groupes d'utilisateurs sélectionnés.
     */
    public function processLinkUserGroup($modelUser)
    {
        UserUserGroup::deleteAll([
            'id_user' => $modelUser->id
        ]);

        if (is_array($modelUser->user_groups) && count($modelUser->user_groups) > 0) {
            foreach ($modelUser->user_groups as $userGroupId) {

                $userUserGroup = UserUserGroup::searchOne([
                    'id_user' => $modelUser->id,
                    'id_user_group' => $userGroupId
                ]);

                if (!$userUserGroup) {
                    $userUserGroup = new UserUserGroup();
                    $userUserGroup->id_user = $modelUser->id;
                    $userUserGroup->id_user_group = $userGroupId;
                    $userUserGroup->save();
                }
            }
        }
    }

    public function processProductPricePercent($model)
    {
        $userProducer = UserProducer::searchOne([
            'id_producer' => GlobalParam::getCurrentProducerId(),
            'id_user' => $model->id
        ]);
        $userProducer->product_price_percent = $model->product_price_percent;

        $userProducer->save();
    }

    /**
     * Désactive l'utilisateur de l'établissement.
     */
    public function actionDelete(int $id)
    {
        $userManager = $this->getUserManager();
        $userProducerManager = $this->getUserProducerManager();

        $user = $userManager->findOneUserById($id);
        $producer = $this->getProducerCurrent();
        $userProducer = $userProducerManager->findOneUserProducer($user, $producer);

        if ($userProducer) {
            $userProducer->active = 0;
            $userProducer->bookmark = 0;
            $userProducer->save();

            $this->setFlash('success', 'L\'utilisateur a bien été supprimé de votre établissement.');
        } else {
            throw new \yii\web\NotFoundHttpException('L\'enregistrement UserProducer est introuvable', 404);
        }

        $params = Yii::$app->getRequest()->getQueryParams();
        unset($params['id']);

        $this->redirect(array_merge(['index'], $params));
    }

    /**
     * Affiche la liste des emails des utilisateurs liés à un point de vente donné.
     */
    public function actionMail(
        $idPointSale = 0,
        $sectionSubscribers = 0,
        $sectionInactiveUsers = 0,
        $usersPointSaleLink = 0,
        $usersPointSaleHasOrder = 0)
    {
        $userManager = $this->getUserManager();
        $distributionManager = $this->getDistributionManager();

        if ($idPointSale && !$usersPointSaleLink && !$usersPointSaleHasOrder) {
            $usersPointSaleLink = 1;
        }

        $users = $userManager->queryUsersBy([
            'id_producer' => GlobalParam::getCurrentProducerId(),
            'id_point_sale' => $idPointSale,
            'users_point_sale_link' => $usersPointSaleLink,
            'users_point_sale_has_order' => $usersPointSaleHasOrder,
            'subscribers' => $sectionSubscribers,
            'inactive' => $sectionInactiveUsers,
        ])->all();

        $usersArray = [];
        foreach ($users as $key => $user) {
            if (isset($user['email']) && strlen($user['email']) > 0) {
                $usersArray[] = $user['email'];
            }
            else {
                unset($users[$key]);
            }
        }

        $pointsSaleArray = PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId()])->all();

        $pointSale = null;
        if ($idPointSale) {
            $pointSale = PointSale::findOne(['id' => $idPointSale]);
        }

        $mailForm = new MailForm();
        if ($mailForm->load(\Yii::$app->request->post()) && $mailForm->validate()) {
            $responseSendMail = $mailForm->sendEmail($users);
            if ($responseSendMail->success()) {
                $this->setFlash('success', 'Votre email a bien été envoyé.');
            } else {
                $bodyResponseSendMail = $responseSendMail->getBody();
                $emailsErrorArray = [];

                if (isset($bodyResponseSendMail['Messages'])) {
                    foreach ($bodyResponseSendMail['Messages'] as $message) {
                        if ($message['Status'] != 'success') {
                            $emailsErrorArray[] = $message['Errors'][0]['ErrorMessage'];
                        }
                    }
                }

                $messageError = 'Un problème est survenu lors de l\'envoi de votre email.';
                if (count($emailsErrorArray) > 0) {
                    $messageError .= '<br />Problème détecté : ' . implode(',', $emailsErrorArray);
                }
                $this->setFlash('error', $messageError);
            }

            return $this->redirect(['mail', 'idPointSale' => $idPointSale]);
        }

        $incomingDistributions = $distributionManager->findDistributionsIncoming();
        $incomingDistributionsArray = ['0' => '--'];
        foreach ($incomingDistributions as $distribution) {
            $incomingDistributionsArray[$distribution->id] = strftime('%A %d %B %Y', strtotime($distribution->date));
        }

        return $this->render('emails', [
            'usersArray' => $usersArray,
            'pointsSaleArray' => $pointsSaleArray,
            'pointSale' => $pointSale,
            'mailForm' => $mailForm,
            'idPointSaleActive' => $idPointSale,
            'incomingDistributionsArray' => $incomingDistributionsArray,
            'sectionSubscribers' => $sectionSubscribers,
            'sectionInactiveUsers' => $sectionInactiveUsers,
            'usersPointSaleLink' => $usersPointSaleLink,
            'usersPointSaleHasOrder' => $usersPointSaleHasOrder,
        ]);
    }

    /**
     * Affiche les données liées au crédit d'un utilisateur (formulaire, historique).
     */
    public function actionCredit(int $id)
    {
        $user = User::find()->with('userProducer')->where(['id' => $id])->one();
        $userProducer = UserProducer::findOne(['id_user' => $id, 'id_producer' => GlobalParam::getCurrentProducerId()]);

        if (($userProducer) || $this->isUserCurrentAdmin()) {

            $creditForm = new CreditForm();
            if ($creditForm->load(\Yii::$app->request->post()) && $creditForm->validate()) {
                $creditForm->id_user = $id;
                $creditForm->save();

                $creditForm = new CreditForm;
            }

            $history = CreditHistory::find()
                ->with(['order', 'userAction'])
                ->where([
                    'id_user' => $user->id,
                    'id_producer' => GlobalParam::getCurrentProducerId(),
                ])
                ->orderBy('date DESC')
                ->all();

            return $this->render('credit', [
                'user' => $user,
                'userProducer' => $userProducer,
                'creditForm' => $creditForm,
                'history' => $history
            ]);
        } else {
            throw new UserException("Vous ne pouvez pas créditer un utilisateur qui n'est pas associé à votre établissement.");
        }
    }

    /**
     * Affiche les commandes d'un utilisateur.
     *
     * @param integer $id
     * @return mixed
     */
    public function actionOrders($id)
    {
        $userManager = $this->getUserManager();

        $user = $userManager->findOneUserById($id);
        $searchModel = new OrderSearch();
        $dataProvider = $searchModel->search(array_merge(\Yii::$app->request->queryParams, ['id_user' => $id]));

        return $this->render('orders', [
            'user' => $user,
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Modifie l'option "credit_active" d'un utilisateur pour le producteur courant.
     * Redirige vers la page de crédit de l'utilisateur.
     */
    public function actionStateCredit($idUser, $state)
    {
        $userManager = $this->getUserManager();
        $userProducerManager = $this->getUserProducerManager();

        $user = $userManager->findOneUserById($idUser);
        $producerCurrent = $this->getproducerCurrent();
        $userProducer = $userProducerManager->findOneUserProducer($user,$producerCurrent);

        if ($userProducer) {
            $userProducer->credit_active = $state;
            $userProducer->save();
        }

        return $this->redirect(['user/credit', 'id' => $idUser]);
    }

    /**
     * Finds the User model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     */
    protected function findModel($id)
    {
        $userManager = $this->getUserManager();

        if (($user = $userManager->findOneUserById($id)) !== null) {
            return $user;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}
