<?php


namespace Shop\Application\Office;

use Core\Concrete\AuthFactory;
use Core\Concrete\RequestAwareTrait;
use Core\Contracts\RequestAwareInterface;
use Core\Helpers\Text;
use Diamond\Application\Imaging\ImagingService;
use Diamond\Application\Notification\NotificationService;
use Diamond\Application\User\UserService;
use Diamond\Repositories\Site\SiteCriteria;
use Diamond\Repositories\Site\SiteRepository;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Propel;
use Shop\Application\Notification\SendEmail\NotificationException;
use Shop\Application\Office\AuthCustomer\AuthCustomerCommand;
use Shop\Application\Office\ChangeCustomerPassword\ChangeCustomerPasswordCommand;
use Shop\Application\Office\CreateDealer\CreateDealerCommand;
use Shop\Application\Office\CreateDealer\DealerRequest;
use Shop\Application\Office\CustomerRegistration\CustomerRegistrationCommand;
use Shop\Application\Office\CustomerRegistration\CustomerRequest;
use Shop\Application\Office\CustomerRegistration\CustomerResult;
use Shop\Application\Office\Decorators\Customer;
use Shop\Application\Office\Decorators\GuestCustomer;
use Shop\Application\Office\Exceptions\CustomerNotFoundException;
use Shop\Application\Office\Exceptions\MainStockNotFoundException;
use Shop\Application\Office\Exceptions\MainStockSomeFoundException;
use Shop\Application\Office\Exceptions\ManagerNotFoundException;
use Shop\Application\Office\Exceptions\OfficeException;
use Shop\Application\Office\Exceptions\StockNotFoundException;
use Shop\Application\Office\SendNotifyCustomerPassword\SendNotifyCustomerPasswordCommand;
use Shop\Application\Office\SendNotifyCustomerRegistration\SendNotifyCustomerRegistrationCommand;
use Shop\Presentation\Config\Main;
use Shop\Domain\Customer\Contracts\CustomerEntityInterface;
use Shop\Domain\Manager\Contracts\ManagerEntityInterface;
use Shop\Domain\Stock\Contracts\StockEntityInterface;
use Shop\Domain\Stock\StockCollection;
use Shop\Infrastructure\Models\Customer\CustomerQuery;
use Shop\Infrastructure\Models\PriceAccess\Map\PriceAccessTableMap;
use Shop\Infrastructure\Repositories\City\CityRepository;
use Shop\Infrastructure\Repositories\Customer\CustomerCriteria;
use Shop\Infrastructure\Repositories\Customer\CustomerRepository;
use Shop\Infrastructure\Repositories\Manager\ManagerCriteria;
use Shop\Infrastructure\Repositories\Manager\ManagerRepository;
use Shop\Infrastructure\Repositories\Stock\StockCriteria;
use Shop\Infrastructure\Repositories\Stock\StockRepository;
use spaceonfire\CommandBus\CommandBus;
use Symfony\Component\HttpFoundation\Session\Session;

class OfficeService implements RequestAwareInterface
{
    use RequestAwareTrait;

    protected static $customer;

    protected $customerRepository;
    protected $mainConf;
    protected $authFactory;
    protected $stockService;
    protected $cityRepository;
    protected $systemConf;
    protected $session;
    protected $managerRepository;
    protected $stockRepository;
    protected $siteRepository;

    protected $commandBus;
    protected $imagingService;


    protected $notificationService;
    protected $userService;

    public function __construct(CustomerRepository   $customerRepository,
                                Main                 $mainConf,
                                \Diamond\Config\Main $systemConf,
                                AuthFactory          $authFactory,
                                Session              $session,
                                StockRepository      $stockRepository,
                                ManagerRepository    $managerRepository,
                                CommandBus           $commandBus,
                                SiteRepository       $siteRepository,
                                ImagingService       $imagingService,
                                NotificationService  $notificationService,
                                CityRepository       $cityRepository,
                                UserService          $userService)
    {
        $this->customerRepository = $customerRepository;
        $this->mainConf = $mainConf;
        $this->authFactory = $authFactory;
        $this->cityRepository = $cityRepository;
        $this->systemConf = $systemConf;
        $this->session = $session;
        $this->managerRepository = $managerRepository;
        $this->stockRepository = $stockRepository;
        $this->siteRepository = $siteRepository;

        $this->commandBus = $commandBus;
        $this->imagingService = $imagingService;
        $this->userService = $userService;
        $this->notificationService = $notificationService;
    }

