<?php

namespace common\logic\User\User\Repository;

use common\helpers\GlobalParam;
use common\logic\AbstractRepository;
use common\logic\Order\Order\Model\Order;
use common\logic\PointSale\PointSale\Model\PointSale;
use common\logic\PointSale\UserPointSale\Model\UserPointSale;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\User\User\Model\User;
use common\logic\User\User\Service\UserSolver;
use common\logic\User\UserGroup\Model\UserGroup;
use common\logic\User\UserProducer\Repository\UserProducerRepository;
use common\logic\User\UserUserGroup\Model\UserUserGroup;
use yii\db\Query;

class UserRepository extends AbstractRepository
{
    protected UserRepositoryQuery $query;
    protected UserProducerRepository $userProducerRepository;
    protected UserSolver $userSolver;

    public function loadDependencies(): void
    {
        $this->query = $this->loadService(UserRepositoryQuery::class);
        $this->userProducerRepository = $this->loadService(UserProducerRepository::class);
        $this->userSolver = $this->loadService(UserSolver::class);
    }

    /**
     * Retourne les options de base nécessaires à la fonction de recherche.
     *
     * @return array
     */
    public function getDefaultOptionsSearch(): array
    {
        return [
            'with' => [],
            'join_with' => ['userProducer', 'userUserGroup'],
            'orderby' => 'user.name ASC, user.lastname ASC',
            'attribute_id_producer' => ''
        ];
    }

    public function findOneUserById($id)
    {
        return User::searchOne(['id' => $id]);
    }

    public function findUsers(): array
    {
        return $this->queryUsersBy()->all();
    }

    public function populateUserDropdownList()
    {
        $usersArray = $this->findUsers();
        $usersArrayDropdown = ['' => '--'];

        foreach ($usersArray as $user) {
            $usersArrayDropdown[$user['user_id']] = $this->userSolver->getUsernameFromArray($user, true);
        }

        return $usersArrayDropdown;
    }

    /**
     * Retourne le point de vente favoris d'un utilisateur : le point de vente auquel le client est lié,
     * le point de vente de la dernière commande sinon.
     *
     */
    public function getUserFavoritePointSale(User $user): ?PointSale
    {
        $arrayUserPointSale = UserPointSale::find()
            ->innerJoinWith('pointSale', true)
            ->where([
                'user_point_sale.id_user' => $user->id,
                'point_sale.id_producer' => GlobalParam::getCurrentProducerId()
            ])
            ->all();

        if (count($arrayUserPointSale) == 1) {
            $pointSale = PointSale::findOne(['id' => $arrayUserPointSale[0]->id_point_sale]);
        } else {
            $lastOrder = Order::find()->innerJoinWith('pointSale', true)->where([
                'order.id_user' => $user->id,
                'point_sale.id_producer' => GlobalParam::getCurrentProducerId()
            ])
                ->orderBy('order.id DESC')
                ->one();

            if ($lastOrder) {
                $pointSale = PointSale::findOne(['id' => $lastOrder->id_point_sale]);
            }
        }

        if (isset($pointSale)) {
            return $pointSale;
        }

        return null;
    }

    /**
     * Retourne le crédit de l'utilisateur pour un producteur donné.
     *
     */
    public function getCredit(User $user, Producer $producer): float
    {
        $userProducer = $this->userProducerRepository->findOneUserProducer($user, $producer);
        return $userProducer ? $userProducer->credit : 0;
    }

