<?php
/**
 * Copyright (c) 2016 Serhii Borodai <clarifying@gmail.com>.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 *
 */

namespace App\Action\Admin;

use App\Authentication\AggregateAuth;
use App\Entity\User;
use Monolog\Logger;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Hydrator\DelegatingHydrator;


/**
 * Class Users
 * @package App\Action\Admin
 */
class Users extends Common
{
    const STATUS_DEACTIVATE = 'deactivate';
    const STATUS_ACTIVATE = 'activate';
    const STATUS_UNBLOCK = 'unblock';
    const STATUS_BLOCK = 'block';
    const STATUS_CREATE = 'create';
    const STATUS_EDIT = 'edit';
    const STATUS_SITE = 'checksite';

    /**
     * @param ServerRequestInterface $request
     * @param ResponseInterface $response
     * @param callable|null $next
     * @return HtmlResponse|JsonResponse
     */
    function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
    {    
        if ($request->getMethod() == 'POST') {
            $action = $request->getAttribute('action');
            $id = $request->getAttribute('id');
            
            try {
                switch ($action) {
                    case self::STATUS_ACTIVATE:
                        $response = $this->updateStatus($id, 'activated', User::ACTIVATED);
                        break;
                    case self::STATUS_DEACTIVATE:
                        $response = $this->updateStatus($id, 'activated', User::NOT_ACTIVATED);
                        break;
                    case self::STATUS_BLOCK:
                        $response = $this->updateStatus($id, 'blocked', User::BLOCKED);
                        break;
                    case self::STATUS_UNBLOCK:
                        $response = $this->updateStatus($id, 'blocked', User::NOT_BLOCKED);
                        break;   
                    case self::STATUS_CREATE:
                        $response = $this->createUser($request);
                        break;
                    case self::STATUS_EDIT:
                        $response = $this->editUser($request);
                        break;
                    case self::STATUS_SITE:
                        $response = $this->checkSite($request);
                        break;
                    default:
                        throw new \Exception('Unknown action');                     
                }
            } catch(\Exception $e) {                
                $data = [
                    'result' => false,
                    'msg' => $e->getMessage(),
                ];                
                $response = new JsonResponse($data);
            }
            return $response;
        } elseif($request->getAttribute('page') == 'create' || $request->getAttribute('page') == 'edit') {                        
            
            $user = null;
            $sites = null;                      
            
            if($request->getAttribute('id')) {
                /** @var \App\Model\Users $userModel */
                $userModel = $this->container->get(\App\Model\Users::class);
                $user = $userModel->findOne(['id' => $request->getAttribute('id')]);                      
                
                if(!$user) {
                    return new HtmlResponse($this->template->render('error::404'), $status=404);                         
                }                                
            
                /** @var \App\Model\Sites $sitesModel */
                $sitesModel = $this->container->get(\App\Model\Sites::class);               
                $sites = $sitesModel->findByUserId($user->getId())->toArray();
                $listAllSites = $sitesModel->findAll(null, 'domain ASC')->toArray();
                
                foreach($listAllSites as $i => $requestSite) {
                    $is_user_site = false;
                    foreach($sites as $site) {
                        if($requestSite['id'] == $site['id']) {
                            $is_user_site = true;
                            break;
                        }
                    }
                    if($is_user_site) {
                        unset($listAllSites[$i]);
                    }
                }
                
                /** @var \App\Model\Feeds $feedsModel */
                $feedsModel = $this->container->get(\App\Model\Feeds\Feeds::class);
                $feeds = $feedsModel->findAll(['clientid' => $user->getId()])->toArray();
                
            }          
            
            /** @var \App\Model\UserCategory $categoryModel */
            $categoryModel = $this->container->get(\App\Model\Categories::class);                        
            $categories = array();           
            foreach($categoryModel->findAll() as $category) {
                $categories[$category->getId()] = $category->getName();
            }           
            
            /** @var \App\Model\Countries $countryModel */
            $countryModel = $this->container->get(\App\Model\Countries::class);
            $countries = array();
            foreach($countryModel->findAll() as $country) {
                $countries[$country->getCode()] = $country->getName();
            }            
            
            /** @var \App\Model\Currencies $currencyModel */
            $currencyModel = $this->container->get(\App\Model\Currencies::class);
            $currencies = array();
            
            foreach($currencyModel->findAll() as $currency) {
                $currencies[$currency->getCode()] = $currency->getName();
            }
            
            $supportModel = $this->container->get(\App\Model\Supports::class);
            $_managers = $supportModel->findAll([
                'role_id' => array(\App\Entity\Support::ROLE_MANAGER, \App\Entity\Support::ROLE_ADMIN)
            ], [
                'email' => 'asc'
            ]);
            
            $managers = array();
            foreach($_managers as $manager) {
                if ($manager->getId() !== 1) // Не выводим менеджеров-админов
                {
                    $managers[$manager->getId()] = $manager->getFullname();
                }
            }
            
            
            /** @var \App\Model\Providers $providerModel */
            $providerModel = $this->container->get(\App\Model\Providers::class);
            $providersList = $providerModel->findAll(null, 'name ASC')->toArray();
            
            $userId = $request->getAttribute('id');
            if($userId) {
                
                $userCodeProviderId = json_decode($user->getCodeProvider(), true);
                if ($userCodeProviderId) {
                    $codeProvider = $providerModel->findAll(['id' => $userCodeProviderId])->toArray();
                } else {
                    $codeProvider = null;  
                }
            }
            
            return new HtmlResponse($this->template->render('adm::users/user', [
                'lang' => $request->getAttribute('layoutInfo')->getLang(),
                'page' => $request->getAttribute('page'),
                'user' => $user,
                'sites' => $sites,
                'feeds' => $feeds,
                'segments' => $this->container->get('config')['users_segment'],
                'categories' => $categories,
                'countries' => $countries,
                'managers' => $managers,
                'currencies' => $currencies,
                'listAllSites' => $listAllSites,
                'accessSites' => $listAccessSites,
                'codeProvider' => $codeProvider,
                'providersList' => $providersList,
            ]));                        
        
        } elseif($request->getAttribute('page') == 'statistic') {                            
            
            try {

                /** @var \App\Model\Users $userModel */
                $userModel = $this->container->get(\App\Model\Users::class);
                $userId = $request->getAttribute('id');
                $user = $userModel->findOne(['id' => $request->getAttribute('id')]);

                /** @var \App\Model\Sites $sitesModel */
                $sitesModel = $this->container->get(\App\Model\Sites::class);
                $listSites = $sitesModel->findByUserId($user->getId())->buffer();
                
                $listSites->rewind();
                
                $allDomains = $listDomains;
                
                $subdomains = true;
                $allStat = false;
                $group = false;

                /** @var \App\Model\Statistics $stats */
                $stats = $this->container->get(\App\Model\Statistics::class);

                $summaryReport = $stats->getSummuryReport($allDomains, $user->getCurrencyCode(), $subdomains, $allStat, $group);
                list($periodStats, $periodDates) = $stats->getPeriodReport(date('Y-m'), $allDomains, $user->getCurrencyCode(), $subdomains, $allStat, $group);

                $dateIterval = new \DateInterval('P1M');                
                $dateStart = \DateTime::createFromFormat('Y-m-d', $periodDates['min'] ?: date('Y-m-01'));
                $dateEnd = \DateTime::createFromFormat('Y-m-d', $periodDates['max'] ?: date('Y-m-01'));              

                $dateRange = array();
                if($dateStart != $dateEnd) {
                    $_dateRange = new \DatePeriod($dateStart, $dateIterval, $dateEnd);                         
                    foreach($_dateRange as $dt) {
                        $dateRange[] = $dt;
                    }         
                }
                $dateRange[] = $dateEnd;
                $dateRange = array_reverse($dateRange);

                $currencyModel = $this->container->get(\App\Model\Currencies::class);
                $currencies = array();
                foreach($currencyModel->findAll() as $currency) {
                    $currencies[$currency->getCode()] = $currency->getName();
                }
                
                $currentCurencyCode = $user->getCurrencyCode();

                
                $data['adminStats'] = true;
                $data['sites'] = $listSites;
                $data['accessSites'] = $listAccessSites;
                $data['summaryStats'] = $summaryStats;
                $data['periodStats'] = $periodStats;
                $data['summaryReport'] = $summaryReport;
                $data['listDomains'] = $listDomains;
                $data['dateRange'] = $dateRange;        
                $data['currencies'] = $currencies;
                $data['currency'] = ['code' => $currentCurencyCode, 'name' => $currencies[$currentCurencyCode]];
                $data['userId'] = $userId;
                

            } catch(\Exception $e) {
                $data['error'] = _t('Извините, статистика временно не работает');
            }
            
            $data = array_merge($data, [
                'lang' => $request->getAttribute('layoutInfo')->getLang(),
                'layoutInfo' => $request->getAttribute('layoutInfo'),
            ]);
            
            return new HtmlResponse($this->template->render('app::user/cabinet', $data));
            
        } else {
            $queryParams = $request->getQueryParams();
            
            /** @var \App\Model\Users $usersModel */
            $usersModel = $this->container->get(\App\Model\Users::class);
            
            /** @var AggregateAuth $aggregateAuth */
            $aggregateAuth = $this->container->get(AggregateAuth::class);
            $role = $aggregateAuth->getIdentity()->getRoleId();

            if (in_array($role, ['admin'])) {
                // Список пользователей для админа
                $users = $usersModel->findAll(['type' => 'shop']);
            } else if ($role == 'manager') {
                // Список пользователей менеджера
                $manager_id = $aggregateAuth->getIdentity()->getId();
                $manager_users = $usersModel->findBySupportId($manager_id);
                $users = $usersModel->findAll(['id' => $manager_users]);
            } else {
                $users = $usersModel->findAll(['type' => 'shop']);
            }
            
            return new HtmlResponse($this->template->render('adm::users/list', [
                'lang' => $request->getAttribute('layoutInfo')->getLang(),
                'users' => $users,  
            ]));
        }
    }

