<?php /** @noinspection PhpIllegalPsrClassPathInspection */

namespace Kicken\Chat;


use 
InvalidArgumentException;

class 
ChatClient extends HTTPClient {
    private 
ChatServer $mChatServer;
    private ?
ChatUser $mUser null;
    private array 
$mHeaders;
    private 
string $mRequestBody;
    private array 
$mAllowedOrigins;
    
/** @var callable */
    
private $mPollingCb;

    public function 
__construct($resourceChatServer $chatServer, array $allowedOrigins){
        
parent::__construct($resource);
        
$this->mChatServer $chatServer;
        
$this->mAllowedOrigins $allowedOrigins;
    }

    public function 
route(string $uri) : void{
        
$path trim($uri'/');
        
$path str_replace('/''_'$path);

        
$this->mHeaders $this->getRequestHeaders();
        
$this->mRequestBody $this->getRequestBody();

        if (
method_exists($this$path)){
            
$headers = [];
            if (isset(
$this->mHeaders['ORIGIN'])){
                
$headers $this->getCORSHeaders($path);
            }

            if (
$this->mHeaders['REQUEST_METHOD'] === 'OPTIONS'){
                
$this->sendResponse(200'CORS Response'$headers);
            } else {
                
$this->$path($headers);
            }
        } else {
            
parent::route($uri);
        }
    }

    private function 
getCORSHeaders(string $path) : ?array{
        
$methodList = [
            
'messages' => ['GET''POST'],
            
'userlist' => ['GET''REPORT'],
            
'register' => ['POST'],
            
'chat' => ['GET']
        ];

        
$headers = [];
        if (!isset(
$methodList[$path])){
            
$this->sendResponse(405'Method not allowed');

            return 
null;
        } else {
            foreach (
$methodList[$path] as $m){
                
$headers['ACCESS_CONTROL_ALLOW_METHODS'][] = $m;
            }
        }

        if (isset(
$this->mHeaders['ORIGIN'])){
            
$origin $this->mHeaders['ORIGIN'];
            foreach (
$this->mAllowedOrigins as $o){
                if (
strcasecmp($origin$o) == || $o == '*'){
                    
$headers['ACCESS_CONTROL_ALLOW_ORIGIN'] = $o;
                    break;
                }
            }
        }

        
$headers['ACCESS_CONTROL_ALLOW_HEADERS'] = 'x-requested-with,content-type';

        return 
$headers;
    }

    private function 
getPostedJson(){
        if (!
$this->isPostRequest()){
            
$this->sendResponse(405'POST method only.');

            return 
false;
        } else if (!
$this->isJsonRequest()){
            
$this->sendResponse(415'Request entity must be a JSON string');

            return 
false;
        }

        
$json json_decode($this->mRequestBody);

        return 
$json;
    }

    public function 
register(array $additionalHeaders) : void{
        
$json $this->getPostedJson();
        if (
$json === false){
            return;
        }
        try {
            
$nick trim($json->nick);
            
$uid $this->mChatServer->registerNick($nick);
            
$this->sendJsonResponse(200, ['uid' => $uid'nick' => $nick]);
        } catch (
InvalidArgumentException $e){
            
$this->sendJsonResponse(400, [
                
'error' => $e->getMessage(),
                
'code' => $e->getCode()
            ], 
$additionalHeaders);
        }
    }

    public function 
messages(array $additionalHeaders) : void{
        
$GET $this->getQueryVars();
        if (!isset(
$GET['uid'])){
            
$this->sendResponse(400'Missing uid parameter'$additionalHeaders);

            return;
        } else if ((
$this->mUser $this->mChatServer->getUserByUID($GET['uid'])) === null){
            
$this->sendResponse(404'User not found'$additionalHeaders);

            return;
        }

        
$this->mUser->setActiveRequest($this);
        if (
$this->mHeaders['REQUEST_METHOD'] === 'GET'){
            
//Polling for new messages
            
if (!$this->mPollingCb && $this->isConnected()){
                
$this->mPollingCb = function() use ($additionalHeaders){
                    
$messages $this->mUser->getNewMessages();
                    
$this->sendJsonResponse(200, [
                        
'messages' => $messages
                    
], $additionalHeaders);
                };

                
$this->mChatServer->onNewMessage($this->mPollingCb);
            }
        } else if (
$this->mHeaders['REQUEST_METHOD'] === 'POST'){
            
//Posting a new message
            
$json $this->getPostedJson();
            if (
$json === false){
                return;
            }

            if (!isset(
$json->type) || !isset($json->text)){
                
$this->sendResponse(400'Invalid JSON object format.  Requires type and text properties.'$additionalHeaders);
            }

            
$json->timestamp $this->now();
            
$json->uid $this->mUser->getUid();
            
$newId $this->mChatServer->saveMessage($json);
            
//echo "Saved new message as {$newId}\r\n";
            
$messages $this->mUser->getNewMessages();
            
$this->sendJsonResponse(200, [
                
'messageId' => $newId,
                
'timestamp' => $json->timestamp,
                
'messages' => $messages
            
], $additionalHeaders);
        }
    }

    private function 
now() : int{
        return 
time();
    }

    public function 
isPostRequest() : bool{
        return 
$this->mHeaders['REQUEST_METHOD'] == 'POST';
    }

    public function 
isJsonRequest() : bool{
        return 
str_contains($this->mHeaders['CONTENT_TYPE'], 'text/json');
    }

    public function 
close() : void{
        
parent::close();
        if (
$this->mPollingCb){
            
$this->mChatServer->cancelOnNewMessage($this->mPollingCb);
            
$this->mPollingCb null;
        }

        if (
$this->mUser && $this->mUser->getActiveRequest() === $this){
            
$this->mUser->setActiveRequest();
        }
    }

    public function 
chat(array $additionalHeaders) : void{
        
$this->sendResponse(200file_get_contents('chat.html'), $additionalHeaders);
    }

    
/** @noinspection PhpUnused */
    
public function userlist(array $additionalHeaders) : void{
        
$uidMap = [];
        foreach (
$this->mChatServer->getUsers() as $u){
            
$uidMap[$u->getUid()] = $u->getNick();
        }

        
$this->sendJsonResponse(200, [
            
'uidmap' => $uidMap
        
], $additionalHeaders);
    }

    public function 
sendJsonResponse(int $code, array $arr, array $headers = []) : void{
        
$json json_encode($arr);
        
$headers array_merge($headers, [
            
'CONTENT_TYPE' => 'text/json',
            
'CONTENT_LENGTH' => strlen($json)
        ]);

        
$this->sendResponse($code$json$headers);
    }
}