<?php

namespace kernel;

use Illuminate\Database\Eloquent\Collection;

class CollectionTableRenderer
{
    protected \Illuminate\Database\Eloquent\Collection $collection;
    protected array $columns;
    protected array $tableAttributes;
    protected array $customColumns = [];
    protected array $valueProcessors = [];

    protected array $filters = [];
    protected bool $filterRowEnabled = false;
    protected array $filterRowAttributes = [
        'class' => 'filter-row'
    ];
    
    /**
     * Конструктор класса
     * 
     * @param Collection $collection
     */
    public function __construct(Collection $collection)
    {
        $this->collection = $collection;
        $this->columns = [];
        $this->customColumns = [];
        $this->valueProcessors = [];
        $this->filters = [];
        $this->tableAttributes = [
            'class' => 'table table-bordered table-striped',
            'id' => 'dataTable'
        ];
    }
    
    /**
     * Установка столбцов для отображения
     * 
     * @param array $columns Массив столбцов в формате ['field' => 'Заголовок']
     * @return $this
     */
    public function setColumns(array $columns): static
    {
        $this->columns = $columns;

        return $this;
    }

    /**
     * Добавление кастомной колонки
     *
     * @param string $columnName Название колонки (ключ)
     * @param string $title Заголовок колонки
     * @param callable $callback Функция для генерации содержимого
     * @return $this
     */
    public function addCustomColumn(string $columnName, string $title, callable $callback): static
    {
        $this->customColumns[$columnName] = [
            'title' => $title,
            'callback' => $callback
        ];

        return $this;
    }

    /**
     * Добавление обработчика значения для колонки
     *
     * @param string $columnName Название колонки
     * @param callable $processor Функция обработки значения
     * @return $this
     */
    public function addValueProcessor(string $columnName, callable $processor): static
    {
        $this->valueProcessors[$columnName] = $processor;

        return $this;
    }

    /**
     * Добавление фильтра для колонки
     *
     * @param string $columnName Название колонки
     * @param string $filterHtml HTML-код фильтра
     * @return $this
     */
    public function addFilter(string $columnName, string $filterHtml): static
    {
        $this->filters[$columnName] = $filterHtml;
        $this->filterRowEnabled = true;

        return $this;
    }

    /**
     * Включение/отключение строки фильтров
     *
     * @param bool $enabled
     * @return $this
     */
    public function enableFilterRow(bool $enabled = true): static
    {
        $this->filterRowEnabled = $enabled;

        return $this;
    }

    /**
     * Установка атрибутов строки фильтров
     *
     * @param array $attributes
     * @return $this
     */
    public function setFilterRowAttributes(array $attributes): static
    {
        $this->filterRowAttributes = array_merge($this->filterRowAttributes, $attributes);

        return $this;
    }
    
    /**
     * Установка атрибутов для таблицы
     * 
     * @param array $attributes Массив атрибутов HTML
     * @return $this
     */
    public function setTableAttributes(array $attributes): static
    {
        $this->tableAttributes = array_merge($this->tableAttributes, $attributes);

        return $this;
    }

    /**
     * Получить HTML-код таблицы (без вывода)
     *
     * @return string
     */
    public function getHtml(): string
    {
        if (empty($this->columns) && empty($this->customColumns) && $this->collection->isNotEmpty()) {
            // Автоматически определяем столбцы на основе первой модели
            $firstItem = $this->collection->first();
            $this->columns = array_combine(
                array_keys($firstItem->toArray()),
                array_map('ucfirst', array_keys($firstItem->toArray()))
            );
        }

        $html = '<table ' . $this->buildAttributes($this->tableAttributes) . '>';
        $html .= $this->renderHeader();
        if ($this->filterRowEnabled) {
            $html .= $this->renderFilterRow();
        }
        $html .= $this->renderBody();
        $html .= '</table>';

        return $html;
    }

    
    /**
     * Генерация HTML-таблицы
     * 
     * @return string
     */
    public function render(): void
    {
        echo $this->getHtml();
    }

