<?php


namespace App\FrontModule\Presenters;


use App\Components\Forms\BootstrapForm;
use App\Model\Crates\DkpRoom;
use App\Model\Entities\Components\Event\Event;
use App\Model\Entities\Security\User;
use App\Model\Exceptions\Db\RecordNotFoundException;
use Kdyby\Doctrine\EntityManager;
use Nette\Application\UI\Form;
use Nette\Caching\Cache;
use Nette\Caching\IStorage;
use Nette\Http\IResponse;
use Throwable;
use Tracy\Debugger;

class DkpPresenter extends FrontBasePresenter
{
    /** @var  EntityManager */
    protected $entityManager;
    /** @var  Cache */
    protected $cache;
    /** @var  DkpRoom */
    protected $data;

    /** @var  int */
    protected $eventId;
    /** @var  Event */
    protected $event;

    public function injectServices(IStorage $storage, EntityManager $entityManager)
    {
        $this->cache = new Cache($storage, 'dkp-room');
        $this->entityManager = $entityManager;
    }

    public function actionRoom($id)
    {
        $this->eventId = $id;

        $data = $this->getData();
        if (empty($data->getUsers())) {
            foreach ($this->getEvent()->getUsers() as $user) {
                $this->setupUserData($user);
            }
        }
        if (!$data->hasUser($this->user->getId())) {
            $this->setupUserData($this->user->getUserEntity());
        }
    }

    public function renderRoom()
    {
        $data = $this->getData();
        $this->template->data = $data;

        if ($data->isSaveRequired()) {
            $data->savingDone();
            $this->saveData($data);
        }
    }

    public function handleRepeatBidding()
    {
        $this->getData()->repeatBidding();
        $this->getData()->clearBids();
        if ($this->isAjax()) {
            $this->redrawControl('dkp-header');
            $this->redrawControl('dkp-bids');
            $this->redrawControl('dkp-bid-form');
        } else {
            $this->redirect('this');
        }
    }

    public function handleCancelBidding()
    {
        $this->getData()->endBidding();
        $this->getData()->clearBids();
        if ($this->isAjax()) {
            $this->redrawControl('dkp-header');
            $this->redrawControl('dkp-bids');
            $this->redrawControl('dkp-bid-form');
        } else {
            $this->redirect('this');
        }
    }

    public function handleGiveItem($userId)
    {
        $data = $this->getData();
        $event = $this->getEvent();

        $this->siteFacade->getDkpWriter()->grantDkp($event->getSite(), $userId, $data->getItemName(), $data->getBid($userId) * (-1));
        $data->giveItem($userId);
        $data->endBidding();

        try {
            $amount = $this->siteFacade->getDkpReader()->getUsersDkp($event->getSite(), $userId);
            $data->setUserDkp($userId, $amount);
        } catch (RecordNotFoundException $e) {
            $this->error('User not found.');
        }

        if ($this->isAjax()) {
            $this->redrawControl('dkp-header');
            $this->redrawControl('dkp-bids');
            $this->redrawControl('dkp-bid-form');
        } else {
            $this->redirect('this');
        }
    }

    public function handleRefreshData()
    {
        if ($this->isAjax()) {
            $this->redrawControl('dkp-header');
            $this->redrawControl('dkp-bids');
            if ($this->getData()->refreshBidForm()) {
                $this->redrawControl('dkp-bid-form');
            }
        }
    }

    protected function createComponentNewBiddingForm()
    {
        $form = new BootstrapForm();
        $form->addText('item', 'Item name')
            ->setAttribute('class', ['dkp-input'])
            ->setRequired();
        $form->addText('duration', 'Duration [s]')
            ->setAttribute('class', ['dkp-input'])
            ->addRule(Form::RANGE, "Enter positive number.", array(0, null))
            ->setDefaultValue(60);
        $form->addSubmit('submit', 'Start')
            ->setButtonType(BootstrapForm::BTN_TYPE_PRIMARY)
            ->setButtonDisplay(BootstrapForm::BTN_DISPLAY_BLOCK);
        $form->onSuccess[] = [$this, 'processNewBidding'];

        return $form;
    }