    /**
     * Получаем текущего клиента магазина
     * @return CustomerEntityInterface
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getCurrentCustomer(): CustomerEntityInterface
    {

        if (self::$customer !== null) {
            return self::$customer;
        }

        $userData = [];

        //-------------------------------------
        if (!$lifeTime = $this->systemConf->getSessionLife()) {
            $lifeTime = 1;
        }

        $resumeService = $this->authFactory->newResumeService(null, $lifeTime * 60 * 60, $lifeTime * 60 * 60);

        //объект авторизации
        $authService = $this->authFactory->newInstance();

        $resumeService->resume($authService);

        //если срок сессии вышел очистим ее
        if ($authService->isExpired()) {
            $this->logout();
        } elseif ($authService->isValid()) {
            $userData = $authService->getUserData();
        }

        if (!isset($userData['customer_id'])) {

            try {
                $stock = $this->getCurrentStock();
            } catch (StockNotFoundException $ex) {
                $stock = $this->getMainStock();
            }

            if (!$manager = $stock->getManagers()->getFirst()) {
                throw new OfficeException(sprintf(
                    'manager for stock %s not set', $stock->getTitle()
                ));
            }

            $city = $this->cityRepository->findById($this->mainConf->getDefaultCity());

            $customer = (new GuestCustomer())
                ->setName('guest' . time())
                ->setPhone(time())
                ->setEmail('guest' . time() . '@guest.ru')
                ->setCityId($city->getId())
                ->setCustomerCity($city)
                ->setManagerId($manager->getId())
                ->setManager($manager)
                ->setGroupId($this->mainConf->getDefaultGuestGroup());
        } else {
            $customer = $this->customerRepository->findById($userData['customer_id']);
        }

        //Замешиваем доступ к прайсам
        $pricesIds = [];

//        $pricesQ =
//            PriceAccessQuery::create('ac')
////                ->usePricelistFileQuery()
////                ->useProviderQuery()
////                ->filterByContractorId($customer->getManager()->getShop()->getContractorId())
////                ->endUse()
////                ->endUse()
//                ->addAlias('s', 'shop_pricelist_sheets')
//                ->addJoin('ac.price_scheet_id','s.id','RIGHT JOIN')
//                ->_if($customer->getGroupId()!==null)
//                    ->where($customer->getGroupId() . ' LIKE ac.customer_group_id')
//                ->_endif()
//                ->_if($customer->getGroupId()!==null)
//                ->where($customer->getGroupId() . ' LIKE ac.customer_group_id')
//                ->_endif()
//                ->_if($customer->getId()!==null)
//                ->where($customer->getId() . ' LIKE ac.customer_id')
//                ->_endif()
//                ->_if($customer->getManager()->getShop()!==null)
//                ->where($customer->getManager()->getShop()->getId() . ' LIKE ac.shop_id')
//                ->_endif()
//        ;
        $sql = 'SELECT
 GROUP_CONCAT(shop_pricelist_sheets.id) AS c
FROM shop_price_access
  INNER JOIN shop_pricelist_sheets
    ON shop_pricelist_sheets.id LIKE shop_price_access.price_scheet_id

WHERE ' . (int)$customer->getGroupId() . ' LIKE customer_group_id AND ' . (int)$customer->getManager()->getShop()->getId() .
            ' LIKE shop_id AND ' . (int)$customer->getId() . ' LIKE customer_id';


        $con = Propel::getServiceContainer()->getReadConnection(PriceAccessTableMap::DATABASE_NAME);
        $stmt = $con->prepare($sql);
        $stmt->execute();

        $res = $stmt->fetch();

        $priceIds = explode(',', $res['c']);

        array_walk($priceIds, function (&$v) {
            $v = (int)$v;
        });

        $customer->setAccessPrices($priceIds);

        self::$customer = $this->buildCustomer($customer);

        return self::$customer;
    }


    public function logout(): void
    {
        $this->userService->logout();
    }

    public function getChieldStocks(int $stockId): StockCollection
    {
        return $this->stockRepository->findByCriteria(
            StockCriteria::create()->setFilterByActive(true)->setFilterByParentId($stockId)
        );
    }

    /**
     * @param StockEntityInterface $stockEntity
     * @return string|null
     */
    public function getStockLogoPath(StockEntityInterface $stockEntity): ?string
    {

        $imgPath = $this->mainConf->getStockImagePath() . $stockEntity->getLogo();

        return is_file($this->imagingService->getUploadDir() . $imgPath) ?
            ($this->imagingService->getPublicUploadDir() . $imgPath) : null;
    }