    public function fetch(): string
    {
        return $this->getHtml();
    }
    
    /**
     * Генерация заголовка таблицы
     * 
     * @return string
     */
    protected function renderHeader(): string
    {
        $html = '<thead><tr>';

        // Обычные колонки
        foreach ($this->columns as $title) {
            $html .= '<th>' . htmlspecialchars($title) . '</th>';
        }

        // Кастомные колонки
        foreach ($this->customColumns as $column) {
            $html .= '<th>' . htmlspecialchars($column['title']) . '</th>';
        }

        $html .= '</tr></thead>';

        return $html;
    }

    /**
     * Генерация строки фильтров
     *
     * @return string
     */
    protected function renderFilterRow(): string
    {
        $html = '<tr ' . $this->buildAttributes($this->filterRowAttributes) . '>';

        // Обычные колонки
        foreach (array_keys($this->columns) as $field) {
            $html .= '<td>';
            if (isset($this->filters[$field])) {
                $html .= $this->filters[$field];
            } else {
                $html .= '&nbsp;';
            }
            $html .= '</td>';
        }

        // Кастомные колонки
        foreach (array_keys($this->customColumns) as $columnName) {
            $html .= '<td>';
            if (isset($this->filters[$columnName])) {
                $html .= $this->filters[$columnName];
            } else {
                $html .= '&nbsp;';
            }
            $html .= '</td>';
        }

        $html .= '</tr>';

        return $html;
    }
    
    /**
     * Генерация тела таблицы
     * 
     * @return string
     */
    protected function renderBody(): string
    {
        $html = '<tbody>';

        foreach ($this->collection as $item) {
            $html .= '<tr>';

            // Обычные колонки
            foreach (array_keys($this->columns) as $field) {
                $value = $this->getValue($item, $field);
                $value = $this->processValue($field, $value, $item);
                $html .= '<td>' . $value . '</td>';
            }

            // Кастомные колонки
            foreach ($this->customColumns as $columnName => $column) {
                $value = call_user_func($column['callback'], $item, $columnName);
                $html .= '<td>' . $value . '</td>';
            }

            $html .= '</tr>';
        }

        $html .= '</tbody>';

        return $html;
    }

    /**
     * Обработка значения ячейки
     *
     * @param string $field Название поля
     * @param mixed $value Значение
     * @param mixed $item Весь элемент коллекции
     * @return mixed
     */
    protected function processValue(string $field, mixed $value, mixed $item): mixed
    {
        // Если есть обработчик для этого поля - применяем его
        if (isset($this->valueProcessors[$field])) {
            $value = call_user_func($this->valueProcessors[$field], $value, $item, $field);
        }

        // Если значение не прошло обработку - экранируем его
//        if (is_string($value)) {
//            return htmlspecialchars($value);
//        }

        return $value;
    }
    
    /**
     * Получение значения из элемента коллекции
     * 
     * @param mixed $item
     * @param string $field
     * @return mixed
     */
    protected function getValue(mixed $item, string $field): mixed
    {
        // Поддержка dot-notation для отношений
        if (str_contains($field, '.')) {
            return data_get($item, $field);
        }
        
        // Поддержка accessor'ов модели
        if (method_exists($item, 'get' . ucfirst($field) . 'Attribute')) {
            return $item->{$field};
        }
        
        return $item->{$field} ?? null;
    }
    
    /**
     * Сборка HTML-атрибутов
     * 
     * @param array $attributes
     * @return string
     */
    protected function buildAttributes(array $attributes): string
    {
        $result = [];
        
        foreach ($attributes as $key => $value) {
            $result[] = $key . '="' . htmlspecialchars($value) . '"';
        }
        
        return implode(' ', $result);
    }
    
    /**
     * Магический метод для преобразования в строку
     * 
     * @return string
     */
    public function __toString()
    {
        return $this->render();
    }
}