<?php

namespace PHPNative\Ui\Widget;

class FileBrowser extends Container
{
    private VirtualListView $fileTable;
    private Label $pathLabel;
    private Label $summaryLabel;
    private string $currentPath;
    private $onFileSelect = null;
    private $onEditFile = null;
    private $onRenameFile = null;
    private $onDeleteFile = null;
    private bool $isRemote = false;
    private null|string $lastClickPath = null;
    private float $lastClickTime = 0.0;

    public function __construct(string $initialPath = '.', bool $isRemote = false, string $style = '')
    {
        // Root-Container füllt die verfügbare Höhe im Eltern-Layout (flex-1)
        // und enthält eine VirtualListView für performantes Scrollen.
        parent::__construct('w-full flex flex-col flex-1 gap-2 ' . $style);

        $this->currentPath = $initialPath;
        $this->isRemote = $isRemote;

        // Path display
        $this->pathLabel = new Label($initialPath, 'px-3 py-2 bg-gray-200 text-black rounded text-sm font-mono');
        $this->addComponent($this->pathLabel);

        // VirtualListView rendert nur die sichtbaren Zeilen und
        // bleibt damit auch bei großen Verzeichnissen flüssig.
        $this->fileTable = new VirtualListView(' flex-1');
        $this->fileTable->setColumns([
            ['key' => 'type', 'title' => 'Typ', 'width' => 60],
            ['key' => 'name', 'title' => 'Name'],
            ['key' => 'size', 'title' => 'Größe', 'width' => 100],
            ['key' => 'modified', 'title' => 'Geändert', 'width' => 200],
            ['key' => 'actions', 'title' => 'Action', 'width' => 100, 'render' => [$this, 'renderActionsCell']],
        ]);

        $this->addComponent($this->fileTable);

        // Summary label: zeigt Anzahl Ordner/Dateien im aktuellen Verzeichnis
        $this->summaryLabel = new Label('0 Ordner, 0 Dateien', 'text-xs text-gray-600');
        $this->addComponent($this->summaryLabel);

        // Load initial directory (only if local)
        if (!$isRemote) {
            $this->loadDirectory($initialPath);
        }
    }

    /**
     * Load directory contents (local only)
     */
    public function loadDirectory(string $path): void
    {
        if ($this->isRemote) {
            return; // Remote loading is handled separately
        }

        $this->currentPath = realpath($path) ?: $path;
        $this->pathLabel->setText($this->currentPath);

        $files = [];

        // Add parent directory entry if not at root
        if ($this->currentPath !== '/') {
            $files[] = [
                'type' => 'DIR',
                'name' => '..',
                'size' => '',
                'modified' => '',
                'path' => dirname($this->currentPath),
                'isDir' => true,
            ];
        }

        // Read directory
        try {
            $entries = scandir($this->currentPath);
            foreach ($entries as $entry) {
                if ($entry === '.' || $entry === '..') {
                    continue;
                }

                $fullPath = $this->currentPath . DIRECTORY_SEPARATOR . $entry;
                $isDir = is_dir($fullPath);
                $mtime = filemtime($fullPath);

                $files[] = [
                    'type' => $isDir ? 'DIR' : 'FILE',
                    'name' => $entry,
                    'size' => $isDir ? '' : $this->formatSize(filesize($fullPath)),
                    'modified' => $mtime ? date('Y-m-d H:i:s', $mtime) : '',
                    'path' => $fullPath,
                    'isDir' => $isDir,
                ];
            }
        } catch (\Exception $e) {
            // Handle permission errors
            $files[] = [
                'type' => 'ERR',
                'name' => 'Fehler: ' . $e->getMessage(),
                'size' => '',
                'modified' => '',
                'path' => '',
                'isDir' => false,
            ];
        }

        $this->fileTable->setData($files);
        $this->updateSummaryFromFiles($files);

        // Handle row selection
        $fileBrowser = $this;
        $this->fileTable->setOnRowSelect(function ($index, $row) use ($fileBrowser) {
            if (!$row || empty($row['path'])) {
                return;
            }

            $path = $row['path'];

            // Immer Auswahl-Callback auslösen (z.B. für Upload)
            if ($fileBrowser->onFileSelect !== null) {
                ($fileBrowser->onFileSelect)($path, $row);
            }

            // Nur Verzeichnisse navigieren – per Doppelklick
            if (!($row['isDir'] ?? false)) {
                return;
            }

            $now = microtime(true);
            $doubleClickThreshold = 0.4; // Sekunden

            if ($fileBrowser->lastClickPath !== $path || ($now - $fileBrowser->lastClickTime) > $doubleClickThreshold) {
                // Erster Klick: nur merken
                $fileBrowser->lastClickPath = $path;
                $fileBrowser->lastClickTime = $now;
                return;
            }

            // Zweiter Klick innerhalb des Zeitfensters -> als Doppelklick werten
            $fileBrowser->lastClickPath = null;
            $fileBrowser->lastClickTime = 0.0;
            $fileBrowser->loadDirectory($path);
        });
    }

