<?php

namespace App\Game\Service;

use App\Game\Enum\DecodeMessage;
use App\Game\Enum\SessionSettingType;
use App\Game\Entity\Player;
use App\Game\Repository\SessionSettingRepository;
use App\Tech\Entity\User;
use Symfony\Bundle\SecurityBundle\Security;

class GameResponseService
{
    public function __construct(
        private Security $security,
        private PlayerService $playerService,
        private SessionSettingRepository $sessionSettingRepository,
    ) {
    }

    public function getGameResponse(string $raw)
    {
        $info = json_decode($raw, true);

        $message = $info['message'] ?? '';
        $ts = $info['ts'] ?? '';

        if(!is_string($message))
            return ['error' => 'Invalid message.'];

        $user = $this->security->getUser();

        if(!$user instanceof User)
            return ['error' => 'You are not logged in.'];

        $player = $this->playerService->GetCurrentlyActiveAsPlayer($user);

        if(!$player)
            return ['error' => 'You are not in a game.'];

        // TODO: Here i need to add a message handler to save the message in a big log.

        $data = [];

        if(str_starts_with($message, '/')) {
            $data = $this->checkGameCommando($message, $player);
        } else {
            $data = $this->checkConsoleCommando($message, $player);
        }

        return $data;
    }

    private function getRechten(Player $player): array
    {
        $settingName = match($player->getScreen()) {
            1 => SessionSettingType::RIGHTS_FOR_PLAYER1,
            2 => SessionSettingType::RIGHTS_FOR_PLAYER2,
            3 => SessionSettingType::RIGHTS_FOR_PLAYER3,
            default => null,
        };

        if (!$settingName) {
            return [];
        }

        $setting = $this->sessionSettingRepository->getSetting($player->getSession(), $settingName, $player);
        if (!$setting || !$setting->getValue()) {
            return [];
        }

        return json_decode($setting->getValue(), true) ?? [];
    }

    private function checkGameCommando(string $message, Player $player) : array
    {
        $messagePart = explode(' ', $message);

        $rechten = $this->getRechten($player);

        switch($messagePart[0]) {
            case '/chat':
                if(!in_array('chat', $rechten))
                    return ['result' => ['Unknown command']];

                $this->handleChatMessage($message);
                break;
            case '/help':
                return ['result' => $this->getHelpCommand($rechten)];
            case '/decode':
                if(!in_array('decode', $rechten))
                    return ['result' => ['Unknown command']];

                return ['result' => [$this->handleDecodeMessage($messagePart[1], $player)]];
            case '/verify':
                if(!in_array('verify', $rechten))
                    return ['result' => ['Unknown command']];

                $result = $this->handleVerifyMessage($message);
                return ['result' => [$result]];
            default:
                return ['result' => ['Unknown command']];
        }
        return [];
    }

    private function checkConsoleCommando(string $message, Player $player, bool $sudo = false) : array
    {
        $messagePart = explode(' ', $message);
        $rechten = $this->getRechten($player);
        switch($messagePart[0]) {
            case 'help':
                return ['result' => $this->getHelpCommand($rechten)];
            case 'ls':
                break;
            case 'cd':
                $pwd = $this->playerService->getCurrentPwdOfPlayer($player);
                if(!$pwd)
                    return ['result' => ['Unknown command']];
                $newLocation = $this->goToNewDir($pwd, $messagePart[1], $player);

                if($newLocation === false)
                    return ['result' => ['Unknown path']];

                $this->playerService->saveCurrentPwdOfPlayer($player, $newLocation);
                return ['result' => ['Path: ' . $newLocation]];
            case 'rm':
                break;
            case 'sudo':
                break;
            default:
                return ['result' => ['Unknown command']];
        }
        return [];
    }