    /**
     * @param $id
     * @param $status
     * @param $value
     * @return JsonResponse
     */
    private function updateStatus($id, $status, $value)
    {
        /** @var \App\Model\Users $usersModel */
        $usersModel = $this->container->get(\App\Model\Users::class);
        $user = $usersModel->findById($id);

        $data = [];
        if ($user) {
            $hydrator = $this->container->get(DelegatingHydrator::class);
            $user = $hydrator->hydrate([$status => $value], $user);

            try {
                $usersModel->save($user);
                $data['result'] = true;
            } catch (\Exception $e) {
                $data['result'] = false;
                $data['msg'] = $e->getMessage();
            }
        } else {
            $data = [
                'result' => false,
                'msg' => 'User not found',
            ];
        }

        $response = new JsonResponse($data);

        return $response;
    }
    
    private function createUser(ServerRequestInterface $request)
    {
        /** @var \App\Model\Users $userModel */
        $userModel = $this->container->get(\App\Model\Users::class);      
        
        /** @var \App\Model\Sites $siteModel */
        $siteModel = $this->container->get(\App\Model\Sites::class);
        
        /** @var \App\Model\Categories $categoriesModel */
        $categoriesModel = $this->container->get(\App\Model\Categories::class);       
        
        /** @var DelegatingHydrator $hydrator */
        $hydrator = $this->container->get(DelegatingHydrator::class);
        
        $validatorUser = new \App\Validator\User($hydrator);
        $validatorSite = new \App\Validator\Site($hydrator);
        
        $body = $request->getParsedBody();
        
        $data_user = [
            'email' => trim($body['email']),
            'fullname' => trim($body['fullname']),
            'phone' => trim($body['phone']),
            'password' => $userModel->hashPassword(trim($body['password'])),            
            'comment' => trim($body['comment']),
            'date_create' => trim($body['date_create']) ?: date('Y-m-d'),
            'segment' => trim($body['segment']),
            'country_code' => trim($body['country']),
            'currency_code' => trim($body['currency']),
            'traffic' => (int) $body['traffic'],
            'activated' => 1,
            'website' => '',
            'support_id' => (int) $body['manager'],
            'code_provider' => trim($body['code_provider']),
        ];               
        
        if($data_user['currency_code'] == '') {
            throw new \Exception(_t('Input user currency'));
        }
        
        
        $user = $userModel->findOne(['email' => $data_user['email']]);                
        if($user) {
            throw new \Exception(sprintf(_t('User with email &lt;%s&gt; already exists'), $data_user['email']));
        }
        
        $user = $hydrator->hydrate($data_user, new User());
        
        
        
        $categories = array();
        if($body['category']) {
            foreach($body['category'] as $categoryId) {
                $cat = $categoriesModel->findById((int)$categoryId);                
                if($cat) {
                   $categories[] = $cat;
                }
            }
        }
        $user->setCategories($categories);        
        
        
        if(!$validatorUser->isValid($user)) {
            throw new \Exception($validatorUser->getMessages()[0]);
        }
        

        $data_sites = $body['sites'];
        if(mb_strlen($data_sites) != 0) {
            $data_sites_arr = explode(',', $data_sites);
            $data_sites_arr = array_unique(array_map('trim', $data_user));
            
            $sites = array();
            foreach(explode(',', $data_sites) as $domain) {                
                $data_site = array(
                    'domain' => trim($domain),
                );
                $site = $hydrator->hydrate($data_site, new Site());                                               
                
                if(!$validatorSite->isValid($site)) {
                    throw new \Exception($validatorSite->getMessages()[0]);
                }
                
                $sites[] = $site;
            }
        }     

        $userId = $userModel->save($user);
       
        $response = new JsonResponse(array(
            'result' => true,
            'redirect' => $this->router->generateUri('adm.users.edit', ['lang' => $request->getAttribute('layoutInfo')->getLang(), 'id' => $userId])
        ));
        return $response;
    }
    