    /**
     * @return string
     */
    public function getStockImageDir(): string
    {
        return $this->imagingService->getUploadDir() . $this->mainConf->getStockImagePath();
    }

    /**
     * @param StockEntityInterface $stockEntity
     * @return string|null
     */
    public function getStockImagePath(StockEntityInterface $stockEntity): string
    {
        $imgPath = $this->mainConf->getStockImagePath() . $stockEntity->getImage();

        return is_file($this->imagingService->getUploadDir() . $imgPath)
            ? ($this->imagingService->getUploadDir(false) . $imgPath) : '/assets/images/no-photo.jpg';
    }


    /**
     * получение активного клиента по email
     * @param string $email
     * @return CustomerEntityInterface
     * @throws CustomerNotFoundException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getActiveCustomerByEmail(string $email): CustomerEntityInterface
    {
        $customers = $this->customerRepository->findByCriteria(
            CustomerCriteria::create()->setFilterByEmail($email)->setFilterByBlocked(false)
        );

        if (!$customer = $customers->current()) {
            throw new CustomerNotFoundException(sprintf('active customer not found by email `%s`', $email));
        }

        return $this->buildCustomer($customer);
    }

    /**
     * @param string $code
     * @return CustomerEntityInterface
     * @throws CustomerNotFoundException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getActiveCustomerByFastCode(string $code): CustomerEntityInterface
    {
        $customers = $this->customerRepository->findByCriteria(
            CustomerCriteria::create()
                ->setFilterByPassword($code)
                ->setFilterByBlocked(false)
        );

        if (!$customer = $customers->current()) {
            throw new CustomerNotFoundException('customer not found by code ' . $code);
        }

        return $customer;
    }

    public function getByFastAuthKey(string $key): CustomerEntityInterface
    {

        $criteria = new Criteria();
        $criteria->add(
            sprintf(
                "md5(CONCAT(LOWER(email),'%s',password))",
                getenv('ENCRYPTION_KEY')
            )
            , $key, Criteria::EQUAL);

        $criteria->setLimit(1);

        $customer = CustomerQuery::create(null, $criteria)
            ->findOne();

        if (!$customer) {
            throw new CustomerNotFoundException('customer not found by code ' . $key);
        }

        return $customer;
    }


    /**
     * полученгие по id
     * @param int $id
     * @return CustomerEntityInterface
     * @throws CustomerNotFoundException
     */
    public function getCustomerById(int $id): CustomerEntityInterface
    {
        if (!$customer = $this->customerRepository->findById($id)) {
            throw new CustomerNotFoundException('customer not found by id' . $id);
        }


        return $this->buildCustomer($customer);
    }

    /**
     * @param CustomerEntityInterface $customer
     * @return CustomerEntityInterface
     */
    protected function buildCustomer(CustomerEntityInterface $customer): CustomerEntityInterface
    {
        return new Customer($customer, $this->session, $this->mainConf);
    }

    /**
     * @param CustomerEntityInterface $customerEntity
     */
    public function saveCustomer(CustomerEntityInterface $customerEntity): void
    {
        $this->customerRepository->save($customerEntity);
    }

    public function getCustomerRepository(): CustomerRepository
    {
        return $this->customerRepository;
    }

    /**
     * @return int
     */
    public function getCustomersCount(): int
    {
        return $this->customerRepository->count(CustomerCriteria::create());
    }

    /**
     * @return int
     */
    public function getActiveCustomersCount(): int
    {
        return $this->customerRepository->count(CustomerCriteria::create()->setFilterByBlocked(false));
    }

    /**
     * @param int $id
     * @return ManagerEntityInterface
     * @throws ManagerNotFoundException
     */
    public function getFirstManagerByStockId(int $id): ManagerEntityInterface
    {
        $managers = $this->managerRepository->findByCriteria(
            ManagerCriteria::create()->setFilterByStockId($id)
        );
        if ($managers->count() === 0) {
            throw new ManagerNotFoundException('manager not found by stock id ' . $id);
        }

        return $managers->current();
    }

