<?php

namespace Win7\Application\Manager;

use Win7\Application\Common\Message;
use Win7\Application\Exception\UserException;
use Win7\Application\Manager\DataTable\Column;
use Win7\Application\Manager\DataTable\ColumnDefinition;
use Win7\Application\Manager\DataTable\CustomColumnResult;

/**
 * @author Thiago Daher
 */
abstract class TableManager extends Manager
{

    /**
     * @var \Win7\Application\Manager\FilterParams
     */
    private FilterParams $params;

    /**
     * @param \Win7\Application\Manager\FilterParams $params
     * @return array
     * @throws \ReflectionException
     * @throws \Win7\Application\Exception\UserException
     */
    public function getTableList(FilterParams $params): array
    {
        $this->setParams($params);
        $definitions = $this->prepareColumns($params);
        $list        = $this->getRepository()->getTableList($params);
        $count       = $this->getRepository()->getTableCount($params);
        $data        = [];
        $columns     = $params->getColumns();

        foreach ($list->toArray() as $values) {
            $content        = [];
            $filteredValues = [];

            foreach ($values as $key => $value) {
                if (strpos($key, 'column') === false) {
                    $filteredValues[$key] = $value;
                    continue;
                }

                $column = $columns[(int)explode('_', $key)[1]];

                if (!isset($column)) {
                    continue;
                }

                $filteredValues[$column->getName()] = $value;
            }

            foreach ($values as $key => $value) {
                if (strpos($key, 'column') === false) {
                    continue;
                }

                $column = $columns[(int)explode('_', $key)[1]];

                if (!isset($column)) {
                    continue;
                }

                $definition = $definitions[$column->getName()];

                if ($callback = $definition->getCallback()) {
                    $value = $callback($value, $filteredValues);
                }

                $this->fillContent($content, $value, $definition, $definitions);
            }

            foreach ($params->getCustomColumns() as $column) {
                $definition = $definitions[$column->getName()];
                $value      = '';

                if ($callback = $definition->getCallback()) {
                    $value = $callback($filteredValues);
                }

                $this->fillContent($content, $value, $definition, $definitions);
            }

            $data[] = $content;
        }

        return [
            'draw'            => $params->getDraw(),
            'recordsTotal'    => $count,
            'recordsFiltered' => $count,
            'data'            => $data,
        ];
    }

    /**
     * @param \Win7\Application\Manager\FilterParams $params
     * @return array
     * @throws \ReflectionException
     * @throws \Win7\Application\Exception\UserException
     */
    public function getAllData(FilterParams $params): array
    {
        $params->setColumns($this->getAllColumnsFromDefinition());
        $params->setOrder([]);
        $params->setLimit(-1);

        return $this->getTableList($params);
    }

    /**
     * @param array $content
     * @param mixed $value
     * @param \Win7\Application\Manager\DataTable\ColumnDefinition $definition
     * @param \Win7\Application\Manager\DataTable\ColumnDefinition[] $definitions
     * @return void
     */
    private function fillContent(array &$content, $value, ColumnDefinition $definition, array $definitions)
    {
        if (!$value instanceof CustomColumnResult) {
            $content[$definition->getPosition()] = $value;

            return;
        }

        foreach ($value->getExtrasValues() as $name => $extraValue) {
            if (!isset($definitions[$name])) {
                continue;
            }

            $definition                          = $definitions[$name];
            $content[$definition->getPosition()] = $value;
        }
    }

    /**
     * @param \Win7\Application\Manager\FilterParams $params
     * @return \Win7\Application\Manager\DataTable\ColumnDefinition[]
     * @throws \Win7\Application\Exception\UserException
     */
    private function prepareColumns(FilterParams $params): array
    {
        $columns     = $params->getColumns();
        $definitions = $this->getColumnDefinitions();

        foreach ($columns as $column) {
            if (!isset($definitions[$column->getName()])) {
                throw new UserException(Message::COLUNA_INVALIDA($column->getName()));
            }

            $definition = $definitions[$column->getName()];

            if (!$definition->isOrderable()) {
                $column->setOrder('');
            }

            if (!$definition->isSearchable()) {
                $column->setSearch('');
            }

            $column->setSearchType($definition->getSearchType());
            $column->setDefinition($definition);
            $definition->setPosition($column->getPosition());
        }

        return $definitions;
    }

    /**
     * @return array
     */
    public function getAllColumnsFromDefinition(): array
    {
        $columns = [];

        foreach ($this->getColumnDefinitions() as $name => $definition) {
            $column = new Column();
            $column->setOrder('');
            $column->setSearch('');
            $column->setSearchType($definition->getSearchType());
            $column->setDefinition($definition);
            $column->setPosition($definition->getPosition());
            $column->setName($name);
            $columns[] = $column;
        }

        return $columns;
    }

    /**
     * @return \Win7\Application\Manager\FilterParams
     */
    public function getParams(): FilterParams
    {
        return $this->params;
    }

    /**
     * @param \Win7\Application\Manager\FilterParams $params
     */
    public function setParams(FilterParams $params): void
    {
        $this->params = $params;
    }

    /**
     * @return \Win7\Application\Manager\DataTable\ColumnDefinition[]
     */
    abstract protected function getColumnDefinitions(): array;
}