    private function editUser(ServerRequestInterface $request)
    {
        $userId = $request->getAttribute('id');
        
        /** @var DelegatingHydrator $hydrator */
        $hydrator = $this->container->get(DelegatingHydrator::class);
        
        /** @var \App\Model\Users $userModel */
        $userModel = $this->container->get(\App\Model\Users::class);               
        
        /** @var \App\Entity\User $user */
        $user = $userModel->findOne(['id' => $userId]);
        if(!$user) {
            throw new \Exception(_t('User not found'));
        }        
        
        /** @var \App\Model\Categories $categoriesModel */
        $categoriesModel = $this->container->get(\App\Model\Categories::class);       
        
        $sitesModel = $this->container->get(\App\Model\Sites::class);
        $sites = $sitesModel->findByUserId($userId);
        $sites->buffer();       
        
        $data = $request->getParsedBody();        
        if(($existedUser = $userModel->findOne(['email' => $data['email']])) && $existedUser->getId() != $userId) {
            throw new \Exception(sprintf(_t('User with email &lt;%s&gt; already exists'), $data['email']));
        }
        
        $user->setDateCreate(trim($data['date_create'] ?: date('Y-m-d')));
        $user->setEmail(trim($data['email']));
        $user->setFullname(trim($data['fullname']));
        $user->setPhone(trim($data['phone']));
        $user->setComment(trim($data['comment']));    
        $user->setSegment(trim($data['segment']));
        $user->setCountryCode(trim($data['country']));
        $user->setCurrencyCode(trim($data['currency']));
        $user->setTraffic((int)$data['traffic']);   
        $user->setSupportId((int)$data['manager']);
        $user->setCodeProvider(trim($data['code_provider']));    
        
        if(trim($data['password'])) {
            $user->setPassword($userModel->hashPassword(trim($data['password'])));                      
        }        
        
        $categories = array();
        if($data['category']) {
            foreach($data['category'] as $categoryId) {
                $cat = $categoriesModel->findById((int)$categoryId);                
                if($cat) {
                   $categories[] = $cat;
                }
            }
        }
        $user->setCategories($categories);               

        $userValidator = new \App\Validator\User($hydrator);
        if(!$userValidator->isValid($user)) {
            throw new Exception($userValidator->getMessages()[0]);
        }        
        
        // Save
        $userModel->save($user);        
        
        $response = new JsonResponse(array(
            'result' => true,
            'redirect' => $this->router->generateUri('adm.users.edit', ['lang' => $request->getAttribute('layoutInfo')->getLang(), 'id' => $userId])
        ));
        return $response;        
    }
    
    private function checkSite(ServerRequestInterface $request)
    {
        $userId = $request->getAttribute('id');
        $data = $request->getParsedBody();
        
        $sitesModel = $this->container->get(\App\Model\Sites::class);
        $sites = $sitesModel->findByDomain($data['domain']);
        
        $isUnique = true;
        if($sites) {
            foreach($sites as $site) {
                if($site->getUsersId() != $userId) {
                    $isUnique = false;
                    break;
                }
            }
        }
        
        $response = new JsonResponse(array(
            'isUnique' => $isUnique,
        ));
        return $response;        
    }
}