    public function processNewBidding(BootstrapForm $form, $values)
    {
        $data = $this->getData();
        $data->startBidding($values->duration, $values->item);
        if ($this->isAjax()) {
            $this->redrawControl('dkp-header');
            $this->redrawControl('dkp-bids');
            $this->redrawControl('dkp-bid-form');
        } else {
            $this->saveData($data);
            $this->redirect('this');
        }
    }

    protected function createComponentGrantDkpForm()
    {
        $form = new Form();
        $form->addCheckboxList('users', null, $this->getData()->getUsersList())
            ->setDefaultValue($this->getData()->getGrantCheckboxes());
        $form->addText('reason', 'Granted for')->setRequired();
        $form->addText('amount', 'DKP')->addRule(Form::RANGE, "Enter positive number.", array(0, null));
        $form->addSubmit("submit", "Done");
        $form->onSuccess[] = [$this, 'processGrantDkp'];

        return $form;
    }

    public function processGrantDkp(Form $form, $values)
    {
        $data = $this->getData();
        $data->setGrantCheckboxes($values->users);

        try {
            $this->entityManager->beginTransaction();
            foreach ($values->users as $userId) {
                $this->siteFacade->getDkpWriter()->grantDkp($this->getEvent()->getSite(), $userId, $values->reason, $values->amount);
                $amount = $this->siteFacade->getDkpReader()->getUsersDkp($this->getEvent()->getSite(), $userId);
                $data->setUserDkp($userId, $amount);
            }
            $this->entityManager->commit();
        } catch (Throwable $e) {
            $this->entityManager->rollback();
            Debugger::log($e);
            $this->error(null, IResponse::S400_BAD_REQUEST);
        }

        $this->saveData($data);
        $this->redirect('this');
    }

    /**
     * @return Form
     */
    protected function createComponentBidForm()
    {
    	$form = new Form();
    	$form->addText('bid')->addRule(Form::RANGE, "Invalid value.", array(1, $this->getData()->getUserDkp($this->user->id)));
        $form->addSubmit("submit", "Bid");
    	$form->onSuccess[] = [$this, 'processBid'];

    	return $form;
    }

    public function processBid(Form $form, $values)
    {
        $data = $this->getData();
        $data->bid($this->user->getId(), $values->bid);

        if ($this->isAjax()) {
            // todo: find out why is it not redrawing
//            $this->redrawControl('dkp-bids');
//            $this->redrawControl('dkp-bid-form');
            $data->bidFormRedraw();
        } else {
            $this->saveData($data);
            $this->redirect('this');
        }
    }

    protected function getData() : DkpRoom
    {
        if ($this->data === null) {
            $this->data = $this->cache->load($this->eventId, [$this, 'loadData']);
            $this->cache->clean();
        }

        return $this->data;
    }

    protected function saveData($data)
    {
        $this->cache->save($this->eventId, $data, [Cache::EXPIRE => '10 seconds', Cache::SLIDING => true]);
    }

    public function loadData(&$dependencies)
    {
        $dependencies[Cache::EXPIRE] = '10 seconds';
        $dependencies[Cache::SLIDING] = true;

        return new DkpRoom($this->getEvent());
    }

    protected function getEvent() : Event
    {
        if ($this->event === null) {
            try {
                $this->event = $this->siteFacade->getEventReader()->findEventById($this->eventId);
            } catch (RecordNotFoundException $e) {
                $this->error('Page not found.');
            }
        }

        return $this->event;
    }

    protected function setupUserData(User $user)
    {
        $data = $this->getData();
        if ($this->getEvent()->getUsers()->contains($user)) {
            $data->addUser($user->getId(), $user->getNickname());       // check if user is registered in raid planner
        } else {
            $this->error(null, IResponse::S403_FORBIDDEN);
        }
        try {
            $amount = $this->siteFacade->getDkpReader()->getUsersDkp($this->getEvent()->getSite(), $user);
            $data->setUserDkp($user->getId(), $amount);
        } catch (RecordNotFoundException $e) {
            $this->error('User not found.');
        }
    }
}