<?php

namespace PHPNative\Ui\Widget;

use PHPNative\Framework\TextRenderer;
use PHPNative\Ui\Viewport;

class VirtualListView extends Container
{
    private array $columns = [];
    private array $rows = [];
    private float $rowHeight = 0.0;
    private float $scrollY = 0.0;
    private int $firstVisibleRow = 0;
    private int $lastVisibleRow = -1;
    private null|int $selectedRowIndex = null;
    private $onRowSelect = null;

    private const SCROLLBAR_WIDTH = 16;
    private const SCROLLBAR_MIN_SIZE = 20;
    private const VISIBLE_BUFFER = 5;

    public function __construct(string $style = '')
    {
        parent::__construct('w-full ' . $style);
    }

    public function setColumns(array $columns): void
    {
        $this->columns = $columns;
        $this->markDirty(true);
    }

    public function setData(array $rows): void
    {
        $this->rows = array_values($rows);
        $this->scrollY = 0.0;
        $this->firstVisibleRow = 0;
        $this->lastVisibleRow = -1;
        $this->selectedRowIndex = null;
        $this->markDirty(true);
    }

    /**
     * Update data without resetting scroll position
     * Useful for updating row data while preserving user's scroll position
     */
    public function updateData(array $rows): void
    {
        $this->rows = array_values($rows);
        $this->markDirty(true);
    }

    public function setOnRowSelect(callable $callback): void
    {
        $this->onRowSelect = $callback;
    }

    public function getSelectedRow(): null|array
    {
        if ($this->selectedRowIndex === null) {
            return null;
        }

        return $this->rows[$this->selectedRowIndex] ?? null;
    }

    public function layout(null|TextRenderer $textRenderer = null): void
    {
        parent::layout($textRenderer);

        if (empty($this->rows) || $textRenderer === null || !$textRenderer->isInitialized()) {
            $this->clearChildren();
            return;
        }

        if ($this->rowHeight <= 0) {
            $this->rowHeight = $this->measureRowHeight($textRenderer);
        }

        $maxScroll = $this->getMaxScroll();
        $this->scrollY = max(0.0, min($this->scrollY, $maxScroll));

        $this->updateVisibleRows($textRenderer);
    }

    private function measureRowHeight(TextRenderer $textRenderer): float
    {
        if (empty($this->rows)) {
            return 24.0;
        }

        $testRow = $this->buildRowContainer($this->rows[0], 0);

        $viewport = new Viewport(
            x: $this->contentViewport->x,
            y: $this->contentViewport->y,
            width: $this->contentViewport->width,
            height: $this->contentViewport->height,
            windowWidth: $this->contentViewport->windowWidth,
            windowHeight: $this->contentViewport->windowHeight,
            uiScale: $this->contentViewport->uiScale,
        );
        $testRow->setViewport($viewport);
        $testRow->setContentViewport(clone $viewport);
        $testRow->layout($textRenderer);

        $rowViewport = $testRow->getViewport();
        $height = max(1.0, (float) $rowViewport->height);

        return $height;
    }

    private function updateVisibleRows(null|TextRenderer $textRenderer): void
    {
        $this->clearChildren();

        if ($this->rowHeight <= 0 || empty($this->rows) || $textRenderer === null) {
            return;
        }

        $viewportHeight = $this->contentViewport->height;
        if ($viewportHeight <= 0) {
            return;
        }

        $rowCount = count($this->rows);
        $first = (int) floor($this->scrollY / $this->rowHeight);
        $visibleCount = ((int) ceil($viewportHeight / $this->rowHeight)) + self::VISIBLE_BUFFER;
        $last = min($rowCount - 1, ($first + $visibleCount) - 1);

        $this->firstVisibleRow = $first;
        $this->lastVisibleRow = $last;

        for ($i = $first; $i <= $last; $i++) {
            $rowData = $this->rows[$i];
            $rowContainer = $this->buildRowContainer($rowData, $i);

            $rowY = ($this->contentViewport->y + ($i * $this->rowHeight)) - $this->scrollY;

            $rowViewport = new Viewport(
                x: $this->contentViewport->x,
                y: (int) $rowY,
                width: $this->contentViewport->width,
                height: $this->rowHeight,
                windowWidth: $this->contentViewport->windowWidth,
                windowHeight: $this->contentViewport->windowHeight,
                uiScale: $this->contentViewport->uiScale,
            );

            $rowContainer->setViewport($rowViewport);
            $rowContainer->setContentViewport(clone $rowViewport);
            $rowContainer->layout($textRenderer);

            $this->addComponent($rowContainer);
        }
    }