    /**
     * Load remote directory contents via SFTP
     */
    public function loadRemoteDirectory(string $path, $sftpConnection): void
    {
        if (!$this->isRemote) {
            return;
        }

        $this->currentPath = $path;
        $this->pathLabel->setText($path);

        $files = [];

        // Add parent directory entry if not at root
        if ($path !== '/') {
            $parentPath = dirname($path);
            if ($parentPath === '.') {
                $parentPath = '/';
            }
            $files[] = [
                'type' => 'DIR',
                'name' => '..',
                'size' => '',
                'modified' => '',
                'path' => $parentPath,
                'isDir' => true,
            ];
        }

        // Read remote directory
        try {
            $entries = $sftpConnection->nlist($path);
            if ($entries === false) {
                throw new \Exception('Cannot read directory');
            }

            foreach ($entries as $entry) {
                if ($entry === '.' || $entry === '..') {
                    continue;
                }

                $fullPath = rtrim($path, '/') . '/' . $entry;
                $stat = $sftpConnection->stat($fullPath);
                $isDir = ($stat['type'] ?? 0) === 2; // NET_SFTP_TYPE_DIRECTORY
                $mtime = $stat['mtime'] ?? 0;

                $files[] = [
                    'type' => $isDir ? 'DIR' : 'FILE',
                    'name' => $entry,
                    'size' => $isDir ? '' : $this->formatSize($stat['size'] ?? 0),
                    'modified' => $mtime ? date('Y-m-d H:i:s', $mtime) : '',
                    'path' => $fullPath,
                    'isDir' => $isDir,
                ];
            }
        } catch (\Exception $e) {
            $files[] = [
                'type' => 'ERR',
                'name' => 'Fehler: ' . $e->getMessage(),
                'size' => '',
                'modified' => '',
                'path' => '',
                'isDir' => false,
            ];
        }

        $this->fileTable->setData($files);
        $this->updateSummaryFromFiles($files);

        // Handle row selection
        $fileBrowser = $this;
        $this->fileTable->setOnRowSelect(function ($index, $row) use ($fileBrowser, $sftpConnection) {
            if ($row && isset($row['isDir']) && $row['isDir'] && !empty($row['path'])) {
                // Navigate to directory
                $fileBrowser->loadRemoteDirectory($row['path'], $sftpConnection);
            } elseif ($row && isset($row['path']) && !empty($row['path']) && $fileBrowser->onFileSelect !== null) {
                // File selected
                ($fileBrowser->onFileSelect)($row['path'], $row);
            }
        });
    }

    /**
     * Format file size
     */
    private function formatSize(int|float $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= pow(1024, $pow);

        return round($bytes, 2) . ' ' . $units[$pow];
    }

    /**
     * Set file select callback
     */
    public function setOnFileSelect(callable $callback): void
    {
        $this->onFileSelect = $callback;
    }

    /**
     * Set edit file callback
     */
    public function setOnEditFile(callable $callback): void
    {
        $this->onEditFile = $callback;
    }

    /**
     * Set rename file callback
     */
    public function setOnRenameFile(callable $callback): void
    {
        $this->onRenameFile = $callback;
    }

    /**
     * Set delete file callback
     */
    public function setOnDeleteFile(callable $callback): void
    {
        $this->onDeleteFile = $callback;
    }