    private function getHelpCommand(mixed $rechten) : array
    {
        $messages = [];

        foreach($rechten as $recht) {
            switch($recht) {
                case 'chat':
                    $messages[] = '/chat';
                    $messages[] = '   Use /chat {message} to send the message to the other agents.';
                    $messages[] = '   If you want to send a message specifically to one other agent, use the id of the agent after /chat, like /chat 6 {message}';
                    $messages[] = '   This will send the message only to agent with id 6.';
                    $messages[] = '   USAGE: /chat {message}';
                    $messages[] = '';
                    break;
                case 'help':
                    $messages[] = '/help';
                    $messages[] = '   This shows this help.';
                    $messages[] = '   USAGE: /help';
                    $messages[] = '';
                    break;
                case 'decode':
                    $messages[] = '/decode';
                    $messages[] = '   This message will decode the message followed by it.';
                    $messages[] = '   Every agent has a different way to decode messages. This is a security measure. The AI Virus has no access to all decoders.';
                    $messages[] = '   USAGE: /decode {message}';
                    $messages[] = '';
                    break;
                case 'cat':
                    $messages[] = 'cat';
                    $messages[] = '   To read a file, use cat {filename}.';
                    $messages[] = '   This will print the full content of the file on the screen.';
                    $messages[] = '   USAGE: cat {filename}';
                    $messages[] = '';
                    break;
                case 'ls':
                    $messages[] = 'ls';
                    $messages[] = '   To show all the files in the current directory, use ls.';
                    $messages[] = '   This will print the full list of directories and files of the current location on your screen.';
                    $messages[] = '   USAGE: ls';
                    $messages[] = '';
                    break;
                case 'rm':
                    $messages[] = 'rm';
                    $messages[] = '   Use rm to delete a file.';
                    $messages[] = '   Be careful with this command. It can not be undone and we do not want to lose any valuable data.';
                    $messages[] = '   USAGE: rm {filename}';
                    $messages[] = '';
                    break;
                case 'cd':
                    $messages[] = 'cd';
                    $messages[] = '   Use cd to move to a different directory.';
                    $messages[] = '   You can go into a folder by using cd {foldername}, or a folder up by using "cd ..".';
                    $messages[] = '   Using cd / moves you to the root directory.';
                    $messages[] = '   USAGE: cd {directory}';
                    $messages[] = '';
                    break;
                case 'sudo':
                    $messages[] = 'sudo';
                    $messages[] = '   If you do not have enough rights to execute a command, you can use sudo to execute it as root.';
                    $messages[] = '   This is only possible for verified users. To verify yourself, use the /verify command.';
                    $messages[] = '   USAGE: sudo {command}';
                    $messages[] = '';
                    break;
                case 'verify':
                    $messages[] = '/verify';
                    $messages[] = '   You can verify yourself by using this command.';
                    $messages[] = '   Use this command and follow instructions to verify yourself.';
                    $messages[] = '   USAGE: /verify';
                    $messages[] = '';
                    break;
            }
        }

        return $messages;
    }

    private function handleChatMessage(string $message)
    {

    }

    private function handleDecodeMessage(string $message, Player $player)
    {
        $userNumber = $player->getScreen();

        preg_match('/\d+/', $message, $matches);
        $num = $matches[0] ?? null;
        $randomString = $this->generateRandomString(250, 500);

        if(is_null($num) || $num != $userNumber)
            return $randomString;

        foreach (DecodeMessage::cases() as $decodeMessage) {
            if ($decodeMessage->name === $message) {
                return $decodeMessage->value;
            }
        }

        return $randomString;
    }

    private function generateRandomString(int $min, int $max): string
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ';
        $charactersLength = strlen($characters);
        $randomString = '';
        $length = random_int($min, $max);
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[random_int(0, $charactersLength - 1)];
        }
        return $randomString;
    }

    private function handleVerifyMessage(string $message) : string
    {
        return '';
    }

    private function goToNewDir(string $pwd, string $newPwd, Player $player) : string|bool
    {
        $allPossiblePaths = $this->getAllPossiblePaths($player);

        $dirParts = explode('/', $newPwd);

        $int = count($dirParts);

        if($dirParts[0] == '') {
            $newDir = '';
            $startPart = 1;
        } else {
            $newDir = $pwd;
            $startPart = 0;
        }

        for($i = $startPart; $i < $int; $i++) {
            if($dirParts[$i] == '..')
                $newDir = $this->getPrevPath($newDir);
            else
                $newDir .= '/' . $dirParts[$i];

            if(!in_array($newDir, $allPossiblePaths))
                return false;
        }

        return $newDir;
    }

    private function getPrevPath(string $pwd) : string
    {
        $pwdParts = explode('/', $pwd);
        array_pop($pwdParts);
        $pwd = implode('/', $pwdParts);
        return $pwd;
    }

    private function getAllPossiblePaths(Player $player) : array
    {
        $paths = [];

        $paths[] = '/';
        $paths[] = '/var';
        $paths[] = '/var/arrest';
        $paths[] = '/var/www';
        $paths[] = '/var/marriage';
        $paths[] = '/var/rapports';
        $paths[] = '/var/linking';

        $paths[] = '/etc';
        $paths[] = '/etc/short';
        $paths[] = '/etc/long';
        $paths[] = '/etc/arrest';
        $paths[] = '/etc/power';
        $paths[] = '/etc/break';
        $paths[] = '/etc/handle';
        $paths[] = '/etc/freak';
        $paths[] = '/etc/host';

        $paths[] = '/home';

        $playerNames = ['root', 'Luke', 'Charles', 'William', 'Peter'];

        $players = $player->getSession()->getPlayers();

        foreach($players as $player) {
            $playerNames[] = $player->getUser()->getUsername();
        }

        $playerNames = array_unique($playerNames);

        foreach($playerNames as $name) {
            $paths[] = '/home/' . $name;
        }

        return $paths;
    }
}