    /**
     * @param int $int
     * @return StockCollection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getStocksByCityId(int $int): StockCollection
    {
        return $this->stockRepository->findByCriteria(
            StockCriteria::create()->setFilterByCityId($int)->setFilterByActive(true)
        );
    }

    /**
     * @param int $int
     * @return StockCollection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getStocksByRegionId(int $int): StockCollection
    {
        return $this->stockRepository->findByCriteria(
            StockCriteria::create()->setFilterByRegionId($int)->setFilterByActive(true)
        );
    }


    /**
     * @param DealerRequest $dealerComplex
     */
    public function createDealerComplex(DealerRequest $dealerComplex): void
    {
        $this->commandBus->handle(new CreateDealerCommand($dealerComplex));
    }


    /**
     * Получение всех активных точек с менджерами
     * @return StockCollection
     * @throws \Propel\Runtime\Exception\PropelException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getActiveStocksExistsManagers(): StockCollection
    {
        $stocks = $this->stockRepository->findByCriteria(
            StockCriteria::create()
                ->setFilterByActive(true)
                ->setFilterByTypes([
                    StockCriteria::TYPE_STOCK_DEALER,
                    StockCriteria::TYPE_STOCK_STOCK
                ])
        );
        /**
         * @var StockEntityInterface|Shop $stock
         */
        foreach ($stocks as $stock) {
            if ($stock->getManagers()->count() === 0) {
                $stocks->offsetUnset($stock);
            }
        }

