<?php

/**
 * Copyright Souke (2018)
 *
 * contact@souke.fr
 *
 * 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\Password;
use domain\Order\Order\OrderSearch;
use domain\PointSale\PointSale\PointSale;
use domain\PointSale\UserPointSale\UserPointSale;
use domain\User\User\User;
use domain\User\User\UserSearch;
use domain\User\UserProducer\UserProducer;
use domain\User\UserUserGroup\UserUserGroup;
use Yii;
use yii\base\UserException;
use yii\filters\AccessControl;
use yii\helpers\Html;
use yii\web\NotFoundHttpException;

/**
 * UserController implements the CRUD actions for User model.
 */
class UserController extends BackendController
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                        'matchCallback' => function ($rule, $action) {
                            $userCurrent = $this->getUserCurrent();
                            $authorizationChecker =  $this->getUserModule()->getAuthorizationChecker();
                            if(in_array($action->id, ['messages', 'message-delete'])) {
                                return $authorizationChecker->isGrantedAsAdministrator($userCurrent);
                            }
                            else {
                                return $authorizationChecker->isGrantedAsProducer($userCurrent);
                            }
                        }
                    ],
                ],
            ],
        ];
    }

    /**
     * Liste les utilisateurs.
     */
    public function actionIndex(
        int  $idPointSale = 0,
        bool $sectionSubscribers = false,
        bool $sectionInactiveUsers = false)
    {
        $pointSaleModule = $this->getPointSaleModule();
        $producer = $this->getProducerCurrent();
        $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);

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

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

    public function actionCreate(string $redirectUrl = null)
    {
        $userModule = $this->getUserModule();
        $producerModule = $this->getProducerModule();
        $producerCurrent = $this->getProducerCurrent();
        $model = $userModule->instanciateUser();
        $model->redirect_url = $redirectUrl;
        $posts = Yii::$app->request->post();
        $userExist = false;

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

        if ($userExist) {
            $producerModule->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.");
            return $this->redirectAfterSave('user', $userExist->id);
        } else {
            if ($model->load(\Yii::$app->request->post()) && $model->validate() && YII_ENV != 'demo') {

                $userCreate = $userModule->getBuilder()->createUser(
                    $model->type,
                    $model->email,
                    $model->name,
                    $model->lastname,
                    $model->name_legal_person,
                    $model->phone,
                    $model->address,
                    $model->newsletter,
                    Password::generate(),
                    (bool)$model->send_mail_welcome
                );
                $userCreate->points_sale = $model->points_sale;
                $userCreate->user_groups = $model->user_groups;

                $this->processLinkPointSale($userCreate);
                $this->processLinkUserGroup($userCreate);
                //$this->processProductPricePercent($model);

                $this->setFlash('success', 'Utilisateur créé.');

                return $this->redirectAfterSave('user', $userCreate->id, $model->redirect_url);
            }
        }

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

    public function actionView($id)
    {
        $userModule = $this->getUserModule();
        $pointSaleModule = $this->getPointSaleModule();
        $model = $this->findModel($id);

        // Email de bienvenue
        $mailWelcome = Yii::$app->request->post('submit_mail_welcome');
        if ($mailWelcome) {
            $this->getUserModule()->getManager()->welcome($model);
            $this->setFlash('success', 'Email de bienvenue envoyé à <strong>' . Html::encode($userModule->getSolver()->getUsername($model)) . '</strong>.');
            return $this->redirect(['view', 'id' => $model->id]);
        }

        // Mot de passe oublié
        $newPassword = Yii::$app->request->post('submit_new_password');
        if ($newPassword) {
            $this->getUserModule()->getManager()->newPassword($model);
            $this->setFlash('success', 'Nouveau mot de passe envoyé à <strong>' . Html::encode($userModule->getSolver()->getUsername($model)) . '</strong>.');
            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('view', [
            'model' => $model,
            'pointSaleBillingArray' => $pointSaleModule->findByBillingUser($model),
            'subscriptionsArray' => $this->getSubscriptionModule()->getRepository()->findSubscriptionsByUser($model)
        ]);
    }

    public function actionUpdate($id)
    {
        if($this->getProducerCurrent()->isDemoAccount()) {
            $this->addFlash('error', "Fonctionnalité non disponible sur le compte de démo.");
            return $this->redirect(['view', 'id' => $id]);
        }

        $userModule = $this->getUserModule();
        $pointSaleModule = $this->getPointSaleModule();
        $model = $this->findModel($id);
        $previousMail = $model->email;
        $userBelongToProducer = UserProducer::findOne(['id_user' => $id, 'id_producer' => GlobalParam::getCurrentProducerId()]);
        if ($userBelongToProducer) {
            $model->newsletter = $userBelongToProducer->newsletter;
            $model->trust_alert = $userBelongToProducer->trust_alert;
            $model->trust_alert_comment = $userBelongToProducer->trust_alert_comment;

            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) && $model->send_mail_welcome) {
                    $password = Password::generate();
                    $userModule->setPassword($model, $password);
                    $model->username = $model->email;
                    $userModule->sendMailWelcome($model, $password);
                }
                $this->processLinkPointSale($model);
                $this->processLinkUserGroup($model);
                //$this->processProductPricePercent($model);

                // Newsletter
                if ($model->newsletter) {
                    $userModule->getNewsletterManager()->subscribeUserNewsletter($model);
                } else {
                    $userModule->getNewsletterManager()->unsubscribeUserNewsletter($model);
                }

                // Alerte confiance
                $userBelongToProducer->trust_alert = $model->trust_alert;
                $userBelongToProducer->trust_alert_comment = $model->trust_alert_comment;
                $userBelongToProducer->save();

                // Mot de passe
                if($this->getUserModule()->getAuthorizationChecker()->isGrantedAsAdministrator($this->getUserCurrent())
                    && $model->password_new
                    && strlen($model->password_new)) {

                    $this->getUserModule()->getBuilder()->initPassword($model, $model->password_new);
                    $model->save();
                }

                $this->setFlash('success', 'Utilisateur <strong>' . Html::encode($userModule->getUsername($model)) . '</strong> modifié.');
                return $this->redirect(['update', 'id' => $model->id]);
            }
        } else {
            throw new UserException("Vous ne pouvez pas modifier cet utilisateur.");
        }

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

    public function actionMessages(int $id, int $idUserMessage = null)
    {
        $userMessageModule = $this->getUserMessageModule();
        $user = $this->findModel($id);
        $isUpdate = false;

        if($idUserMessage) {
            $isUpdate = true;
            $userMessageModel = $this->findUserMessage($idUserMessage);
        }
        else {
            $userMessageModel = $userMessageModule->getBuilder()->instanciateUserMessage(
                $user,
                $this->getUserCurrent()
            );
        }

        if($userMessageModel->load(\Yii::$app->request->post()) && $userMessageModel->validate()) {
            if($isUpdate) {
                $userMessageModel->save();
                $this->setFlash('success', "Le message a bien été modifié.");
            }
            else {
                $userMessageModule->getManager()->createUserMessage(
                    $user,
                    $userMessageModel->getMessage(),
                    $this->getUserCurrent()
                );
                $this->setFlash('success', "Le message a bien été envoyé à l'utilisateur.");
            }

            return $this->redirect(['messages', 'id' => $id]);
        }

        return $this->render('messages', [
            'user' => $user,
            'userMessageModel' => $userMessageModel,
            'userMessagesDataProvider' => $userMessageModule->getRepository()
                ->queryUserMessagesByUser($user)
                ->getDataProvider(20)
        ]);
    }

    public function actionMessageDelete(int $idUser, int $idUserMessage)
    {
        $userMessageModule = $this->getUserMessageModule();
        $userMessage = $this->findUserMessage($idUserMessage);
        if($userMessageModule->getManager()->deleteUserMessage($userMessage)) {
            $this->setFlash('success', "Le message a bien été supprimé.");
        }
        else {
            $this->setFlash('error', "Une erreur est survenue pendant la suppression du message.");
        }
        return $this->redirect(['messages', 'id' => $idUser]);
    }

    public function findUserMessage(int $idUserMessage)
    {
        $userMessage = $this->getUserMessageModule()->getRepository()->findOneUserMessageById($idUserMessage);

        if(!$userMessage) {
            throw new NotFoundHttpException("Le message utilisateur n'a pas été trouvé.");
        }

        return $userMessage;
    }

    public function actionReportProblemReceivingEmails(int $id)
    {
        $user = $this->findModel($id);

        if($this->getUserModule()->getManager()->reportProblemReceivingEmails($user)) {
            $this->setFlash('success', "L'utilisateur <strong>".$this->getUserModule()->getSolver()->getUsername($user)."</strong> a bien été signalé comme ayant des problèmes dans la réception de vos emails. L'administrateur débloquera la situation dès que possible et préviendra l'utilisateur.");
        }
        else {
            $this->setFlash('error', "Une erreur est survenue.");
        }

        return $this->redirect('index');
    }

    public function initForm($model)
    {
        $userPointSaleModule = $this->getUserPointSaleModule();
        $userUserGroupModule = $this->getUserUserGroupModule();
        $userProducerModule = $this->getUserProducerModule();
        $userGroupModule = $this->getUserGroupModule();

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

            $userUserGroupsArray = $userUserGroupModule->findUserUserGroupsByUser($model);
            if ($userUserGroupsArray && count($userUserGroupsArray) > 0) {
                foreach ($userUserGroupsArray as $userUserGroup) {
                    $model->user_groups[] = $userUserGroup->id_user_group;
                }
            }
            $userProducer = $userProducerModule->findOneUserProducer($model);
            $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 = $userGroupModule->findUserGroups();

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

    /**
     * Lie un utilisateur aux points de vente sélectionnés.
     */
    public function processLinkPointSale(User $modelUser)
    {
        $posts = Yii::$app->request->post();
        $userPointSaleModule = $this->getUserPointSaleModule();
        $pointSaleModule = $this->getPointSaleModule();

        UserPointSale::deleteAll([
            'id_user' => $modelUser->id
        ]);
        if (is_array($modelUser->points_sale) && count($modelUser->points_sale) > 0) {
            foreach ($modelUser->points_sale as $pointSaleId) {
                $pointSale = $pointSaleModule->findOnePointSaleById($pointSaleId);
                $userPointSale = $userPointSaleModule->findOneUserPointSale($modelUser, $pointSale);

                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)
    {
        $userModule = $this->getUserModule();
        $userProducerModule = $this->getUserProducerModule();

        $user = $userModule->findOneUserById($id);
        $userProducer = $userProducerModule->findOneUserProducer($user);

        if ($userProducer) {
            if ($userProducerModule->hasOutstandingCredit($userProducer)) {
                $this->setFlash('error', "Vous ne pouvez pas supprimer cet utilisateur car il a toujours de l'argent dans sa cagnotte.");
            } else {
                $userProducerModule->unlinkUserProducer($userProducer);
                $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 les données liées à la cagnotte d'un utilisateur (formulaire, historique).
     */
    public function actionCredit(int $id)
    {
        $producerModule = $this->getProducerModule();
        $userModule = $this->getUserModule();
        $paymentModule = $this->getPaymentModule();
        $userProducerModule = $this->getUserProducerModule();

        $user = $userModule->findOneUserById($id);
        $userProducer = $userProducerModule->getRepository()->findOneUserProducer($user);

        if ($userProducer) {
            $creditForm = new CreditForm();
            $creditForm->send_mail = $producerModule->getSolver()->getConfig('option_check_by_default_prevent_user_credit');

            if ($creditForm->load(\Yii::$app->request->post()) && $creditForm->validate()) {

                $dateTransaction = null;
                if($creditForm->date_transaction) {
                    $dateTransaction = date('Y-m-d',  strtotime(str_replace('/', '-', $creditForm->date_transaction)));
                }

                $paymentModule->getManager()
                    ->creditOrDebitUser(
                        $creditForm->type,
                        $user,
                        $creditForm->amount,
                        $creditForm->mean_payment,
                        $this->getUserCurrent(),
                        $creditForm->comment,
                        $dateTransaction
                    );

                if ($creditForm->send_mail && !$this->getProducerCurrent()->isDemoAccount()) {
                    $paymentModule->getNotifier()
                        ->notifyUserCreditMovement(
                            $user,
                            $creditForm->type,
                            $creditForm->amount
                        );
                    if (!$user->email) {
                        $this->addFlash('error', "L'utilisateur n'a pas pu être prévenu car son adresse email n'est pas définie.");
                    }
                }

                $this->addFlash('success', 'Cagnotte mise à jour.');

                return $this->refresh();
            }

            return $this->render('credit', [
                'user' => $user,
                'userProducer' => $userProducer,
                'creditForm' => $creditForm,
                'dataProvider' => $paymentModule->getRepository()
                    ->queryPaymentsCreditHistoryByUser($user)->getDataProvider(20),
            ]);
        } else {
            throw new UserException("Utilisateur introuvable.");
        }
    }

    /**
     * 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)
    {
        $userModule = $this->getUserModule();
        $userProducerModule = $this->getUserProducerModule();

        $user = $userModule->findOneUserById($idUser);
        $userProducer = $userProducerModule->findOneUserProducer($user);

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

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

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

        $user = $userModule->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,
        ]);
    }

    /**
     * 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)
    {
        $userModule = $this->getUserModule();

        if (($user = $userModule->findOneUserById($id)) !== null) {
            return $user;
        } else {
            throw new NotFoundHttpException("Utilisateur introuvable");
        }
    }

    public function actionSwitchIdentity(int $id)
    {
        $userModule = $this->getUserModule();
        if ($this->isUserCurrentAdmin()) {
            $initialId = Yii::$app->user->getId();
            if ($initialId != $id) {
                $user = $userModule->findOneUserById($id);
                $duration = 0;
                Yii::$app->user->switchIdentity($user, $duration);
                Yii::$app->session->set('user.idbeforeswitch', $initialId);
                return $this->redirect($this->getUrlManagerFrontend()->createUrl(['site/index']));
            }
        } else {
            throw new NotFoundHttpException('Page introuvable');
        }
    }
}