    /**
     * Render actions cell with edit, rename, and delete buttons for files
     */
    public function renderActionsCell(array $rowData, int $rowIndex): Container
    {
        // Match the cell style from Table (100px width for icon buttons)
        $container = new Container('w-25 flex flex-row items-center justify-center gap-1');

        $isDir = (bool) ($rowData['isDir'] ?? false);
        $hasPath = !empty($rowData['path']);
        $isParentEntry = ($rowData['name'] ?? '') === '..';

        // Skip action buttons for invalid rows or parent navigation
        if (!$hasPath || $isParentEntry) {
            return $container;
        }

        $fileBrowser = $this;

        // Edit button only for files
        if (!$isDir) {
            $editButton = new Button('', 'text-blue-500 hover:text-blue-600 flex items-center justify-center');
            $editIcon = new Icon(\PHPNative\Tailwind\Data\Icon::edit, 16, 'text-blue-500');
            $editButton->setIcon($editIcon);
            $editButton->setOnClick(function () use ($fileBrowser, $rowData) {
                if ($fileBrowser->onEditFile !== null) {
                    ($fileBrowser->onEditFile)($rowData['path'], $rowData);
                }
            });
            $container->addComponent($editButton);
        }

        // Rename button for files and directories
        $renameButton = new Button('', 'text-amber-500 hover:text-amber-600 flex items-center justify-center');
        $renameIcon = new Icon(\PHPNative\Tailwind\Data\Icon::pen, 16, 'text-amber-500');
        $renameButton->setIcon($renameIcon);
        $renameButton->setOnClick(function () use ($fileBrowser, $rowData) {
            if ($fileBrowser->onRenameFile !== null) {
                ($fileBrowser->onRenameFile)($rowData['path'], $rowData);
            }
        });
        $container->addComponent($renameButton);

        // Delete button only for files
        if (!$isDir) {
            $deleteButton = new Button('', 'text-red-500 hover:text-red-600 flex items-center justify-center');
            $deleteIcon = new Icon(\PHPNative\Tailwind\Data\Icon::trash, 16, 'text-red-500');
            $deleteButton->setIcon($deleteIcon);
            $deleteButton->setOnClick(function () use ($fileBrowser, $rowData) {
                if ($fileBrowser->onDeleteFile !== null) {
                    ($fileBrowser->onDeleteFile)($rowData['path'], $rowData);
                }
            });
            $container->addComponent($deleteButton);
        }

        return $container;
    }

    /**
     * Get current path
     */
    public function getCurrentPath(): string
    {
        return $this->currentPath;
    }

    /**
     * Set path (updates label only, doesn't reload)
     */
    public function setPath(string $path): void
    {
        $this->currentPath = $path;
        $this->pathLabel->setText($path);
    }

    /**
     * Set file data directly (for remote browsers where data comes from async operations)
     */
    public function setFileData(array $files): void
    {
        $tableData = [];

        // Add parent directory if not at root
        if ($this->currentPath !== '/') {
            $parentPath = dirname($this->currentPath);
            if ($parentPath === '.') {
                $parentPath = '/';
            }
            $tableData[] = [
                'type' => 'DIR',
                'name' => '..',
                'size' => '',
                'modified' => '',
                'path' => $parentPath,
                'isDir' => true,
            ];
        }

        // Add files
        foreach ($files as $file) {
            $tableData[] = [
                'type' => $file['isDir'] ? 'DIR' : 'FILE',
                'name' => $file['name'],
                'size' => $file['isDir'] ? '' : $this->formatSize($file['size']),
                'modified' => isset($file['mtime']) ? date('Y-m-d H:i:s', $file['mtime']) : '',
                'path' => $file['path'],
                'isDir' => $file['isDir'],
            ];
        }

        $this->fileTable->setData($tableData);

        // Update summary from original file list (without '..' Eintrag)
        $this->updateSummaryFromFiles($files);

        // Set up row selection handler AFTER data is set (for remote browsers)
        // This needs to be done every time because setData might reset handlers
        $this->setupRemoteNavigationHandler();
    }

    /**
     * Setup navigation handler for remote file browser
     */
    private function setupRemoteNavigationHandler(): void
    {
        if (!$this->isRemote) {
            return;
        }

        $fileBrowser = $this;
        $this->fileTable->setOnRowSelect(function ($index, $row) use ($fileBrowser) {
            if ($row && isset($row['isDir']) && $row['isDir'] && !empty($row['path'])) {
                // Trigger the external callback for directory navigation
                if ($fileBrowser->onFileSelect !== null) {
                    ($fileBrowser->onFileSelect)($row['path'], $row);
                }
            } elseif ($row && isset($row['path']) && !empty($row['path']) && !($row['isDir'] ?? false)) {
                // File selected
                if ($fileBrowser->onFileSelect !== null) {
                    ($fileBrowser->onFileSelect)($row['path'], $row);
                }
            }
        });
    }

    /**
     * Update summary label based on list of files (with isDir flag).
     */
    private function updateSummaryFromFiles(array $files): void
    {
        $dirCount = 0;
        $fileCount = 0;

        foreach ($files as $file) {
            if (!isset($file['isDir'])) {
                continue;
            }

            if ($file['isDir']) {
                $dirCount++;
            } else {
                $fileCount++;
            }
        }

        $this->summaryLabel->setText(sprintf('%d Ordner, %d Dateien', $dirCount, $fileCount));
    }
}