    /**
     * Recherche des utilisateurs suivant les paramètres : id_etablissement,
     * inactifs, id_point_vente, nom, prenom, email, telephone.
     *
     * @param array $params
     * @return Query
     */
    // findBy
    public function queryUsersBy(array $params = [])
    {
        if (!isset($params['id_producer'])) {
            $params['id_producer'] = GlobalParam::getCurrentProducerId();
        }

        $query = (new Query())
            ->select(['user.id AS user_id', 'user.name', 'user.lastname', 'user.phone', 'user.email', 'user.created_at', 'user.date_last_connection', 'user_producer.*', 'user.address', 'user.name_legal_person'])
            ->from('user');

        $active = (isset($params['inactive']) && $params['inactive']) ? 0 : 1;
        $query->innerJoin('user_producer', 'user.id = user_producer.id_user AND user_producer.active = ' . $active . ' AND user_producer.id_producer = :id_producer', [':id_producer' => $params['id_producer']]);

        if (isset($params['id_point_sale']) && $params['id_point_sale']) {
            $point_sale = PointSale::findOne(['id' => $params['id_point_sale']]);

            $conditionLinkUserPointSale = 'user.id = user_point_sale.id_user AND user_point_sale.id_point_sale = :id_point_sale';

            $usersPointSaleLink = false;
            $usersPointSaleHasOrder = false;

            if (isset($params['users_point_sale_link']) && $params['users_point_sale_link']) {
                $usersPointSaleLink = true;
            } elseif (isset($params['users_point_sale_has_order']) && $params['users_point_sale_has_order']) {
                $usersPointSaleHasOrder = true;
            } elseif ($point_sale->restricted_access) {
                $usersPointSaleLink = true;
            } else {
                $usersPointSaleHasOrder = true;
            }

            if ($usersPointSaleLink) {
                $query->innerJoin('user_point_sale', 'user.id = user_point_sale.id_user AND user_point_sale.id_point_sale = :id_point_sale', [':id_point_sale' => $params['id_point_sale']]);
            } elseif ($usersPointSaleHasOrder) {
                $query->innerJoin(
                    'order',
                    'user.id = order.id_user AND order.id_point_sale = :id_point_sale',
                    [':id_point_sale' => $params['id_point_sale']]
                )->groupBy('user.id');
            }
        }

        if (isset($params['subscribers']) && $params['subscribers']) {
            $query->innerJoin(
                'subscription',
                'user.id = subscription.id_user AND subscription.id_producer = :id_producer',
                [':id_producer' => GlobalParam::getCurrentProducerId()]
            )->groupBy('user.id');
        }

        if (isset($params['inactive']) && $params['inactive']) {
            $query->innerJoin(
                'order',
                'user.id = order.id_user'
            )
                ->groupBy('user.id');
        }

        if (isset($params['name'])) {
            $query->andFilterWhere(['like', 'name', $params['name']]);
        }

        if (isset($params['lastname'])) {
            $query->andFilterWhere(['like', 'lastname', $params['lastname']]);
        }

        if (isset($params['email'])) {
            $query->andFilterWhere(['like', 'email', $params['email']]);
        }

        if (isset($params['phone'])) {
            $query->andFilterWhere(['like', 'phone', $params['phone']]);
        }

        $query->orderBy('user.type DESC, user.lastname ASC, user.name ASC');

        return $query;
    }

    /**
     * Finds user by password reset token
     */
    // getByPasswordResetToken
    public function findOneUserByPasswordResetToken(string $token)
    {
        if (!$this->userSolver->isPasswordResetTokenValid($token)) {
            return null;
        }

        return User::findOne([
            'password_reset_token' => $token,
        ]);
    }

    /**
     * Recherche un utilisateur via son adresse email.
     */
    // getOneByEmail
    public function findOneUserByEmail(string $email): ?User
    {
        return User::searchOne(['email' => $email]);
    }

    // getOneByUsername
    public function findOneUserByUsername(string $username): ?User
    {
        return User::searchOne(['username' => $username]);
    }

    public function findUsersByProducer(Producer $producer)
    {
        return User::find()
            ->where([
                'id_producer' => $producer->id,
                'status' => User::STATUS_PRODUCER
            ])
            ->all();
    }

    public function findUsersByStatus(string $status)
    {
        return User::find()
            ->where([
                'user.status' => $status
            ])
            ->all();
    }
}