<?php

namespace common\logic\User\User\Repository;

use common\helpers\GlobalParam;
use common\logic\AbstractRepository;
use common\logic\PointSale\PointSale\Model\PointSale;
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\UserProducer\Repository\UserProducerRepository;
use yii\db\Query;

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

    public function loadDependencies(): void
    {
        $this->loadQuery(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 [
            self::WITH => [],
            self::JOIN_WITH => ['userProducer', 'userUserGroup'],
            self::ORDER_BY => 'user.name ASC, user.lastname ASC',
            self::ATTRIBUTE_ID_PRODUCER => ''
        ];
    }

    public function findOneUserById($id)
    {
        return $this->createDefaultQuery()
            ->filterById($id)
            ->findOne();
    }

    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 crédit de l'utilisateur pour un producteur donné.
     *
     */
    public function getCredit(User $user, bool $reloadUserProducer = false): float
    {
        if($reloadUserProducer) {
            $userProducer = $this->userProducerRepository->findOneUserProducer($user);
        }
        else {
            $userProducer = $this->userSolver->getUserProducer($user);
        }

        return $userProducer ? $userProducer->credit : 0;
    }

    public function getCreditActive(User $user): bool
    {
        $userProducer = $this->userSolver->getUserProducer($user);
        return $userProducer ? $userProducer->credit_active : false;
    }

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

        $query = (new Query())
            ->select(['user.id AS user_id', 'user.type', '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;
        $filterNewsletter = (isset($params['newsletter']) && $params['newsletter']) ? 'AND user_producer.newsletter = 1' : '';
        $query->innerJoin('user_producer', 'user.id = user_producer.id_user AND user_producer.active = ' . $active . ' '.$filterNewsletter.' 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;
    }

    public function queryUsersWithNegativeCredit()
    {
        return $this->queryUsersBy()
            ->andWhere('user_producer.credit < 0')
            ->orderBy('lastname, name ASC');
    }

    public function findUsersWithNegativeCredit()
    {
        return $this->queryUsersWithNegativeCredit()->all();
    }

    public function queryUsersWithPositiveCredit()
    {
        return $this->queryUsersBy()
            ->andWhere('user_producer.credit > 0')
            ->orderBy('lastname, name ASC');
    }

    public function findUsersWithPositiveCredit()
    {
        return $this->queryUsersWithPositiveCredit()->all();
    }

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

        return $this->createDefaultQuery()
            ->filterByPasswordResetToken($token)
            ->findOne();
    }

    /**
     * Recherche un utilisateur via son adresse email.
     */
    public function findOneUserByEmail(string $email): ?User
    {
        return $this->createDefaultQuery()
            ->filterByEmail($email)
            ->findOne();
    }

    public function findOneUserByUsername(string $username): ?User
    {
        return $this->createDefaultQuery()
            ->filterByUsername($username)
            ->findOne();
    }

    public function findUsersByProducer(Producer $producer)
    {
        return $this->createDefaultQuery()
            ->isStatusProducer()
            ->filterByProducer($producer)
            ->find();
    }

    public function findUsersByStatus(string $status)
    {
        return $this->createDefaultQuery()
            ->filterByStatus($status)
            ->find();
    }

    public function countUsersActiveLastThreeMonths()
    {
        $connection = \Yii::$app->getDb();
        $command = $connection->createCommand(
            '
            SELECT `user`.id, COUNT(`order`.id) AS count_orders
            FROM `user`
            LEFT JOIN `order` ON `user`.id = `order`.`id_user` AND `order`.`date` >= :date_limit
            GROUP BY `user`.id
            HAVING count_orders > 0;',
            [
                ':date_limit' => date('Y-m-d', strtotime("-3 month")),
            ]
        );

        $results = $command->query();

        return count($results);
    }

    public function isActive(User $user)
    {
        $userProducer = $this->userProducerRepository->findOneUserProducer($user);
        return $userProducer ? $userProducer->active : false;
    }
}