<?php

namespace PHPNative\Ui\Widget;

use PHPNative\Framework\TextRenderer;

class Table extends Container
{
    private array $columns = [];
    private array $rows = [];
    private Container $headerContainer;
    private Container $bodyContainer;
    private null|int $selectedRowIndex = null;
    private $onRowSelect = null;
    private null|string $sortColumn = null;
    private bool $sortAscending = true;

    public function __construct(string $style = '')
    {
        // Table selbst ist kein Flex-Container; sie wird als Kind
        // in einem flex-Layout benutzt (z.B. flex-1), aber intern
        // werden Header und Body ganz normal vertikal gestapelt.
        parent::__construct('w-full' . $style);

        // Create header container
        $this->headerContainer = new Container('flex flex-row w-full bg-gray-200 border-b-2 border-gray-400');
        $this->addComponent($this->headerContainer);

        // Create body container (scrollable)
        // Use flex-1 to fill available space, overflow-auto to enable scrolling
        $this->bodyContainer = new Container('flex flex-col w-full overflow-auto flex-1');
        $this->addComponent($this->bodyContainer);
    }

    public function layout(null|TextRenderer $textRenderer = null): void
    {
        $this->headerContainer->layout($textRenderer);
        $this->bodyContainer->layout($textRenderer);

        parent::layout($textRenderer);
    }

    /**
     * Define columns
     *
     * @param array $columns Array of column definitions ['key' => string, 'title' => string, 'width' => int|null]
     */
    public function setColumns(array $columns): void
    {
        $this->columns = $columns;
        $this->headerContainer->clearChildren();

        foreach ($columns as $column) {
            $key = $column['key'];
            $title = $column['title'] ?? $key;
            $width = $column['width'] ?? null;

            $style = 'w-full px-4 py-2 text-black font-bold border-r border-gray-300 hover:bg-gray-300 cursor-pointer';
            if ($width) {
                $style .= ' w-' . ((int) ($width / 4));
            } else {
                $style .= ' flex-1';
            }

            $headerLabel = new ClickableHeaderLabel($key, $this, $title, $style);
            $this->headerContainer->addComponent($headerLabel);
        }
    }

    /**
     * Set table data
     *
     * @param array $data Array of row data (associative arrays)
     * @param bool  $preserveScroll Whether to preserve scroll position
     */
    public function setData(array $data, bool $preserveScroll = false): void
    {
        $this->rows = $data;

        $scrollPosition = null;
        if ($preserveScroll) {
            $scrollPosition = $this->bodyContainer->getScrollPosition();
        }

        $this->bodyContainer->clearChildren();

        foreach ($data as $rowIndex => $row) {
            $this->addRow($row, $rowIndex);
        }

        if ($preserveScroll && $scrollPosition !== null) {
            $this->bodyContainer->setScrollPosition($scrollPosition['x'], $scrollPosition['y']);
        }
    }

    /**
     * Add a single row
     */
    private function addRow(array $rowData, int $rowIndex): void
    {
        $isSelected = $rowIndex === $this->selectedRowIndex;
        $rowStyle = 'flex flex-row border-b border-gray-200 hover:bg-gray-400';
        if ($isSelected) {
            $rowStyle .= ' bg-blue-100';
        }

        $rowContainer = new Container($rowStyle);

        // Check if any column has custom render (interactive components)
        $hasCustomRender = false;
        foreach ($this->columns as $column) {
            if (isset($column['render']) && is_callable($column['render'])) {
                $hasCustomRender = true;
                break;
            }
        }

        // Enable texture caching for table rows only if no custom render
        // Custom renders may contain interactive components (buttons) that need event handling
        if (!$hasCustomRender) {
            $rowContainer->setUseTextureCache(true);
        }

        foreach ($this->columns as $column) {
            $key = $column['key'];
            $value = $rowData[$key] ?? '';
            $width = $column['width'] ?? null;

            $cellStyle = 'w-full px-4 py-2 text-black border-r border-gray-300';
            if ($width) {
                $cellStyle .= ' w-' . ((int) ($width / 4));
            } else {
                $cellStyle .= ' flex-1';
            }

            // Check if column has custom render function
            if (isset($column['render']) && is_callable($column['render'])) {
                // Wrap custom cell content in a container so that
                // width/padding styles are applied consistently.
                $cellContent = $column['render']($rowData, $rowIndex);
                $cellContainer = new Container($cellStyle);
                $cellContainer->addComponent($cellContent);
                $rowContainer->addComponent($cellContainer);
            } else {
                $cellLabel = new Label((string) $value, $cellStyle);
                $rowContainer->addComponent($cellLabel);
            }
        }

        // Make row clickable
        $clickHandler = new class($rowContainer, $rowIndex, $this) extends Container {
            private int $rowIndex;
            private Table $table;

            public function __construct(Container $rowContainer, int $rowIndex, Table $table)
            {
                $this->rowIndex = $rowIndex;
                $this->table = $table;
                parent::__construct('');
                $this->addComponent($rowContainer);
            }

            public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
            {
                // First, let children (like buttons) handle the click
                $handled = parent::handleMouseClick($mouseX, $mouseY, $button);
                if ($handled) {
                    return true;
                }

                // If no child handled it, check if click is within row bounds
                if (
                    $mouseX >= $this->viewport->x &&
                        $mouseX <= ($this->viewport->x + $this->viewport->width) &&
                        $mouseY >= $this->viewport->y &&
                        $mouseY <= ($this->viewport->y + $this->viewport->height)
                ) {
                    $this->table->selectRow($this->rowIndex);
                    return true;
                }

                return false;
            }
        };

        $this->bodyContainer->addComponent($clickHandler);
    }

    /**
     * Sort by column
     */
    public function sortByColumn(string $columnKey): void
    {
        // Toggle sort direction if clicking same column
        if ($this->sortColumn === $columnKey) {
            $this->sortAscending = !$this->sortAscending;
        } else {
            $this->sortColumn = $columnKey;
            $this->sortAscending = true;
        }

        // Sort the data
        $sortedRows = $this->rows;
        usort($sortedRows, function ($a, $b) use ($columnKey) {
            $aVal = $a[$columnKey] ?? '';
            $bVal = $b[$columnKey] ?? '';

            // Natural sort for strings/numbers
            $result = strnatcasecmp((string) $aVal, (string) $bVal);

            return $this->sortAscending ? $result : -$result;
        });

        // Re-render with sorted data (reset scroll)
        $this->setData($sortedRows, false);
    }

    /**
     * Select a row
     */
    public function selectRow(int $rowIndex): void
    {
        // Remember current scroll position of the body so that selection
        // does not reset scrolling back to the top.
        $scrollPosition = $this->bodyContainer->getScrollPosition();

        $this->selectedRowIndex = $rowIndex;

        // Trigger callback
        if ($this->onRowSelect !== null) {
            ($this->onRowSelect)($rowIndex, $this->rows[$rowIndex] ?? null);
        }

        // Re-render rows to update selection but keep scroll
        $this->setData($this->rows, true);

        // Restore scroll position
        $this->bodyContainer->setScrollPosition($scrollPosition['x'], $scrollPosition['y']);
    }

    /**
     * Set row select callback
     */
    public function setOnRowSelect(callable $callback): void
    {
        $this->onRowSelect = $callback;
    }

    /**
     * Get selected row index
     */
    public function getSelectedRowIndex(): null|int
    {
        return $this->selectedRowIndex;
    }

    /**
     * Get selected row data
     */
    public function getSelectedRow(): null|array
    {
        return $this->selectedRowIndex !== null ? ($this->rows[$this->selectedRowIndex] ?? null) : null;
    }
}
