<?php

namespace apexl\Io\modules\display\components;

use apexl\Config\Singleton;
use apexl\Io\includes\Entity;
use apexl\Io\includes\Routes;
use apexl\Io\modules\component\entities\componentEntity;
use Closure;

class EntityDisplayTable extends componentEntity
{
    protected $config;
    protected $dataSrc;
    protected $filters;
    protected Entity $entity;
    protected $hideDisabled = false;
    protected $dropMenu = null;

    public function __construct(Entity $entity = null, $replacements = [])
    {
        parent::__construct();
        $this->config = Singleton::getInstance();

        $this->setName("DataTable");
        if ($entity !== null) {
            $this->setEntity($entity, $replacements);

            $hideCols = array_filter(
                $entity->getFieldConfig(),
                fn ($config) => $config['table'] ?? null === 'hide'
            );

            if ($hideCols) {
                $this->hideCols(array_keys($hideCols));
            }
        }
        $this->displayPagination();
    }

    public function setEntity(Entity $entity, $replacements = [])
    {
        $this->entity = $entity;
        $this->dataSrc = Routes::getRoutePattern($entity->getEntityName().'.get', $replacements);
        $this->src($this->dataSrc);
        return $this;
    }

    public function setFilters($filters)
    {
        $this->filters = $filters;
        $this->src($this->dataSrc);
        return $this;
    }

    public function showCols(array $cols): EntityDisplayTable
    {
        $this->addProperty('showCols', $cols);
        return $this;
    }

    public function hideCols(array $cols): EntityDisplayTable
    {
        $this->addProperty('hideCols', $cols);
        return $this;
    }

    /**
     * @param $display
     * @param $displayTop
     * @param $displayBottom
     * @param $location ('inside','outside')
     * @return $this
     */
    public function displayPagination($display = true, $displayTop=true, $displayBottom=true, $location='outside')
    {
        $this->addProperty('showPagination', $display);
        $this->addProperty('showPaginationTop', $displayTop);
        $this->addProperty('showPaginationBottom', $displayBottom);
        $this->addProperty('paginationLocation', $location);
        return $this;
    }

    public function setItemsPerPage($numItems)
    {
        $this->addProperty('itemsPerPage', $numItems);
        return $this;
    }

    public function displayShortTotals($display = true)
    {
        $this->addProperty('displayShortTotals', $display);
        return $this;
    }

    public function colspans($colspans = [])
    {
        $this->addProperty('colspans', $colspans);
        return $this;
    }

    public function hideIdColumn($display = true)
    {
        $this->addProperty('hideIdColumn', $display);
        return $this;
    }

    public function src($dataSrc)
    {
        $this->dataSrc = $dataSrc;
        $src = $this->dataSrc ?? '';

        $firstSeparator = strpos($dataSrc, '?') !== false ? '&' : '?';

        if ($this->filters) {
            $src .= $firstSeparator.ltrim($this->filters, '?').'&asTable=1';
        } else {
            $src .= $firstSeparator.'asTable=1';
        }
        $this->addProperty('dataSrc', $src);
        return $this;
    }

    /**
     * @return $this
     */
    public function addCommonButtons($buttonFormat='buttons'): EntityDisplayTable
    {
        $buttons = [
            'get' => Closure::fromCallable(function ($routeName) : self {
                return $this->addViewButton($routeName);
            }),
            'put' => Closure::fromCallable(function ($routeName) : self {
                return $this->addEditButton($routeName);
            }),
            'delete' => Closure::fromCallable(function ($routeName) : self {
                return $this->addDeleteButton($routeName);
            }),
        ];

        $entityName = $this->entity->getEntityName();

        foreach ($buttons as $routeAppend => $callable) {
            $routeName = sprintf('%s.display.%s', $entityName, $routeAppend);
            if (Routes::getRoutePattern($routeName . '.override')) {
                $callable($routeName . '.override');
            } elseif (Routes::getRoutePattern($routeName)) {
                $callable($routeName);
            }
        }

        return $this;
    }

    public function addCommonButtonsAsDropdown()
    {
        if (!$this->dropMenu) {
            $this->dropMenu = new DropMenu();
            $this->addComponent($this->dropMenu);
        }
        $menuItems = [];
        foreach ([   'get' => ['title' => 'View', 'class' => 'dropmenu-item-view'],
                     'put' => ['title' => 'Edit', 'class' => 'dropmenu-item-edit'],
                     'delete' => ['title' => 'Delete', 'class' => 'dropmenu-item-delete']
                 ] as $routeAppend => $config) {
            $routeName = "{$this->entity->getEntityName()}.display.$routeAppend";

            $path = Routes::getRoutePattern($routeName . '.override', [], false);
            if ($route = ($path ? $path : Routes::getRoutePattern($routeName, [], false))) {
                if (!$this->getAccessFromRoute($routeName)) {
                $menuItems[] = (object)[
                    'route' => $route,
                    'title' => $config['title'],
                    'class' => $config['class']
                ];
            }
        }
        }
        $this->dropMenu->addMenuItems($menuItems);
        //$this->addComponent($this->dropMenu);

        return $this;
    }