    private function buildRowContainer(array $rowData, int $rowIndex): Container
    {
        $isSelected = $this->selectedRowIndex !== null && $rowIndex === $this->selectedRowIndex;
        $rowStyle = 'flex flex-row border-b border-gray-200 hover:bg-gray-400';
        if ($isSelected) {
            $rowStyle .= ' bg-blue-100';
        }

        $rowContainer = new VirtualListRow($this, $rowIndex, $rowStyle);

        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';
            }

            if (isset($column['render']) && is_callable($column['render'])) {
                $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);
            }
        }

        return $rowContainer;
    }

    private function getMaxScroll(): float
    {
        if ($this->rowHeight <= 0) {
            return 0.0;
        }

        $totalHeight = count($this->rows) * $this->rowHeight;
        $viewportHeight = $this->contentViewport->height;

        return max(0.0, $totalHeight - $viewportHeight);
    }

    public function handleMouseWheel(float $mouseX, float $mouseY, float $deltaY): bool
    {
        if (!$this->visible) {
            return false;
        }

        if (
            $mouseX < $this->contentViewport->x ||
                $mouseX > ($this->contentViewport->x + $this->contentViewport->width) ||
                $mouseY < $this->contentViewport->y ||
                $mouseY > ($this->contentViewport->y + $this->contentViewport->height)
        ) {
            return parent::handleMouseWheel($mouseX, $mouseY, $deltaY);
        }

        $maxScroll = $this->getMaxScroll();
        if ($maxScroll <= 0.0) {
            return false;
        }

        $scrollStep = max($this->rowHeight * 3, 20.0);
        $oldScroll = $this->scrollY;

        $this->scrollY = max(0.0, min($maxScroll, $this->scrollY + ($deltaY * $scrollStep)));

        if ($this->scrollY === $oldScroll) {
            return false;
        }

        $this->markDirty(true, false);

        return true;
    }

    public function renderContent(&$renderer, null|TextRenderer $textRenderer = null): void
    {
        if (!$this->visible || $textRenderer === null || !$textRenderer->isInitialized()) {
            return;
        }

        $clipRect = [
            'x' => (int) $this->contentViewport->x,
            'y' => (int) $this->contentViewport->y,
            'w' => (int) $this->contentViewport->width,
            'h' => (int) $this->contentViewport->height,
        ];

        sdl_set_render_clip_rect($renderer, $clipRect);

        foreach ($this->children as $child) {
            $child->render($renderer, $textRenderer);
            $child->renderContent($renderer, $textRenderer);
        }

        sdl_set_render_clip_rect($renderer, null);

        $this->renderScrollbar($renderer);
    }

    private function renderScrollbar(&$renderer): void
    {
        if ($this->rowHeight <= 0 || empty($this->rows)) {
            return;
        }

        $totalHeight = count($this->rows) * $this->rowHeight;
        $viewportHeight = $this->contentViewport->height;

        if ($totalHeight <= $viewportHeight) {
            return;
        }

        $scrollbarHeight = $viewportHeight;
        $thumbHeight = max(self::SCROLLBAR_MIN_SIZE, ($viewportHeight / $totalHeight) * $scrollbarHeight);

        $maxScroll = $this->getMaxScroll();
        if ($maxScroll <= 0.0) {
            return;
        }

        $thumbRange = $scrollbarHeight - $thumbHeight;
        $scrollRatio = $this->scrollY / $maxScroll;
        $thumbY = $this->contentViewport->y + ($scrollRatio * $thumbRange);

        $scrollbarX = ($this->contentViewport->x + $this->contentViewport->width) - self::SCROLLBAR_WIDTH;

        sdl_set_render_draw_color($renderer, 220, 220, 220, 255);
        sdl_render_fill_rect($renderer, [
            'x' => (int) $scrollbarX,
            'y' => (int) $this->contentViewport->y,
            'w' => (int) self::SCROLLBAR_WIDTH,
            'h' => (int) $scrollbarHeight,
        ]);

        sdl_set_render_draw_color($renderer, 120, 120, 120, 230);
        sdl_render_fill_rect($renderer, [
            'x' => (int) ($scrollbarX + 2),
            'y' => (int) $thumbY,
            'w' => (int) (self::SCROLLBAR_WIDTH - 4),
            'h' => (int) $thumbHeight,
        ]);
    }

    public function selectRow(int $rowIndex): void
    {
        if ($rowIndex < 0 || $rowIndex >= count($this->rows)) {
            return;
        }

        $this->selectedRowIndex = $rowIndex;

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

        $this->markDirty(true);
    }
}

class VirtualListRow extends Container
{
    private int $rowIndex;
    private VirtualListView $table;

    public function __construct(VirtualListView $table, int $rowIndex, string $style = '')
    {
        $this->rowIndex = $rowIndex;
        $this->table = $table;
        parent::__construct($style);
    }

    public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
    {
        $handled = parent::handleMouseClick($mouseX, $mouseY, $button);
        if ($handled) {
            return true;
        }

        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;
    }
}
