<?php
/**
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link      http://cakephp.org CakePHP(tm) Project
 * @since     0.2.9
 * @license   http://www.opensource.org/licenses/mit-license.php MIT License
 */
namespace App\Controller;

use Cake\Core\Configure;
use Cake\Network\Exception\NotFoundException;
use Cake\View\Exception\MissingTemplateException;
use Cake\ORM\TableRegistry;

/**
 * Static content controller
 *
 * This controller will render views from Template/Pages/
 *
 * @link http://book.cakephp.org/3.0/en/controllers/pages-controller.html
 */
class PagesController extends AppController
{

    /**
     *
     * @param
     *            $user
     * @return boolean Give authorization for materiels
     */
    /*
     * // (EP) TODO: ameliorer ca avec des variables globales IS_VALIDATED, IS_ADMIN, ...
     * public function isAuthorized($user) {
     * $path = func_get_args();
     * if($path[0] === null) {
     * $path[0] = '';
     * }
     * $page = $subpage = null;
     * if (!empty($path[0])) {
     * $page = $path[0];
     * }
     * if (!empty($path[1])) {
     * $subpage = $path[1];
     * }
     * if ($page == 'tools') {
     * // Autoriser seulement à partir du role ADMIN et +
     * if ($this->userHasRoleAtLeast('Utilisateur')) {
     * return false;
     * }
     * }
     * return true;
     * }
     */

    
    /*
     * Toute première méthode appelée,
     *
     * AVANT authentification
     *
     */
    public function initialize()
    {
        $this->myDebug("step 0A (specific): PagesController.initialize()");
        parent::initialize();
        // On autorise l'action add SANS authentification (unauthenticated)
        //$this->Auth->allow(['add']);
        //$this->LdapAuth->allow(['display']);
    }
    
    // (20200424 EP)
    public function beforeFilter(\Cake\Event\Event $event) {
        $this->myDebug("step 1A (specific): PagesController.beforeFilter()");
        // On donne d'abord les autorisations par défaut de AppController
        parent::beforeFilter($event);
        
        // Puis on ajoute les autorisations spécifiques
        /*
        $path = func_get_args();
        pr($path);
        $page = null;
        if (! empty($path[0])) $page = $path[0];
        */
        
        $pages_authorized_without_connexion = ['about', 'acls', 'changes', 'stats', 'notifications'];
        $this->action = $this->getActionPassed();
        if ($this->action == "display") {
            $this->page = $this->request->getParam('pass.0');
            
            // On autorise l'action display SANS connexion 
            // mais seulement pour la page 'about' 
            //if (in_array($path[0], ['about', 'tools'])) echo "yes"; else echo "no";
            //if ( $this->page == 'about' ) $this->LdapAuth->allow(['display']);
            if ( in_array($this->page, $pages_authorized_without_connexion) ) $this->LdapAuth->allow(['display']);
        }
        
    }
    
