<?php


namespace App\Components\DataGrids;


use App\Model\Enums\FlashMessage;
use App\Model\Exceptions\Db\NullValueException;
use App\Model\Interfaces\ICommonRecord;
use App\Model\Translator;
use Kdyby\Doctrine\QueryBuilder;
use Nette\Application\UI\Control;
use Nette\Forms\Container;
use Nette\NotImplementedException;
use Throwable;
use Tracy\Debugger;
use Ublaboo\DataGrid\DataGrid;

abstract class BaseDataGrid extends Control
{
    protected static $templateName = 'grid.latte';

    /** @var Translator */
    protected $translator;

    /** @var bool */
    private $isAdderEnabled = false;

    public function __construct(Translator $translator)
    {
        parent::__construct();
        $this->translator = $translator;
    }

    public function render()
    {
        $this->template->setFile(__DIR__ . '/templates/' . static::$templateName);
        $this->template->render();
    }

    protected function createComponentGrid() : DataGrid
    {
        $grid = new DataGrid();
        $grid->setTranslator($this->translator);
        $grid->setItemsPerPageList([20, 40, 60, 80], false);

        $grid->setDataSource($this->setupDataSource());

        $this->setupColumns($grid);

        $this->setupFilter($grid);

        $this->setupActions($grid);

        $this->setupInlineEdit($grid);

        if ($this->isAdderEnabled) {
            $grid->addInlineAdd()->setPositionTop(true)->onControlAdd[] = [$this, 'setupAdder'];
            $grid->getInlineAdd()->onSubmit[] = [$this, 'processAdderWrapper'];
        }

        return $grid;
    }

    abstract protected function setupDataSource() : QueryBuilder;

    abstract protected function setupColumns(DataGrid $grid);

    protected function setupFilter(DataGrid $grid)
    {
        // optional
    }

    protected function setupActions(DataGrid $grid)
    {
        // optional
    }

    protected function setupInlineEdit(DataGrid $grid)
    {
        // optional
    }

    protected function enableAdder()
    {
        $this->isAdderEnabled = true;
    }

    public function setupAdder(Container $container)
    {
        throw new NotImplementedException();
    }

    public function processAdderWrapper($values)
    {
        try {
            $this->processAdder($values);
        } catch (NullValueException $e) {
            $this->presenter->flashMessage('Inserted value cannot be empty.', FlashMessage::ERROR);
        } catch (Throwable $e) {
            Debugger::log($e);
            $this->presenter->flashMessage('Adding a new record failed.', FlashMessage::ERROR);
        } finally {
            $this->redrawFlashMessages();
            $this->getGrid()->redrawControl();
        }
    }

    protected function processAdder($values)
    {
        throw new NotImplementedException();
    }

    protected function getGrid() : DataGrid
    {
        return $this['grid'];
    }

    protected function redrawFlashMessages()
    {
        if ($this->presenter->isAjax()) {
            $this->presenter->redrawControl('flashMessages');
        } else {
            $this->redirect('this');
        }
    }

    protected function redrawGrid()
    {
        if ($this->presenter->isAjax()) {
            $this->getGrid()->redrawControl();
        } else {
            $this->presenter->redirect('this');
        }
    }

    protected function redrawRow(int $id)
    {
        if ($this->presenter->isAjax()) {
            $this->getGrid()->redrawItem($id);
        } else {
            $this->presenter->redirect('this');
        }
    }

    protected function columnIdPreset(DataGrid $grid)
    {
        $grid->addColumnNumber('id', 'Id', 'id')
            ->setSortable()
            ->setSortableResetPagination()
            ->getElementPrototype('th')->class('fit-column no-break');
    }

    protected function columnNamePreset(DataGrid $grid, $edit = false)
    {
        $grid->addColumnText('name', 'Name', 'name')->setSortable()->sortableResetPagination();

        if ($edit) {
            $grid->getColumn('name')
                ->setEditableCallback([$this, 'editNameWrapper'])
                ->setEditableInputType('text', ['class' => 'form-control input-sm']);
        }
    }

    public function editNameWrapper(int $id, string $name)
    {
        try {
            $this->editName($id, $name);
        } catch (NullValueException $e) {
            $this->presenter->flashMessage('Name cannot be empty string.', FlashMessage::ERROR);
        } catch (Throwable $e) {
            Debugger::log($e);
            $this->presenter->flashMessage('Name edit failed.', FlashMessage::ERROR);
        } finally {
            $this->redrawFlashMessages();
            $this->redrawRow($id);
        }
    }

    protected function editName(int $id, string $name)
    {
        throw new NotImplementedException();
    }

    protected function columnStatusPreset(DataGrid $grid)
    {
        $grid->addColumnStatus('status', 'Status', 'state')->setSortable()->setSortableResetPagination()
            ->setCaret(true)
            ->addOption(ICommonRecord::CREATED, 'Created')
                ->setClass('btn-primary')
                ->endOption()
            ->addOption(ICommonRecord::ACTIVE, 'Active')
                ->setClass('btn-success')
                ->endOption()
            ->addOption(ICommonRecord::TRASH, 'Trash')
                ->setClass('btn-danger')
                ->endOption()
            ->addOption(ICommonRecord::DELETED, 'Deleted')
                ->setClass('btn-danger')
                ->endOption()
            ->onChange[] = [$this, 'statusChangeWrapper'];

        $grid->getColumn('status')->getElementPrototype('th')->class('fit-column no-break');
    }

    public function statusChangeWrapper(int $id, string $status)
    {
        try {
            $this->statusChange($id, $status);
        } catch (Throwable $e) {
            Debugger::log($e);
            $this->getPresenter()->flashMessage('Status editing has failed.', FlashMessage::ERROR);
        } finally {
            $this->redrawFlashMessages();
            $this->redrawRow($id);
        }
    }

    protected function statusChange(int $id, string $status)
    {
        throw new NotImplementedException();
    }
}