<?php

namespace Cadoles\CronBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LockableTrait;

use Cadoles\CronBundle\Entity\Cron;

class CronCommand extends ContainerAwareCommand
{
    private $container;
    private $em;
    private $output;
    private $filesystem;
    private $rootlog;


    use LockableTrait;

    protected function configure()
    {
        $this
            ->setName('Cron:Exec')
            ->setDescription('Execution of the cron command')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->container    = $this->getApplication()->getKernel()->getContainer();
        $this->em           = $this->container->get('doctrine')->getEntityManager();
        $this->output       = $output;
        $this->filesystem   = new Filesystem();
        $this->rootlog      = $this->getContainer()->get('kernel')->getRootDir()."/../var/logs/";

        $cron_activate = $this->getContainer()->getParameter('cron_activate');
        if(!$cron_activate)
        {
            $this->writelnred('CRON désactivé');
            return false;
        }

        if (!$this->lock()) {
            $this->output->writeln("CRON LOCK");
            return 0;
        }

        $crons = $this->em->getRepository('CadolesCronBundle:Cron')->toexec();
        $i=0;
        
        if($crons) {
            $now=new \DateTime();
            $this->writelnred('');
            $this->writelnred('');
            $this->writelnred('');
            $this->writelnred('');
            $this->writelnred('==========================================================================================================');
            $this->writelnred('== CRON ==================================================================================================');
            $this->writelnred('==========================================================================================================');   
            $this->writeln   ('Date = '.$now->format('Y-m-d H:i:s'));   
        }

        // Cas particulier de la synchro
        // Dans la synchro il y a un clear du manager ce qui perturbe totalement le manager de Core:Exec
        // Il pert le lien avec la boucle sur crons
        // Alors si dans le cron il y a la synchro alors on n'execute que lui le reste sera executé lors du prochain passage
        $cronsynchro=$this->em->getRepository('CadolesCronBundle:Cron')->find(100);
        if($cronsynchro&&in_array($cronsynchro,$crons)) {
            $crons=[$cronsynchro];

        }
        
        foreach($crons as $cron) {
            $i++;
            
            // Id du cron
            $idcron = $cron->getId();

            // Flag d'execution en cours
            $now=new \DateTime();
            $cron->setStartexecdate($now);
            //$cron->setStatut(1);
            $this->em->persist($cron);
            $this->em->flush();

            // Récupération de la commande
            $command = $this->getApplication()->find($cron->getCommand());
            
            // Réccuépration des parametres
            $jsonparameter=json_decode($cron->getJsonargument(),true);

            // Parametre id du cron actuel
            $jsonparameter["cronid"]=$cron->getId();

            // Parametre si dernière execution
            $lastchance=false;
            if($cron->getRepeatcall()>0)
                $lastchance=($cron->getRepeatexec()+1==$cron->getRepeatcall());
            $jsonparameter["lastchance"]=$lastchance;

            // Formater la chaine de parametre
            $parameter = new ArrayInput($jsonparameter);

            // Executer la commande
            $returnCode=false;
            try{
                $returnCode = $command->run($parameter, $output);

                // Revenir sur le cron encours à cause du clear du manager présent dans la synchro
                // Sinon le manager se pomme et génère des nouveaux enregistrement plutot que mettre à jour celui en cours
                $cron=$this->em->getRepository('CadolesCronBundle:Cron')->find($idcron);
            }
            catch(\Exception $e) { 
                $this->writelnred("JOB EN ERREUR"); 
            }            

            // Flag de fin d'execution
            $now=new \DateTime();
            $cron->setEndexecdate($now);

            // Date prochaine execution
            if($cron->getrepeatinterval()>=0) {
                // Si interval par heure
                if(fmod($cron->getRepeatinterval(),3600)==0) 
                    $next=clone $cron->getNextexecdate();
                else
                    $next=new \DateTime();

                $next->add(new \DateInterval('PT'.$cron->getRepeatinterval().'S'));
                $cron->setNextexecdate($next);
            }
            
            // Statut OK/KO/Retry
            if($returnCode!="retry"||!$returnCode) {
                $cron->setStatut(2);
                if($returnCode!=1) {
                    $cron->setStatut(3);
                    if($cron->getRepeatcall()>0) $cron->setRepeatexec($cron->getRepeatexec()+1);

                    // Envoyer un mail à l'ensemble des administrateurs
                    $this->sendMailerror($cron);
                }
            }

            $this->em->persist($cron);
            $this->em->flush();
        }

        if($crons) {
            $this->writelnred("==");
            $this->writelnred("FIN CRON");
            $this->writelnred("==");
            $this->writelnred("");
        }
    }

    private function sendMailerror($cron) {
        $appname="";
        $config = $this->em->getRepository("CadolesCoreBundle:Config")->findOneBy(["id"=>"appname"]);
        if($config) $appname = $config->getValue();
        $noreply = $this->container->getParameter('noreply');

        // Email à l'ensemble administrateurs pour les prévenir qu'il y a une personne à valider
        $emailadmins= $this->em->createQueryBuilder()
                            ->select('table.email')
                            ->from("CadolesCoreBundle:User",'table')
                            ->where('table.role = :value')
                            ->setParameter("value", "ROLE_ADMIN")
                            ->getQuery()
                            ->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
        $to=array();
        $from =  $noreply;
        $fromName = $appname;                
        foreach($emailadmins as $emailadmin) {
            array_push($to,$emailadmin["email"]);
        }

        $mail_params=array(
            "subject"  => $appname." : ERREUR SUR EXECUTION JOB",
            "body_html"=> "ATTENTION UN JOB EST EN ERREUR. MERCI DE VERIFIER LES LOGS.<br>Command = ".$cron->getCommand()."<br>Description = ".$cron->getDescription(),
            "body_text"=> "ATTENTION UN JOB EST EN ERREUR. MERCI DE VERIFIER LES LOGS.\nCommand = ".$cron->getCommand()."\nDescription = ".$cron->getDescription()
        );                

        $message = $this->container->get('cadoles.core.service.mail');
        $message->sendEmail("template", $mail_params, $to, $from, $fromName);
    }

    private function writelnred($string) { 
        $this->output->writeln('<fg=red>'.$string.'</>');
        $this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
    }
    private function writeln($string) { 
        $this->output->writeln($string);
        $this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
    } 
}