    public function addDropmenuComponent($component)
    {
        if (!$this->dropMenu) {
            $this->dropMenu = new DropMenu();
            $this->addComponent($this->dropMenu);
        }
        $this->dropMenu->addComponent($component);

        return $this;
    }

    public function setDropmenuItems($menuItems)
    {
        if (!$this->dropMenu) {
            $this->dropMenu = new DropMenu();
            $this->addComponent($this->dropMenu);
        }
        $this->dropMenu->addMenuItems($menuItems);

        return $this;
    }

    public function addDropMenuModal($title, $modalTitle, $modalComponents, $contentSrc='', $conditional=null)
    {
        $this->addDropmenuComponent(
            (new Modal())
                ->setTitle($modalTitle)
                ->setButtonContent($title)
                ->setButtonClass('')
                ->setContentSrc($contentSrc)
                ->addComponents($modalComponents)
                ->setRowConditional($conditional)
        );

        return $this;
    }

    public function addDropMenuItem($routeName, $title, $cssClass='', $method='get', $target=null, $isApiRoute=false, $dynamicPdf=false, $replaceParameters=[])
    {
        $menuItem = (object)[
            'route' => ($isApiRoute ? rtrim($this->config->app->site->backend_domain, "/") : '') . Routes::getRoutePattern($routeName, [], false),
            'title' => $title,
            'class' => $cssClass
        ];
        foreach ($replaceParameters as $find => $replace) {
            $menuItem->route = str_replace("{".$find."}", "{".$replace."}", $menuItem->route);
        }
        if ($method !== 'get') $menuItem->method = $method;
        if ($target) $menuItem->target = $target;
        if ($dynamicPdf) $menuItem->dynamicPdf = true;

        $this->setDropmenuItems([$menuItem]);

        return $this;
    }

    /**
     * @param $routeName
     * @return $this
     */
    public function addViewButton($routeName): EntityDisplayTable
    {
        $this->addButton($routeName, 'eye', $this->getAccessFromRoute($routeName));
        return $this;
    }

    /**
     * @param $routeName
     * @return $this
     */
    public function addEditButton($routeName): EntityDisplayTable
    {
        $this->addButton($routeName, 'edit', $this->getAccessFromRoute($routeName));
        return $this;
    }

    /**
     * @param $routeName
     * @return $this
     */
    public function addDeleteButton($routeName): EntityDisplayTable
    {
        $this->addButton($routeName, 'trash', $this->getAccessFromRoute($routeName));
        return $this;
    }

    /**
     * @param $routeName
     * @param array $replacements
     * @param string $icon
     * @param false $disabled
     * @return $this
     */
    public function addPostButton($routeName, array $replacements = [], string $icon ='', bool $disabled = null): EntityDisplayTable
    {
        if (is_null($disabled)) {
            $disabled = $this->getAccessFromRoute($routeName);
        }
        $button = (new TableButton())
            ->addRoute(Routes::getRoutePattern($routeName, $replacements, false))
            ->addIcon($icon)
            ->isPost()
            ->addClass($this->entity->getEntityTrueName().'_table__btn')
            ->addClass($this->entity->getEntityTrueName().'_table__btn-'.$icon)
            ->addClass('table__btn')
            ->addClass('table__btn-'.$icon);
        if ($disabled) {
            $button->addRoute('#');
            $button->addClass($this->entity->getEntityTrueName() . '_table__btn-disabled');
            $button->addClass('table__btn-disabled');
            $button->disabled($disabled);
            if ($this->hideDisabled) {
                //do not add the button to the component
                return $this;
            }
        }
        $this->addComponent($button);
        return $this;
    }

    /**
     * @param $routeName
     * @param string $icon
     * @param false $disabled
     * @return $this
     */
    public function addButton($routeName, string $icon ='', bool $disabled = false, bool $openInNewWindow = false): EntityDisplayTable
    {
        $entityTrueName = isset($this->entity) && $this->entity ? $this->entity->getEntityTrueName() : '';

        $button = (new TableButton())
            ->addRoute(Routes::getRoutePattern($routeName, [], false))
            ->addIcon($icon)
            ->addClass($entityTrueName.'_table__btn')
            ->addClass($entityTrueName.'_table__btn-'.$icon)
            ->addClass('table__btn')
            ->addClass('table__btn-'.$icon);
        if ($openInNewWindow) {
            $button->openInNewWindow();
        }
        if ($disabled) {
            $button->addRoute('#');
            $button->addClass($entityTrueName . '_table__btn-disabled');
            $button->addClass('table__btn-disabled');
            $button->disabled($disabled);
            if ($this->hideDisabled) {
                //do not add the button to the component
                return $this;
            }
        }
        $this->addComponent($button);
        return $this;
    }

    /**
     * @param bool $value
     * @return $this
     */
    public function hideDisabled(bool $value = true): EntityDisplayTable
    {
        $this->hideDisabled = $value;
        return $this;
    }

    /**
     * @param $routeName
     * @return bool
     */
    protected function getAccessFromRoute($routeName): bool
    {
        $route = Routes::getRoute($routeName);
        if (($route ?? false) && $permission = $route->getArgs('permission')) {
            //we need to pass the opposite here as disabled is false if access it true.
            return !$this->currentUser->isAllowed($permission);
        }
        //no permission? just return true.
        return (bool) $route; //no route? return false.
    }
}