    /*
     * @Override
     *
     * Initialisation des autorisations pour les actions spécifiques à ce controleur
     *
     */
    protected function setAuthorizations() {

        // On supprime les autres actions par défaut (add, view, index, find)
        //foreach (array_keys($this->is_authorized_action[]) as $a) if ($a != 'display') unset($this->is_authorized_action[$a]);
        $this->is_authorized_action = [];
        
        // Action display 'about' (affichage de la page 'A propos')
        $this->setAuthorizationsForAction('display/about', 0);

        // Pages autorisées à tout le monde SANS connexion
        // - Autorisations
        $this->setAuthorizationsForAction('display/acls', 0);
        // - Nouveautés
        $this->setAuthorizationsForAction('display/changes', 0);
        // - Stats
        $this->setAuthorizationsForAction('display/stats', 0);
        // - Notifications
        $this->setAuthorizationsForAction('display/notifications', 0);
        
        // Action display 'home' (affichage de la page 'Accueil')
        $this->setAuthorizationsForAction('display/home', 0);

        // Action display 'tools' (affichage de la page 'menu Outils')
        // admin (+) only
        $this->setAuthorizationsForAction('display/tools', -1, ['admin'=>0, 'super'=>0]);
        $this->setAuthorizationsForAction('display/tools-sm', -1, ['admin'=>0, 'super'=>0]);
        //$this->setAuthorizationsForAction('display/tools', 0);
        //$this->setAuthorizationsForAction('display/tools-sm', 0);
        
        $this->setAuthorizationsForAction('display/printers', 0);
        
        // Superadmin only :
        // - Action display 'infos' (affichage de la page 'Informations techniques sur le serveur')
        $this->setAuthorizationsForAction('display/infos', -1, ['super'=>0]);
        // - Action display 'logs' (affichage des messages de log')
        $this->setAuthorizationsForAction('display/logs', -1, ['super'=>0]);
        
    }
    
    
    /*
    public function isAuthorized($user,
        $action=null, $id=null, $role=null, $userCname=null) {
        $this->myDebug("step 2A (specific): PagesController.isAuthorized(user)");
        $this->myDebug("- user is:", $user);
        
        if ($this->action == 'display') {
            
            //$page = $this->request->getParam('pass.0');
            
            // Action display SANS nom de page => on redirige sur '/'
            // (c'est à dire sur /pages/home, c'est à dire /pages/display/home)
            // cf config/routes.php qui définit l'action "display" par défaut pour tout ce qui commence par pages/
            if (! $this->page) {
                $this->redirect('/');
                return false;
            }
            
            switch ($this->page) {
                
                // Page d'accueil autorisée à tous les profils (roles)
                case 'home': return true;
                
                // Page 'tools' seulement à partir de ADMIN
                // if (! $this->userHasRoleAtLeast('Administration')) {
                case 'tools': return $this->USER_IS_ADMIN_AT_LEAST();
                //case 'tools': return $this->USER_IS_USER();
                
                // Page 'infos' seulement pour SUPERADMIN
                //if (! $this->USER_IS_SUPERADMIN()) return $this->redirect('/');

                case 'infos': return $this->USER_IS_SUPERADMIN();
            
            }
            
        } // display

        // sinon, règle par défaut donnée par AppController
        return parent::isAuthorized($user);
    }
    */
    
    
    /**
     * Displays a view
     *
     * @return void|\Cake\Network\Response
     * @throws \Cake\Network\Exception\NotFoundException When the view file could not
     *         be found or \Cake\View\Exception\MissingTemplateException in debug mode.
     */
    public function display()
    {
        // (EP 21/5/19) 
        // On ne voit ce message que sur la page "about"
        // En effet, les autres pages nécessitant un login, 
        // on est redirigé sur la page login et donc la methode login() du controleur UsersController
        // Du coup, il faut décommenter la ligne "exit" juste après pour voir ce message (évite la redirection).
        $this->myDebug("step 2: PagesController.display()");
        
        $configuration = $this->confLabinvent;

        // Action display SANS nom de page => on redirige sur '/' 
        // (c'est à dire sur /pages/home, c'est à dire /pages/display/home)
        // cf config/routes.php qui définit l'action "display" par défaut pour tout ce qui commence par pages/
        ///$page = $this->request->getParam('pass.0');
        /*
        if (! $page) $this->redirect('/');
        */
        $subpage = $this->request->getParam('pass.1');
        //if (!$subpage) $subpage = null;
        /*        
        $path = func_get_args();
        if (! count($path)) return $this->redirect('/');
        if ($path[0] === null) $path[0] = '';
        $this->myDebug("- path is:");
        $this->myDebug($path);
        $page = $subpage = null;
        if (! empty($path[0])) $page = $path[0];
        if (! empty($path[1])) $subpage = $path[1];
        */
        $this->myDebug("- page is:");
        $this->myDebug($this->page);
        $this->myDebug("- subpage is:");
        $this->myDebug($subpage);
        
        /*
        // @todo : faire plus proprement, dans isAuthorized()
        // Si l'utilisateur n'est pas connecté, on le redirige vers la page login.ctp
        // sauf si l'action demandée est 'about' ou si le mode install est activé
        //if (in_array($path[0], ['about', 'tools'])) echo "yes"; else echo "no";
        if ( $page != 'about' ) {
        // ! in_array($page, ['about', 'tools'])
            if ( 
                // pas logged (pas authentifié)
                ! $this->LdapAuth->user($configuration->ldap_authenticationType)[0]
                //&& $path[0]!='about' 
                //&& ! in_array($path[0], ['about', 'tools'])
                // et pas mode install 
                && ! $configuration->mode_install 
            ) {
                return $this->redirect([
                    'controller' => 'users',
                    'action' => 'login'
                ]);
            }
        }
        */
        
        /*
        // @todo : faire plus proprement, avec isAuthorized()
        // Page 'tools' seulement à partir de ADMIN
        if ($page == 'tools') {
            // if (! $this->userHasRoleAtLeast('Administration')) {
            if (! $this->USER_IS_ADMIN_AT_LEAST()) return false;
                //return $this->redirect('/');
        }
        // Page 'infos' seulement pour SUPERADMIN
        if ($page == 'infos') {
            //if (! $this->USER_IS_SUPERADMIN()) return $this->redirect('/');
            if (! $this->USER_IS_SUPERADMIN()) return false;
        }
        */

        // Finalement, on affiche la $page demandée (avec render())
        $this->set('page', $this->page);
        $this->set(compact('subpage'));

        // - Page d'ACCUEIL
        if ($this->page=='home') {
            $this->set('HAS_ORDER_BUTTON',self::hasOrderButton());
        }
        
        // - Page des LOGS
        // /pages/logs?level=info
        if ($this->page=='logs') {
            $info_levels = ['info','debug','notice'];
            $error_levels = ['warning', 'error', 'critical', 'alert', 'emergency'];
            // Paar défaut, level = INFO
            $level = 'Info';            
            $level_arg = $this->request->getQuery('level');
            if ($level_arg!==null && in_array($level_arg, array_merge($info_levels,$error_levels)))
                $level=$level_arg;
            //debug($level);
            $this->set(compact('level', 'info_levels', 'error_levels'));
        }

        // - Page des AUTORISATIONS et NOTIFICATIONS
        if ($this->page=='acls' || $this->page=='notifications') {
            $lab_name = $this->confLabinvent->labNameShort;
            if (!$lab_name) $lab_name = 'NOM_DU_LABO';
            $this->set(compact('lab_name'));
            $this->set(compact('configuration'));
        }

        
        // - Page des STATISTIQUES
        if ($this->page=='stats') $this->page_stats_set_variables(); 

        
        // - Page des OUTILS
        if ($this->page=='tools') {
            // Mode 'SA a tous les droits'
            $no_limit_mode = $this->confLabinvent->mode_nolimit;
            $this->set(compact('no_limit_mode'));
        }
        
        
        //debug(implode('/', $path));
        //debug(implode('/', array($page,$subpage)));
        try {
            //$this->render(implode('/', $path));
            //$this->render("$page/$subpage");
            $this->render($subpage ? $this->page.'/'.$subpage : $this->page);
            //$this->render(implode('/', array($page,$subpage)));
        } catch (MissingTemplateException $e) {
            if (Configure::read('debug')) throw $e;
            throw new NotFoundException();
        }
        
    } // display()

    
    
