<?php
namespace App\Websocket;
 
use Exception;
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\EntityManagerInterface;
use SplObjectStorage;
use App\Service\MailService;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

use App\Entity\Message;

class MessageHandler implements MessageComponentInterface
{
    protected $container;
    protected $em;
    protected $mail;

    protected $clients;
    private $channels;
    private $channeltypes;
    private $channelkeys;
    private $userconns;
    private $userkeys;
    private $ispush;

    private $fgdebug;

    public function __construct(ContainerInterface $container, EntityManagerInterface $em, mailService $mail)
    {
        $this->container = $container;
        $this->em = $em;
        $this->mail = $mail;

        $this->clients          = new SplObjectStorage;
        $this->channels         = [];
        $this->channeltypes     = [];
        $this->channelkeys      = [];
        $this->userconns        = [];
        $this->userkeys         = [];
        $this->ispush           = [];

        $this->fgdebug          = ($this->container->getParameter("app_env")!="PROD");

    }
 
    public function onOpen(ConnectionInterface $conn)
    {
        $this->trace("== onOpen =============================");
        $this->clients->attach($conn);
        $this->userconns[$conn->resourceId] = $conn;
    }

    public function onClose(ConnectionInterface $conn)
    {
        $this->trace("== onClose ============================");
        $data= new \stdClass;
        $data->command = "adead";

        // Envoyer le message de deconnection que si la connection n'est issu d'un push
        if (isset($this->channels[$conn->resourceId])&&!$this->ispush[$conn->resourceId]) {
            $this->sendMessage($conn,$data,"other");
        }        
        
        // Détacher
        $this->clients->detach($conn);
        unset($this->userconns[$conn->resourceId]);
        unset($this->userkeys[$conn->resourceId]);
        unset($this->channels[$conn->resourceId]);
    }

    public function onError(ConnectionInterface $conn, Exception $e)
    {
        $this->trace("== onError ============================");
        $this->trace($e,"dump");
        $conn->close();
    }

    public function onMessage(ConnectionInterface $conn, $msg)
    {
        $this->trace("== onMessage ==========================");
        $data = json_decode($msg);

        switch ($data->command) {
            case "pushsubscribe":
                $this->channels[$conn->resourceId] = $data->channel;
                $this->channeltypes[$conn->resourceId] = $data->channeltype;
                $this->channelkeys[$conn->resourceId] = $data->channelkey;
                $this->userkeys[$conn->resourceId] = $data->userkey;
                $this->ispush[$conn->resourceId] = true;
                if (isset($this->channels[$conn->resourceId])) {
                    switch ($this->channeltypes[$conn->resourceId]) {
                        case "chat": $this->onMessageChat($conn,$data->subdata); break;
                        default: $this->sendMessage($conn,$data->subdata); break;
                    }
                }              
            break;

            case "subscribe":
                // Channel privé ?
                switch ($data->channeltype) {
                    case "chat":
                        $user=$this->em->getRepository("App\Entity\User")->findOneBy(["apikey"=>$data->userkey]);
                        if(!$user) return false;
                        
                        $group=$this->em->getRepository("App\Entity\Group")->find($data->channelkey);
                        if(!$group) return false;

                        if(!$user->hasRole("ROLE_ADMIN")&&!$user->hasRole("ROLE_MODO")) {
                            $usergroup=$this->em->getRepository("App\Entity\UserGroup")->findOneBy(["user"=>$user,"group"=>$data->channelkey]);
                            if(!$usergroup) return false;
                        }
                    break;
                }
                $this->channels[$conn->resourceId] = $data->channel;
                $this->channeltypes[$conn->resourceId] = $data->channeltype;
                $this->channelkeys[$conn->resourceId] = $data->channelkey;
                $this->userkeys[$conn->resourceId] = $data->userkey;
                $this->ispush[$conn->resourceId] = false;
            break;

            case "meto":
                if (isset($this->channels[$conn->resourceId])) {
                    $this->sendMessage($conn,$data,"other");
                }
            break;

            case "alive":
                if (isset($this->channels[$conn->resourceId])) {
                    $this->sendMessage($conn,$data);
                }
            break;

            default:
                if (isset($this->channels[$conn->resourceId])) {
                    switch ($this->channeltypes[$conn->resourceId]) {
                        case "chat": $this->onMessageChat($conn,$data); break;
                        default: $this->sendMessage($conn,$data);
                    }
                }
            break;
        }
    } 

