<?php
/**
 * Created by PhpStorm.
 * User: silas
 * Date: 10.11.16
 * Time: 11:59
 */

namespace Application\Controller;

use Ainias\Core\Controller\JsonController;
use Ainias\Core\Controller\ServiceActionController;
use Ainias\Core\Module;
use Application\Model\AuthToken;
use Application\Model\Level;
use Application\Model\Manager\AuthTokenManager;
use Application\Model\Manager\LevelManager;
use Application\Model\Manager\RatingManager;
use Application\Model\Manager\WordManager;
use Application\Model\Rating;
use Zend\Log\Logger;
use Zend\View\Model\ViewModel;

class SyncController extends JsonController
{
    const SYNC_MAX_WORDS = 300;
    const SYNC_MAX_LEVELS = 300;

    const MAX_RATINGS_PER_LEVEL = 2;

    public function getAuthTokenAction()
    {
        if (!$this->getRequest()->isPost()) {
            return $this->triggerDispatchError(404);
        }

        /** @var AuthTokenManager $authTokenManager */
        $authTokenManager = $this->get(AuthTokenManager::class);

        $token = new AuthToken();
        $name = $this->getRequest()->getPost("name");
        if (trim($name) == "") {
            return $this->triggerDispatchError(400);
        }
        $token->setName($name);
        $token->setToken($authTokenManager->generateNewAuthToken());
        $authTokenManager->save($token);

        $this->layout("layout/ajaxData");
        $viewModel = new ViewModel();
        $viewModel->setTemplate("ajax/json");
        $viewModel->setVariable("json", [
            "result" => true,
            "data" => [
                "authToken" => $token->getToken(),
            ],
        ]);
        return $viewModel;
    }

    public function getWordsAction()
    {
        $request = $this->getRequest();
        if (!$request->isPost()) {
            return $this->triggerDispatchError();
        }

        $this->getEventManager()->trigger(Module::EVENT_LOG, null, array(
            "message" => "Synchronized Words: " . $request->toString(),
            "level" => Logger::INFO
        ));

        $currentRun = (int)$request->getPost("currentRun", null);
        $dateLastSync = $request->getPost("dateLastSync", null);
        if ($dateLastSync != null) {
            try {
                $dateLastSync = \DateTime::createFromFormat(self::DATETIME_SYNC_FORMAT, $dateLastSync);
            } catch (\Throwable $t) {
                $dateLastSync = null;
            }
        }

        /** @var WordManager $wordManager */
        $wordManager = $this->get(WordManager::class);
        $newDate = new \DateTime();
        $words = $wordManager->wordsToArray($wordManager->findNewerThan($dateLastSync, $currentRun));
        $numberWordsToSync = $wordManager->countNewerThan($dateLastSync);

        $this->layout("layout/ajaxData");
        $viewModel = new ViewModel();
        $viewModel->setTemplate("ajax/json");
        $viewModel->setVariable("json", [
            "result" => true,
            "data" => [
                "countWords" => $numberWordsToSync,
                "currentSyncDate" => $newDate->format(self::DATETIME_SYNC_FORMAT),
                "currentRun" => $currentRun,
                "maxRuns" => ceil($numberWordsToSync / SyncController::SYNC_MAX_WORDS),
                "words" => $words,
            ],
        ]);
        return $viewModel;
    }

    public function getLevelsAction()
    {
        $request = $this->getRequest();

        $currentRun = (int)$request->getQuery("currentRun", null);
        $dateLastSync = $request->getQuery("dateLastSync", null);
        try {
            $dateLastSync = new \DateTime("@" . $dateLastSync, new \DateTimeZone("UTC"));
        } catch (\Throwable $e) {
            $dateLastSync = new \DateTime();
        }

        /** @var LevelManager $levelManager */
        $levelManager = $this->get(LevelManager::class);
        $newDate = new \DateTime("now", new \DateTimeZone("UTC"));
        $levels = $levelManager->levelsToArray($levelManager->findNewerThan($dateLastSync, $currentRun));
        $numberLevelsToSync = $levelManager->countNewerThan($dateLastSync);

        return [
            "countLevels" => $numberLevelsToSync,
            "currentSyncDate" => $newDate->getTimestamp(),
            "currentRun" => $currentRun,
            "maxRuns" => ceil($numberLevelsToSync / SyncController::SYNC_MAX_LEVELS),
            "levels" => $levels,
        ];
    }

    public function rateAction()
    {
        $request = $this->getRequest();
        if (!$request->isPost()) {
            return $this->triggerDispatchError(404);
        }

        $this->getEventManager()->trigger(Module::EVENT_LOG, null, array(
            "message" => "Synchronized Level: " . $request->toString(),
            "level" => Logger::INFO
        ));

        /** @var AuthTokenManager $authTokenManager */
        $authTokenManager = $this->get(AuthTokenManager::class);
        $authToken = $authTokenManager->findOneByToken($request->getPost("authToken"));

        if ($authToken == null) {
            return $this->triggerDispatchError(403);
        }

        /** @var RatingManager $ratingManager */
        $ratingManager = $this->get(RatingManager::class);

        /** @var LevelManager $levelManager */
        $levelManager = $this->get(LevelManager::class);
        $levelsArray = json_decode($request->getPost("levels"), true);

        $levels = [];
        foreach ($levelsArray as $levelArray) {
            $levelManager->beginTransaction();

            $words = json_encode($levelArray["words"]);
            $rotations = json_encode($levelArray["rotations"]);
            $level = $levelManager->findOneBy([
                "words" => $words,
                "positions" => $rotations,
                "renderer" => $levelArray["rendererType"],
                "lang" => $levelArray["language"],
            ]);
            if ($level == null) {
                $level = new Level();
                $level->setDeleted(false);
                $level->setWords($words);
                $level->setPositions($rotations);
                $level->setRenderer($levelArray["rendererType"]);
                $level->setLang($levelArray["language"]);
                $level->setLastUpdated(new \DateTime());
                $level->setDeleted(false);
                $level->setDifficulty($levelArray["difficulty"]);
                $levelManager->save($level);
            }
            $rating = $ratingManager->findOneBy([
                "level" => $level,
                "authToken" => $authToken,
            ]);

            if ($rating == null) {
                $rating = new Rating();
                $rating->setAuthToken($authToken);
                $rating->setLevel($level);
                $level->getRatings()->add($rating);
            }

            $ratingScore = $levelArray["rating"];
            $rating->setRating($ratingScore);

            if ($level->getRatings()->count() >= self::MAX_RATINGS_PER_LEVEL || $ratingScore < 0) {
                $level->setDeleted(true);
                $level->setLastUpdated(new \DateTime());
//                $levelManager->save($level);
            }

            $ratingManager->save($rating);
            $levelManager->endTransaction();
            $levels[] = $level;
        }
        $this->layout("layout/ajaxData");
        $viewModel = new ViewModel();
        $viewModel->setTemplate("ajax/json");
        $viewModel->setVariable("json", [
            "result" => true,
            "data" => [
                "levels" => $levelManager->levelsToArray($levels),
            ],
        ]);
        return $viewModel;
    }
}