<?php

declare(strict_types=1);

namespace core\interpreter;

use core\handler\Handler;
use core\http2Client\Http2Client;
use core\protocol\CommandDeserializer;
use core\protocol\CommandSerializer;
use core\receiver\Receiver;
use core\transmitter\Transmitter;
use core\webSocketClient\WebSocketClient;
use utils\Command;
use utils\CommandInterface;
use utils\connectiondata\Http2ConnectionData;
use utils\connectiondata\IConnectionData;
use utils\connectiondata\WsConnectionData;
use utils\messagehelper\MessageHelper;
use utils\RuntimeName;
use utils\type\CommandType;
use utils\type\ConnectionType;


final class Interpreter
{
    private function __construct()
    {
    }

    private static function isDebugMode(): bool
    {
        $debugMode = getenv('HYPERTUBE_DEBUG');
        if ($debugMode === false) {
            $debugMode = '';
        }

        return strtolower((string) $debugMode) === 'true';
    }

    public static function execute(CommandInterface $command, IConnectionData $IConnectionData): CommandInterface
    {
        if (self::isDebugMode()) {
            echo 'Command sent from calling side: ' . $command;
            MessageHelper::getInstance()->sendMessageToAppInsights(
                'Command sent from calling side',
                (string) $command
            );
        }

        $responseByteArray = self::getResponseByteArray($command, $IConnectionData);

        $response = CommandDeserializer::deserialize($responseByteArray);
        if (self::isDebugMode()) {
            echo 'Command received on calling side: ' . $response;
            MessageHelper::getInstance()->sendMessageToAppInsights(
                'Command received on calling side',
                (string) $response
            );
        }

        return $response;
    }

    private static function getResponseByteArray(CommandInterface $command, IConnectionData $IConnectionData)
    {
        $messageByteArray = CommandSerializer::serialize($command, $IConnectionData);
        if ($IConnectionData instanceof WsConnectionData)
        {
           return WebSocketClient::sendMessage($IConnectionData, $messageByteArray);
        }

        if ($IConnectionData instanceof Http2ConnectionData)
        {
            return Http2Client::sendMessage($IConnectionData, $messageByteArray);
        }

        if (count($IConnectionData->getHeaders()) > 0) {
            $contextMetadataPayload = self::buildContextMetadataPayload($command, $IConnectionData);
            $command = new Command($command->getRuntimeName(), CommandType::CONTEXT_METADATA(), [$contextMetadataPayload, $command]);
            $messageByteArray = CommandSerializer::serialize($command, $IConnectionData);
        }

        if (self::isInMemoryAndSameRuntime($command, $IConnectionData)) {
            return Receiver::sendCommand($messageByteArray);
        }

        return Transmitter::sendCommand($messageByteArray);
    }

    private static function isInMemoryAndSameRuntime(CommandInterface $command, IConnectionData $IConnectionData): bool
    {
        return $command->getRuntimeName()->equalsByValue(RuntimeName::PHP)
            && $IConnectionData->getConnectionType()->equalsByValue(ConnectionType::IN_MEMORY);
    }

    private static function buildContextMetadataPayload(CommandInterface $command, IConnectionData $IConnectionData): CommandInterface
    {
        $metadataPayload = [];
        $headers = $IConnectionData->getHeaders();

        foreach ($headers as $key => $value) {
            $metadataPayload[] = $key;
            $metadataPayload[] = $value;
        }

        return new Command($command->getRuntimeName(), CommandType::ARRAY(), $metadataPayload);
    }

    public static function process(array $byteArray): CommandInterface
    {
        $receivedCommand = CommandDeserializer::deserialize($byteArray);
        if (self::isDebugMode()) {
            echo 'Command received on called side: ' . $receivedCommand;
            MessageHelper::getInstance()->sendMessageToAppInsights(
                'Command received on called side',
                (string) $receivedCommand
            );
        }

        $response = Handler::handleCommand($receivedCommand);
        if (self::isDebugMode()) {
            echo 'Response sent from called side: ' . $response;
            MessageHelper::getInstance()->sendMessageToAppInsights(
                'Response sent from called side',
                (string) $response
            );
        }

        return $response;
    }
}