        return $stocks;
    }


    /**
     * Получение текущей точки, которая привязана к сайту/домену, если несколько берем первую
     * @return StockEntityInterface
     * @throws StockNotFoundException
     * @throws \Repo\Concrete\Exceptions\Collection
     * @throws \Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException
     */
    public function getCurrentStock(): StockEntityInterface
    {
        //определяем текущий сайт/домен
        $sites = $this->siteRepository->findByCriteria(
            SiteCriteria::create()->setSearchByHost($this->request->getHost())->setLimit(1)
        );

        if ($sites->count() === 0) {
            throw new StockNotFoundException('current domain not found');
        }

        $currentDomain = $sites->current();

        //ищем точку по домену но только дилера или ГО
        $stocks = $this->stockRepository->findByCriteria(
            StockCriteria::create()
                ->setFilterBySiteId($currentDomain->getId())
                ->setFilterByTypes([
                    StockCriteria::TYPE_STOCK_MAIN,
                    StockCriteria::TYPE_STOCK_DEALER
                ])
                ->setFilterByActive(true)
        );

        if ($stocks->count() === 0) {
            throw new StockNotFoundException(sprintf('stocks by domain `%s` not found', $currentDomain->getDomain()));
        }

        //возвращаем первую точку
        return $stocks->current();
    }

    /**
     * @param int $id
     * @return StockEntityInterface
     * @throws StockNotFoundException
     */
    public function getStockById(int $id): StockEntityInterface
    {
        if (!$stock = $this->stockRepository->findById($id)) {
            throw new StockNotFoundException(sprintf('stock not found by id `%s`', $id));
        }

        return $stock;
    }


    /**
     * Ищем ГО точку
     * @return StockEntityInterface
     * @throws MainStockNotFoundException
     * @throws MainStockSomeFoundException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getMainStock(): StockEntityInterface
    {

        $stockMain = $this->stockRepository->findByCriteria(
            StockCriteria::create()
                ->setFilterByType(StockCriteria::TYPE_STOCK_MAIN)
                ->setFilterByActive(true)
        );

        if ($stockMain->count() === 0) {
            throw new MainStockNotFoundException('main stock not set');
        }

        if ($stockMain->count() > 1) {
            throw new MainStockSomeFoundException('main stock found more 1');
        }

        return $stockMain->current();
    }

    /**
     * @return StockCollection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getDealerStocks(): StockCollection
    {
        return $this->stockRepository->findByCriteria(
            StockCriteria::create()
                ->setFilterByType(StockCriteria::TYPE_STOCK_DEALER)
                ->setFilterByActive(true)
        );
    }


    /**
     * @param string $email
     * @param string|null $password
     * @throws \Repo\Concrete\Exceptions\Collection
     * @todo in command
     * генерация пароля с уведомлением на почту
     */
    public function generateCustomerPasswordByEmailWithNotify(string $email, string $password = null): void
    {
        $customers = $this->customerRepository->findByCriteria(
            CustomerCriteria::create()->setFilterByEmail($email)
        );

        if ($customers->count() === 0) {
            throw new OfficeException('user not found by email ' . $email);
        }

        if ($customers->count() > 1) {
            throw new OfficeException('many customers found by email ' . $email);
        }

        $customer = $customers->current();

        if (!$password) {
            $password = $this->generatePassword(6);
        }

        $customer->setPassword(md5($password));
        $this->customerRepository->save($customer);


        $data = array_change_key_case($customer->toArray());
        $data['code'] = $password;
        $this->notificationService->sendToEmailByReport('password_recovery', $customer->getEmail(), $data);
    }


    /**
     * Генерация кода
     * @param int $length
     * @return string
     * @deprecated
     */
    public function generateCode($length = 6): string
    {
        return Text::generatePassword($length);
    }

    /**
     * @param int $length
     * @return string
     */
    public function generatePassword($length = 6): string
    {
        return Text::generatePassword($length);
    }


    /**
     * @param CustomerRequest $customerRequest
     * @return CustomerEntityInterface
     * @deprecated
     */
    public function createCustomerWithNotify(CustomerRequest $customerRequest): CustomerEntityInterface
    {
        return $this->registrationCustomerWithNotify($customerRequest);
    }

    /**
     * @param CustomerRequest $customerRequest
     * @return CustomerEntityInterface
     * @deprecated
     */
    public function registrationCustomerWithNotify(CustomerRequest $customerRequest): CustomerEntityInterface
    {

        return $this->registerCustomer($customerRequest);
    }


    /**
     * Создание клиента
     * @param CustomerRequest $customerRequest
     * @return CustomerEntityInterface
     * @deprecated
     */
    public function createCustomer(CustomerRequest $customerRequest): CustomerEntityInterface
    {
        return $this->registerCustomer($customerRequest);
    }

    /**
     * @param CustomerRequest $customerRequest
     * @return CustomerEntityInterface
     */
    public function registerCustomer(CustomerRequest $customerRequest): CustomerEntityInterface
    {
        $result = new CustomerResult();
        $this->commandBus->handle(new CustomerRegistrationCommand($customerRequest, $result));
        $customer = $result->getCustomer();
        //Notification---------------------------------------------------------------------------------
        $this->commandBus->handle(new SendNotifyCustomerRegistrationCommand($customer));
        return $this->buildCustomer($customer);
    }


    /**
     * @param CustomerEntityInterface $customer
     * @param string $password
     */
    public function changeCustomerPassword(CustomerEntityInterface $customer, string $password): void
    {
        $this->commandBus->handle(new ChangeCustomerPasswordCommand($customer, $password));
        $this->commandBus->handle(new SendNotifyCustomerPasswordCommand($customer, $password));
    }

    /**
     * @return array
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getEmailsAll(): array
    {
        $customers = $this->customerRepository->findByCriteria(
            CustomerCriteria::create()
        );

        $emails = [];

        foreach ($customers as $customer) {
            $emails[] = $customer->getEmail();
        }

        return $emails;
    }


    /**
     * получение клиентов офиса 1 группы 3
     * @todo перевести на другой миханизм, путем сохранения фильтров клиентов
     */
    public function getEmailsgroup3ClientsOffice1(): array
    {
        $customers = \Shop\Infrastructure\Models\Customer\CustomerQuery::create()
            ->useManagerQuery()
            ->filterByShopId(1)
            ->endUse()
            ->filterByGroupId(3)
            ->find();
        $emails = [];

        foreach ($customers as $customer) {
            $emails[] = $customer->getEmail();
        }

        return $emails;
    }

    /**
     * @return array
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getEmailsBlockClients(): array
    {
        $customers = $this->customerRepository->findByCriteria(
            CustomerCriteria::create()->setFilterByBlocked(true)
        );
        $emails = [];

        foreach ($customers as $customer) {
            $emails[] = $customer->getEmail();
        }

        return $emails;
    }

    /**
     * @param string $email
     * @return array
     */
    public function getClientDataByEmail(string $email): array
    {
        if (!$customer = \Shop\Infrastructure\Models\Customer\CustomerQuery::create()
            ->findOneByEmail($email)) {
            return [];
        }

        return $customer->toArray();
    }


    /**
     * @param string $email
     * @return CustomerEntityInterface
     * @deprecated
     */
    public function getClientByEmail(string $email): CustomerEntityInterface
    {
        return $this->getCustomerByEmail($email);
    }

    /**
     * @param string $email
     * @return CustomerEntityInterface
     */
    public function getCustomerByEmail(string $email): CustomerEntityInterface
    {
        if (!$customer = \Shop\Infrastructure\Models\Customer\CustomerQuery::create()
            ->filterByGroupId([$this->mainConf->getDefaultGuestGroup(), $this->mainConf->getDefaultContractorGroup()], Criteria::NOT_IN)
            ->filterByEmail($email)
            ->findOne()) {
            throw new CustomerNotFoundException('customer not found by id ' . $email);
        }

        return $customer;
    }

    /**
     *
     * @return array
     */
    public function getEmailsNoneAuthClients(): array
    {
        $customers = \Shop\Infrastructure\Models\Customer\CustomerQuery::create()
            ->filterByBlocked("N")
            ->filterByLastauth(null, \Propel\Runtime\ActiveQuery\Criteria::EQUAL)//=
            ->find();
        $emails = [];

        foreach ($customers as $customer) {
            $emails[] = $customer->getEmail();
        }

        return $emails;
    }


    /**
     *
     * @return array
     */
    public function getEmailsAuthClients(): array
    {
        $customers = \Shop\Infrastructure\Models\Customer\CustomerQuery::create()
            ->filterByBlocked('N')
            ->filterByLastauth('', \Propel\Runtime\ActiveQuery\Criteria::GREATER_THAN)//>=
            ->find();
        $emails = [];

        foreach ($customers as $customer) {
            $emails[] = $customer->getEmail();
        }

        return $emails;
    }


    /**
     * @param string $email
     * @return CustomerEntityInterface
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function recoveryPasswordByEmail(string $email): CustomerEntityInterface
    {

        $customer = $this->getActiveCustomerByEmail($email);

        $code = Text::generatePassword();

        $customer
            ->setRegisterCode($code)
            ->setPassword(md5($code));

        $this->saveCustomer($customer);

        return $customer;
    }

    /**
     * @param string $password
     * @param CustomerEntityInterface $customer
     * @throws \Repo\Concrete\Exceptions\Collection
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function notifyRecoveryPassword(string $password, CustomerEntityInterface $customer): void
    {
        $data = $customer->toArray();
        $data['code'] = $password;
        $this->notificationService->sendToEmailByReport('password_recovery', $customer->getEmail(), $data);
    }


    /**
     * @param int $cityId
     * @return ManagerEntityInterface
     * @throws \Propel\Runtime\Exception\PropelException
     */
    public function getManagerByCityId(int $cityId): ManagerEntityInterface
    {

        $manager = \Shop\Infrastructure\Models\Manager\ManagerQuery::create()
            ->useShopQuery()
            ->filterByCityId($cityId)
            ->filterByType(["main", "dealer"])
            ->endUse()
            ->findOne();

        if (!$manager) {
            $region = \Shop\Infrastructure\Models\Region\RegionQuery::create()
                ->useCityQuery()
                ->filterById($cityId)
                ->endUse()
                ->findOne();
            if ($region) {
                $manager = \Shop\Infrastructure\Models\Manager\ManagerQuery::create()
                    ->useShopQuery()
                    ->useAddressQuery()
                    ->useCityQuery()
                    ->useCityRegionQuery()
                    ->filterById($region->getId())
                    ->endUse()
                    ->endUse()
                    ->endUse()
                    ->filterByType(["main", "dealer"])
                    ->endUse()
                    ->findOne();
            }
        }

        if (!$manager) {
            $manager = \Shop\Infrastructure\Models\Manager\ManagerQuery::create()
                ->useShopQuery()
                ->filterByType("main")
                ->endUse()
                ->findOne();
        }

        if (!$manager) {
            throw new ManagerNotFoundException('manager not found by cityId ' . $cityId);
        }

        return $manager;
    }

    /**
     * авторизация клиента
     * @param string $login
     * @param string|null $password
     * @param string|null $hashPassword
     * @param int|null $customerGroupId
     */
    public function authCustomer(string $login, ?string $password, ?string $hashPassword = null, int $customerGroupId = null): void
    {
        $this->commandBus->handle(new AuthCustomerCommand($login, $password, $hashPassword, $customerGroupId));
    }

}