    private function onMessageChat(ConnectionInterface $conn, $data)
    {
        $this->trace("== onMessageChat ======================");

        $id=$this->channelkeys[$conn->resourceId];
        
        $user=$this->em->getRepository("App\Entity\User")->findOneBy(["apikey"=>$this->userkeys[$conn->resourceId]]);
        if(!$user) return false;
        $group=$this->em->getRepository("App\Entity\Group")->find($this->channelkeys[$conn->resourceId]);
        if(!$group) return false;

        $this->trace($data->command);
        switch ($data->command) {
            case "addchat":
                // Si add par mail
                if(array_key_exists("mail",$data)) {
                    $pages=$group->getPages();
                    $idpage=$pages[0]->getId();

                    $url="https://".$this->container->getParameter('weburl')."/".$this->container->getParameter('alias').$this->container->get('router')->generate('app_core_redirect', ['route'=>'app_core_home','id'=>$idpage]);
                    $cplt="<br><br><b>Attention pour répondre à ce message : n’utilisez pas votre mail mais rendez vous dans ce groupe et utilisez le fil de conversation (chat)</b><br>";
                    $cplturl="<br><a href='".$url."'>".$url."</a>";

                    $subject=$data->subject;
                    $body=$data->message.$cplt.$cplturl;
                    $to=explode(";",$data->to);
                    unset($to[0]);
                    $from =  $this->container->getParameter('noreply');
                    $fromName = $user->getDisplayname();
                    $this->mail->sendEmail($subject, $body, $to, $from, $fromName); 

                    $data->message.="<br><i>Notification envoyée par mail</i>";
                }

                $message=new Message();
                $message->setTopic($data->message);
                $message->setUser($user);
                $message->setGroup($group);
                $this->em->persist($message);
                $this->em->flush();

                $data->id = $message->getId();
                $data->submitdate= $message->getSubmitdate()->format("d/m/Y H:i");

                $this->sendMessage($conn,$data);
            break;

            case "delchat":
                $message=$this->em->getRepository("App\Entity\Message")->find($data->id);
                $usergroup=$this->em->getRepository("App\Entity\UserGroup")->findOneBy(['user'=>$user,'group'=>$group]);
                if($message&&($usergroup->getRolegroup()>=90||$message->getUser()==$user||$user->getRole()=="ROLE_ADMIN"||$user->getRole()=="ROLE_MODO" )) {
                    $id=$message->getId();
                    $this->em->remove($message);
                    $this->em->flush();
                    $this->sendMessage($conn,$data);
                }
            break;

            case "replychat":
                $parent=$this->em->getRepository("App\Entity\Message")->find($data->parent);
                if($parent) {
                    // Si reply par mail
                    if(array_key_exists("mail",$data)) {
                        $pages=$group->getPages();
                        $idpage=$pages[0]->getId();

                        $url="https://".$this->container->getParameter('weburl')."/".$this->container->getParameter('alias').$this->container->get('router')->generate('app_core_redirect', ['route'=>'app_core_home','id'=>$idpage]);
                        $cplt="<br><br><b>Attention pour répondre à ce message : n’utilisez pas votre mail mais rendez vous dans ce groupe et utilisez le fil de conversation (chat)</b><br>";
                        $cplturl="<br><a href='".$url."'>".$url."</a>";

                        $subject=$data->subject;
                        $body=$data->message.$cplt.$cplturl;
                        $to=explode(";",$data->to);
                        unset($to[0]);
                        $from =  $this->container->getParameter('noreply');
                        $fromName = $user->getDisplayname();
                        $this->mail->sendEmail($subject, $body, $to, $from, $fromName); 

                        $data->message.="<br><i>Notification envoyée par mail</i>";
                    }

                    $message=new Message();
                    $message->setTopic($data->message);
                    $message->setUser($user);
                    $message->setGroup($group);
                    $message->setParent($parent);
                    $this->em->persist($message);
                    $this->em->flush();

                    // Si commentaire sur une message : il faut replacer le message en unsee pour tt le monde
                    if($parent->getSees()) {
                        foreach($parent->getSees() as $see) {
                            $parent->removeSee($see);
                        }
                        $this->em->flush();
                    }
                    
                    $data->id = $message->getId();
                    $data->submitdate= $message->getSubmitdate()->format("d/m/Y H:i");
                    $this->sendMessage($conn,$data);
                }
            break;          
        }
    }

    private function sendMessage(ConnectionInterface $conn, $data, $dest="all")
    {
        $this->trace("== sendMessage ========================");
        $target = $this->channels[$conn->resourceId];
        foreach ($this->channels as $id=>$channel) {
            if ($channel == $target) {              
                // dest = all - tout le monde meme l'expediteur
                // dest = other - tout le monde sauf l'expedituer
                // dest = me - seumlement l'expediteur
                if($dest=="all"||($dest=="other"&&$id!=$conn->resourceId)||($dest=="me"&&$id==$conn->resourceId)) {
                    // From
                    $key= $this->userkeys[$conn->resourceId];
                    $from=$this->em->getRepository("App\Entity\User")->findOneBy(["apikey"=>$key]);

                    // To
                    $key= $this->userkeys[$id];
                    $to=$this->em->getRepository("App\Entity\User")->findOneBy(["apikey"=>$key]);

                    // Send
                    if($from && $to) {
                        $now=new \DateTime();
                        
                        $data->from= new \stdClass;
                        $data->from->id             = $from->getId();
                        $data->from->username       = $from->getUsername();
                        $data->from->displayname    = $from->getDisplayname();
                        $data->from->avatar         = $this->getAvatar($from->getAvatar());
                        $data->date                 = $now->format("d/m/Y H:i");
                        
                        $data->log="== GET MSG from ".$data->from->username." to ".$to->getUsername()." = ".$data->command;
                        $this->userconns[$id]->send(json_encode($data));
                    }
                }
            }
        }
    }

    private function getAvatar($avatar) {
        if(stripos($avatar,"http")===0)
            return $avatar;
        else
            return "uploads/avatar/".$avatar;        
    }

    private function trace($msg,$type="echo")
    {
        if($this->fgdebug) {
            if($type=="echo") echo $msg."\n";
            else dump($msg);
        }
    }    
}