    private function page_stats_set_variables() {
        
        // Nb années demandées par l'utilisateur
        $nbyears = $this->request->getQuery('nbyears');

        // Initialisation des variables nécessaires
        // - Données Totales (complètes) (tronquées à $nbyears)
        $tot = [
            'CREATED' => (int) 1,
            'VALIDATED' => (int) 0,
            'TOBEARCHIVED' => (int) 0,
            'ARCHIVED' => (int) 0,
            'suivis' => (int) 0,
            'prets' => (int) 0
        ];
        // - Donées Moyennes
        $avg = [
            'CREATED' => (int) 1,
            'VALIDATED' => (int) 0,
            'TOBEARCHIVED' => (int) 0,
            'ARCHIVED' => (int) 0,
            'suivis' => (int) 0,
            'prets' => (int) 0
        ];
        // - Année en cours
        $now = new \DateTime('now');
        //debug($now);
        // 2020 en 2020
        $current_year = (int) $now->format('Y');
        // - Données par Année
        $years = 
        [
            $current_year => [
                'CREATED' => (int) 1,
                'VALIDATED' => (int) 0,
                'TOBEARCHIVED' => (int) 0,
                'ARCHIVED' => (int) 0,
                'suivis' => (int) 0,
                'prets' => (int) 0
            ]
        ];
        
        // Tables
        $materiels = TableRegistry::getTableLocator()->get('Materiels');
        $suivis = TableRegistry::getTableLocator()->get('Suivis');
        $prets = TableRegistry::getTableLocator()->get('Emprunts');
        $associated_entities = ['suivis','prets'];
        $fk = 'materiel_id';

        // Tous les matos qui ont une date d'achat non nulle
        $all_matos = $materiels->find()->where(['year(date_acquisition) >' => 0]);
        
        // On ne fait pas de stats s'il n'y a aucun matériel dans la BD
        if ($all_matos->count() == 0) {
            $year_min = $current_year;
            //$nbyears = 0;
            $this->set(compact('current_year', 'year_min', 'nbyears', 'tot', 'avg', 'years'));
            return;
        }

        // - Année min
        //$year_min = $materiels->find()->min(['year(date_acquisition)']);
        //$matos_year_min = $materiels->find()->where(['year(date_acquisition) >' => 0])
        $matos_year_min = $all_matos->cleanCopy()
        ->min(function($matos) {
            return $matos->date_acquisition->format('Y');
        });
        $year_min = (int) $matos_year_min->date_acquisition->format('Y');
        
        // - Nb années au total : au moins 1
        $nbyears_max = $current_year - $year_min + 1;
        
        // $nbyears demandé par l'utilisateur, par défaut TOUTES
        $nbyears = ( is_numeric($nbyears) && $nbyears>0 ) ? round($nbyears) : $nbyears_max;
        //debug($nbyears);
        
        // - Nb annnées à traiter (entre 1 et $nbyears_max)
        // ($nbyears=null si pas passé en paramètre)
        // ($nbyears='' si passé en paramètre mais sans valeur '?nbyears=')
        //$nbyears = $nbyears ? abs((int)$nbyears) : $nbyears_max;
        //debug($nbyears);
        $nbyears = min($nbyears,$nbyears_max);
        //debug($nbyears);
        //debug($nbyears);
        // Si on est au max (avec max>2), on enlève la 1ère et la dernière années (car a priori incomplètes)
        //sif ($nbyears==$nbyears_max && $nbyears>2) $nbyears -= 2;
        // Nouvelle année min
        $year_min = $current_year-$nbyears + 1;
        
        // Nouveau $all_matos, en tronquant à $nbyears
        $all_matos = $materiels->find()
            ->where(['year(date_acquisition) <=' => $current_year])
            ->where(['year(date_acquisition) >=' => $year_min]);
        $all_matos_ids = $all_matos->cleanCopy()->select(['id']);
            
        /*
         $statuses = [
         'CREATED' => 'created',
         'VALIDATED' => 'date_validated',
         'ARCHIVED' => 'date_archived'
         ];
         */
        $statuses = [ 'CREATED', 'VALIDATED', 'TOBEARCHIVED', 'ARCHIVED' ];
        
        // - tot : Données Totales (complètes) (tronquées à $nbyears)
        foreach ($statuses as $status) {
            $all_matos_copy = $all_matos->cleanCopy();
            // Tous les matos qui ont une date d'achat non nulle
            if ($status != 'CREATED') $all_matos_copy->where(['status' => $status]);
            $tot[$status] = $all_matos_copy->count();
        }
        // Suivis et Prets
        // Nb materiels qui ont des suivis (i.e. qui ont au moins 1 suivi)
        foreach ($associated_entities as $e) {
            //$nb_matos_suivis = $suivis->find()
            $tot[$e] = $$e->find()
            // materiel_id not null
            ->where(["$fk >" => 0])
            ->select([$fk])
            ->distinct([$fk])
            ->where(["$fk IN" => $all_matos_ids])
            ->count();
        }

        /*
        // - tot2 : Données Totales Partielles (= tot sauf 1ère et dernière années)
        $all_matos2 = $materiels
            ->find()
            ->where(['year(date_acquisition) <' => $current_year])
            ->where(['year(date_acquisition) >' => $current_year-$nbyears]);
        $all_matos_ids2 = $all_matos2->cleanCopy()->select(['id']);
            
        $tot2 = [];
        foreach ($statuses as $status) {
            $all_matos_copy = $all_matos2->cleanCopy();
            // Tous les matos qui ont une date d'achat non nulle
            if ($status != 'CREATED') $all_matos_copy->where(['status' => $status]);
            $tot2[$status] = $all_matos_copy->count();
        }
        // Suivis et Prets
        // Nb materiels qui sont suivis (i.e. qui ont au moins 1 suivi)
        foreach ($associated_entities as $e) {
            //$nb_matos_suivis = $suivis->find()
            $tot2[$e] = $$e->find()
            // materiel_id not null
            ->where(["$fk >" => 0])
            ->select([$fk])
            ->distinct([$fk])
            ->where(["$fk IN" => $all_matos_ids2])
            ->count();
        }
        */
        
        // - Donées Moyennes (avg)
        /*
         $avg['CREATED'] = 10;
         $avg['VALIDATED'] = 7;
         $avg['TOBEARCHIVED'] = 1;
         $avg['ARCHIVED'] = 1;
         */
        foreach ($statuses as $status) {
            $avg[$status] = round($tot[$status]/$nbyears, 1);
            /* 
             * (EP)
             * J'obtiens pas le meme resultat avec cette methode...
             * Peut-être que ca compte pas les années où il n'y a rien (0)
             */
            /*
            //$all_matos = $materiels->find();
            //$all_matos = $materiels->find()->where(['year(date_acquisition) >' => 0]);
            //->groupBy('year(date_acquisition)');
            $all_matos_copy = $all_matos->cleanCopy();
            if ($status != 'CREATED') $all_matos_copy->where(['status' => $status]);
            $avg[$status] = $all_matos_copy
                //->groupBy('year(date_acquisition)');
                ->countBy(function($matos) {
                    return $matos->date_acquisition->format('Y');
                })
                ->avg();
                //->toList();
                //->toArray();
                //->count();
                $avg[$status] = round($avg[$status]);
            */
        }
        // Suivis et Prets
        // Nb materiels qui sont suivis (i.e. qui ont au moins 1 suivi)
        foreach ($associated_entities as $e) {
            $avg[$e] = round($tot[$e]/$nbyears, 1);
            /*
            $nb_matos_having_entity = $$e->find()
                // materiel_id not null
                ->where(["$fk >" => 0])
                ->select([
                    'year' => 'YEAR(created)'
                ])
                ->group('year')
                ->toArray();
                
            //->groupBy(function($entity) {
            ->group(function($entity) {
                return $entity->created->format('Y');
            })
            ->distinct([$fk])
            ->count()
            ->avg();
            $avg[$e] = $nb_matos_having_entity;
            */
            //debug($nb_matos_having_entity);
        }
        
        
        // - Données par Année
        $all_matos = $materiels->find();
        for ($y=$current_year ; $y>=$year_min ; $y--) {
            ////debug($y);
            $all_matos_for_year = $all_matos->cleanCopy()->where(['year(date_acquisition)' => $y]);
            ////debug($all_matos_for_year->count());
            // - statut par année
            try {
                foreach ($statuses as $status) {
                    /*
                    $all_matos_for_year = $materiels->find();
                        ->where(['year(date_acquisition)' => $y]);
                    $all_matos_for_year = $all_matos->cleanCopy()->where(['year(date_acquisition)' => $y]);
                    */
                    $all_matos_for_year_and_status = $all_matos_for_year->cleanCopy();
                    if ($status != 'CREATED') $all_matos_for_year_and_status->where(['status' => $status]);
                    $years[$y][$status] = $all_matos_for_year_and_status->count();
                }
            } catch (\PDOException $e) {
                debug("Mauvais format de requete SQL (PagesController/stats statuses), Exception PDO générée !");
                exit;
            }
            ////debug($years);
            // Suivis et Prets, par année
            $all_matos_suivis_for_year = $all_matos_for_year->cleanCopy();
            foreach ($associated_entities as $e)
                try {
                    ////debug($e);
                    //debug($$e->find()->count());
                    /* 
                     * SQL equivalent : à tester...
                     * 
                     * SELECT COUNT(DISTINCT(suivis.materiel_id)) FROM suivis LEFT JOIN materiels
                     *      ON suivis.materiel_id = materiels.id
                     *      WHERE YEAR(materiels.date_acquisition) = $y
                     *      
                     * $years[$y]['suivis'] = $suivis->find()...
                     * puis
                     * $years[$y]['prets'] = $prets->find()...
                     */
                    $years[$y][$e] = $$e
                        // Tous les suivis/emprunts...
                        ->find()
                        // ... et leur materiel associé...
                        ->leftJoinWith('Materiels')
                        // ... LEFT JOIN ON (suivis.materiel_id = materiels.id) ... // ou emprunts.materiel_id
                        //->enableAutoFields(true)
                        // ... (uniquement les materiels de l'année $y) ...
                        ->where(['year(date_acquisition)' => $y])
                        // ... en ne gardant qu'1 seul suivi/emprunt par materiel (meme si ce materiel en a plusieurs) ...
                        ->distinct(['materiel_id'])
                        // ... et enfin, on compte le nombre de suivis/emprunts (total pour l'année $y)
                        ->count();
                        //->toArray();
                        //$all_matos_for_year
                        //->contain(['Suivis'])
                        //->select(['id', 'count_suivis => count(suivis)'])
                        //->where(['count_suivis >' => 0])
                        //->count();
                } catch (\PDOException $ex) {
                    debug("Mauvais format de requete SQL (PagesController/stats suivis/prets), Exception PDO générée !");
                    debug($ex);
                    exit;
                }
            //debug($years[$y]['suivis']);
            //$years[$y]['suivis'] = 3;
            //$years[$y]['prets'] = 4;
        } // foreach year
        //debug($years);
            
        // Set all variables pour la vue
        //$this->set(compact('current_year', 'year_min', 'tot', 'avg', 'years', 'suivis', 'prets'));
        //$this->set(compact('current_year', 'year_min', 'nbyears', 'tot', 'tot2', 'avg', 'years'));
        $this->set(compact('current_year', 'year_min', 'nbyears', 'tot', 'avg', 'years'));
        /*
        debug($tot);
        //echo json_encode($avg);
        debug($avg);
        debug($years);
        */
        
    } // set_variables_for_page_stats()
    
}