<?php
class FilesController {

    private $fileStorageDir;

    public function __construct() {
        $this->fileStorageDir = __DIR__ . '/../../storage';

        // Создаем директорию для файлов, если её нет
        if (!file_exists($this->fileStorageDir)) {
            mkdir($this->fileStorageDir, 0777, true);
        }
    }

    public function test() {
        echo "<h1>Тест работает</h1>";
        echo "<p>Base path: " . $this->fileStorageDir . "</p>";
        echo "<p>Storage существует: " . (file_exists($this->fileStorageDir) ? 'да' : 'нет') . "</p>";
        echo "<p>Storage доступен для записи: " . (is_writable($this->fileStorageDir) ? 'да' : 'нет') . "</p>";
        exit;
    }

    public function index($params = []) {
        // Получаем текущую директорию
        $path = isset($params['slug']) ? $params['slug'] : '';
        $path = urldecode($path);
        
        // Прямой вызов сервиса без HTTP
        $service = new FileManagerService();
        $result = json_decode($service->getPathInfo($path), true);
        
        //$result = json_encode($service->getPathTree($path, []));
        //header('Content-Type: application/json');
        //echo $result;
        //exit;
        
        $header = ['right' => []];
        addButtonToHeaderSide($header['right'], "⬆", "Загрузить новый файл", "/files.upload/$path");
        
        renderPage('file_manager/index', [
            'title' => 'Файлы',
            'description' => 'Файловый менеджер',
            'basePath' => $this->fileStorageDir,
            'subPath' => $path,
            'folders' => $result['foldersList'] ?? [],
            'files' => $result['filesList'] ?? [],
            'parentDir' => $result['parentDir'] ?? '',
            'header' => $header,
        ]);
    }

    public function delete($params = []) {
        $path = isset($params['slug']) ? $params['slug'] : '';
        $path = urldecode($path);

        if (empty($path)) {
            renderPage('Не указан путь для удаления');
            return;
        }

        // Если есть подтверждение - удаляем
        if (isset($params['confirm']) && $params['confirm'] === 'yes') {
            try {
                $path = str_replace('..', '', $path);
                $path = ltrim($path, '/');

                // Извлекаем имя удаляемого элемента и текущую директорию
                $name = basename($path);
                $parentDir = dirname($path);
                if ($parentDir == '.') $parentDir = '';

                $fullPath = $this->fileStorageDir . '/' . $path;
                $realBase = realpath($this->fileStorageDir);
                $realFull = realpath($fullPath);

                // Проверка безопасности
                if (!$realFull || strpos($realFull, $realBase) !== 0) {
                    throw new Exception("Доступ запрещен.", 403);
                }

                if (!file_exists($realFull)) {
                    throw new Exception("Файл или папка не найдены.", 404);
                }

                // Удаляем файл или папку
                if (is_file($realFull)) {
                    if (unlink($realFull)) {
                        $_SESSION['flash_message'] = "Файл '{$name}' успешно удален.";
                    } else {
                        throw new Exception("Не удалось удалить файл.", 500);
                    }
                } else {
                    // Проверяем, пустая ли папка
                    $files = scandir($realFull);
                    if (count($files) > 2) {
                        $_SESSION['flash_message'] = "Папка '{$name}' не пуста. Сначала удалите содержимое.";
                    } else {
                        if (rmdir($realFull)) {
                            $_SESSION['flash_message'] = "Папка '{$name}' успешно удалена.";
                        } else {
                            throw new Exception("Не удалось удалить папку.", 500);
                        }
                    }
                }

                // Перенаправление обратно в текущую директорию
                $redirect = '/files';
                if (!empty($parentDir)) {
                    $redirect .= '/' . $parentDir;
                }
                header("Location: $redirect");
                exit;

            } catch (Exception $e) {
                $code = $e->getCode() ?: 500;
                http_response_code($code);
                
                $_SESSION['flash_message'] = $e->getMessage();
                
                // В случае ошибки пытаемся вернуться в исходную директорию
                $parentDir = dirname($path);
                if ($parentDir == '.') $parentDir = '';
                
                $redirect = '/files';
                if (!empty($parentDir)) {
                    $redirect .= '/' . $parentDir;
                }
                header("Location: $redirect");
                exit;
            }
        }
        
        // Переводим на страницу с подтверждением
        $currentDir = dirname($path);
        if ($currentDir == '.') $currentDir = '';
        renderPage('confirm_delete', [
            'title' => 'Подтверждение удаления',
            'description' => 'Подтвердите удаление',
            'path' => $path,
            'href_yes' => "/files.delete/$path?confirm=yes",
            'href_no' => "/files/$currentDir"
        ]);
    }

    private function getFolderSize($dir) {
        $size = 0;
        $files = scandir($dir);

        foreach ($files as $file) {
            if ($file == '.' || $file == '..') continue;

            $filePath = $dir . '/' . $file;
            if (is_dir($filePath)) {
                $size += $this->getFolderSize($filePath);
            } else {
                $size += filesize($filePath);
            }
        }

        return $size;
    }

    private function downloadFileSimple($filePath, $fileName) {
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $fileName . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Accept-Ranges: bytes');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($filePath));

        ob_clean();
        flush();
        readfile($filePath);
        exit;
    }

    private function downloadFile($filePath, $fileName) {
        $fileSize = filesize($filePath);

        // Получаем range из заголовка запроса
        $range = null;
        if (isset($_SERVER['HTTP_RANGE'])) {
            $range = $_SERVER['HTTP_RANGE'];
        }

        // Общие заголовки для отдачи файла на скачивание
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $fileName . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Accept-Ranges: bytes');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');

        // Если range есть и не пустой
        if ($range && preg_match('/bytes=(\d+)-(\d*)/', $range, $matches)) {
            $start = (int)$matches[1];
            $end = $matches[2] ? (int)$matches[2] : $fileSize - 1;

            // Проверяем корректность диапазона
            if ($start > $end || $end >= $fileSize) {
                header('HTTP/1.1 416 Range Not Satisfiable');
                header('Content-Range: bytes */' . $fileSize);
                exit;
            }

            $length = $end - $start + 1;

            header('HTTP/1.1 206 Partial Content');
            header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
            header('Content-Length: ' . $length);

            // Открываем файл и отдаём только нужный кусок
            $fp = fopen($filePath, 'rb');
            fseek($fp, $start);
            $bufferSize = 8192;
            $bytesSent = 0;

            while ($bytesSent < $length && !feof($fp)) {
                $chunkSize = min($bufferSize, $length - $bytesSent);
                echo fread($fp, $chunkSize);
                $bytesSent += $chunkSize;
                flush();
            }
            fclose($fp);
        } else {
            // Нет range, отдаём весь файл целиком
            header('HTTP/1.1 200 OK');
            header('Content-Length: ' . $fileSize);

            ob_clean();
            flush();
            readfile($filePath);
        }

        exit;
    }

    private function downloadFolder($folderPath, $folderName) {
        // Проверка расширений
        if (!class_exists('ZipArchive') && !class_exists('PharData')) {
            renderPage("На сервере нет необходимых расширений: ZipArchive или Phar для PHP. Для ZipArchive на сервере с Linux нужно установить пакет php-zip.");
        }

        // Проверка на пустую папку
        $files = scandir($folderPath);
        if (count($files) <= 2) {
            throw new Exception("Папка пуста");
        }

        // Проверяем размер папки
        $folderSize = $this->getFolderSize($folderPath);
        $maxSize = 1024 * 1024 * 1024; // 1 ГБ

        if ($folderSize > $maxSize) {
            throw new Exception("Папка слишком большая для архивации (макс. " . $this->formatBytes($maxSize) . ")");
        }

        // Создаем временный ZIP-архив
        $tmpDir = sys_get_temp_dir();
        if (!is_writable($tmpDir)) {
            $tmpDir = __DIR__ . '/../tmp';
            if (!file_exists($tmpDir)) {
                mkdir($tmpDir, 0777, true);
            }
        }
        $zipFileName = tempnam($tmpDir, 'download_') . '.zip';
        
        $service = new FileManagerService();
        $filesTree = $service->getPathTree($folderPath, []);
        
        if (class_exists('ZipArchive')) {
            $zip = new ZipArchive();
            $zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE);

            // Добавляем файлы и папки в ZipArchive
            foreach ($filesTree as $file) {
               $realPath = realpath($this->fileStorageDir . '/' . $file['path']);
                if ($file['isFolder']) {
                    $zip->addEmptyDir($file['relativePath']); // Пустые папки тоже добавляем
                } else {
                    $zip->addFile($realPath, $file['relativePath']);
                }
            }
            $zip->close();
        }
        else if (class_exists('PharData')) {
            $zip = new PharData($zipFileName);

            foreach ($filesTree as $file) {
                $realPath = realpath($this->fileStorageDir . '/' . $file['path']);

                if ($file['isFolder']) {
                    $zip->addEmptyDir($file['relativePath']);
                }
                else {
                    $zip->addFile($realPath, $file['relativePath']);
                }
            }
        }
        
        if (!file_exists($zipFileName) || filesize($zipFileName) == 0) {
            throw new Exception("Ошибка создания архива");
        }

        // Отдаем файл
        $downloadName = $folderName ?: 'folder';
        $downloadName = preg_replace('/[^a-zA-Z0-9а-яА-Я_\-]/u', '_', $downloadName);
        $downloadName .= '.zip';

        header('Content-Description: File Transfer');
        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="' . $downloadName . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($zipFileName));

        ob_clean();
        flush();
        readfile($zipFileName);

        // Удаляем временный файл
       // unlink($zipFileName);
        exit;
    }

    // Основной метод download
    public function download($params = []) {
        try {
            $path = isset($params['slug']) ? $params['slug'] : '';
            $path = urldecode($path);
            $realPath = realpath($this->fileStorageDir . '/' . $path);

            $service = new FileManagerService();
            $result = json_decode($service->getPathInfo($path), true);

            // Определяем, что скачиваем: файл или папку
            if ($result['isFolder']) { // if (is_file($realPath))
                // Скачивание папки
                $this->downloadFolder($realPath, basename($path));
            } else {
                // Скачивание файла
                $this->downloadFile($realPath, basename($path));
            }

        } catch (Exception $e) {
            $code = $e->getCode() ?: 500;
            http_response_code($code);

            // Логируем ошибку
            error_log("Download error: " . $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());

            // Для отладки (уберите в продакшене)
            if ($code >= 5000) {
                echo "Внутренняя ошибка сервера";
            } else {
                echo $e->getMessage();
            }
            exit;
        }
    }
    
    public function view($params = []) {
        try {
            $requestPath = isset($params['slug']) ? $params['slug'] : '';
            $requestPath = urldecode($requestPath);
            $requestPath = str_replace(['..', './', '../'], '', $requestPath);
            $requestPath = ltrim($requestPath, '/\\');

            if (empty($requestPath)) {
                throw new Exception("Не указан путь к файлу или папке", 400);
            }

            $fullPath = $this->fileStorageDir . '/' . $requestPath;
            $realBase = realpath($this->fileStorageDir);
            $realFull = realpath($fullPath);

            // Проверка безопасности
            if (!$realFull || strpos($realFull, $realBase) !== 0) {
                throw new Exception("Доступ запрещен", 403);
            }

            if (!file_exists($realFull)) {
                renderPage("Файл или папка не найдены: " . $realFull . " path: " . $path);
            }
            
            // Определяем MIME-тип файла
            $mimeType = mime_content_type($realFull);
            if (!$mimeType) {
                // fallback для некоторых систем
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $mimeType = finfo_file($finfo, $realFull);
                finfo_close($finfo);
            }

            // Скачиваем файл
            if (is_file($realFull)) {
                header('Content-Description: File Transfer');
                header('Content-Type: ' . $mimeType);
                header('Content-Disposition: inline; filename="' . basename($requestPath) . '"');
                header('Content-Transfer-Encoding: binary');
                header('Expires: 0');
                header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                header('Pragma: public');
                header('Content-Length: ' . filesize($realFull));

                ob_clean();
                flush();
                readfile($realFull);
                exit;
            }
        } catch (Exception $e) {
            $code = $e->getCode() ?: 500;
            http_response_code($code);

            // Логируем ошибку
            error_log("Download error: " . $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());

            // Для отладки (уберите в продакшене)
            if ($code >= 500) {
                echo "Внутренняя ошибка сервера";
            } else {
                echo $e->getMessage();
            }
            exit;
        }
    }

    public function upload($params = []) {
        $currentDir = $params['slug'] ?? '';
        
        $header = ['right' => []];
        addButtonToHeaderSide($header['right'], "", "Посмотреть загруженные файлы", "/files/$currentDir");
        
        // Показываем форму загрузки
        renderPage('file_manager/upload', [
            'currentDir' => $currentDir,
            'header' => $header,
        ]);
    }
    
    /**
     * AJAX-обработчик загрузки
     */
    public function uploadAjax() {
        // Это чистый AJAX-эндпоинт, без шаблона
        try {
            $targetDir = $this->fileStorageDir;
            if (!empty($_POST['path'])) {
                $path = str_replace('..', '', $_POST['path']);
                $targetDir .= '/' . ltrim($path, '/');
            }
            
            if (!file_exists($targetDir)) {
                mkdir($targetDir, 0755, true);
            }
            
            if (!isset($_FILES['file']) || $_FILES['file']['error'] != UPLOAD_ERR_OK) {
                throw new Exception("Ошибка загрузки файла", 400);
            }
            
            if ($_FILES['file']['size'] > 100 * 1024 * 1024) {
                throw new Exception("Файл слишком большой", 400);
            }
            
            $fileName = basename($_FILES['file']['name']);
            $targetFile = $targetDir . '/' . $fileName;
            
            if (move_uploaded_file($_FILES['file']['tmp_name'], $targetFile)) {
                $redirectPath = !empty($_POST['path']) ? '/files/' . $_POST['path'] : '/files';
                echo $redirectPath;
                exit;
            } else {
                throw new Exception("Не удалось сохранить файл", 500);
            }
            
        } catch (Exception $e) {
            http_response_code($e->getCode() ?: 500);
            echo $e->getMessage();
            exit;
        }
    }
    
/**
 * AJAX-обработчик загрузки по URL
 */
/**
 * AJAX-обработчик загрузки по URL (без cURL)
 */
public function uploadAjaxUrl() {
    // Запрещаем кэширование
    header('Cache-Control: no-cache, must-revalidate');
    header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
    header('Content-Type: application/json');
    
    try {
        // Получаем данные
        $url = $_POST['url'] ?? '';
        $path = $_POST['path'] ?? '';
        
        // Логируем для отладки
        error_log("URL Upload - Получен URL: " . $url);
        error_log("URL Upload - Путь: " . $path);
        
        if (empty($url)) {
            throw new Exception('URL не указан');
        }
        
        // Валидация URL
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            throw new Exception('Некорректный URL');
        }
        
        // Проверяем, что это HTTP/HTTPS
        $scheme = parse_url($url, PHP_URL_SCHEME);
        if (!in_array($scheme, ['http', 'https'])) {
            throw new Exception('Поддерживаются только HTTP и HTTPS ссылки');
        }
        
        // Получаем имя файла из URL
        $fileName = basename(parse_url($url, PHP_URL_PATH));
        if (empty($fileName) || strpos($fileName, '.') === false || strlen($fileName) < 2) {
            $fileName = 'downloaded_' . time() . '.file';
        }
        
        // Очищаем имя файла от опасных символов
        $fileName = preg_replace('/[^a-zA-Z0-9_\-\.]/u', '_', $fileName);
        if (strlen($fileName) > 255) {
            $fileName = substr($fileName, -255);
        }
        
        error_log("URL Upload - Имя файла: " . $fileName);
        
        // Формируем полный путь к директории
        $targetDir = $this->fileStorageDir;
        if (!empty($path)) {
            // Очищаем путь от опасных конструкций
            $cleanPath = str_replace(['..', './', '../'], '', $path);
            $cleanPath = ltrim($cleanPath, '/\\');
            if (!empty($cleanPath)) {
                $targetDir .= '/' . $cleanPath;
            }
        }
        
        error_log("URL Upload - Целевая директория: " . $targetDir);
        
        // Создаем директории если нужно
        if (!file_exists($targetDir)) {
            if (!mkdir($targetDir, 0777, true)) {
                throw new Exception('Не удалось создать папку: ' . $targetDir);
            }
            error_log("URL Upload - Создана директория: " . $targetDir);
        }
        
        // Проверяем права на запись
        if (!is_writable($targetDir)) {
            throw new Exception('Нет прав на запись в папку: ' . $targetDir);
        }
        
        $filePath = $targetDir . '/' . $fileName;
        
        // Если файл существует, добавляем префикс с временем
        if (file_exists($filePath)) {
            $pathinfo = pathinfo($filePath);
            $fileName = $pathinfo['filename'] . '_' . time() . '.' . ($pathinfo['extension'] ?? 'file');
            $filePath = $targetDir . '/' . $fileName;
            error_log("URL Upload - Файл существует, новое имя: " . $fileName);
        }
        
        // Проверяем свободное место
        $maxSize = 100 * 1024 * 1024; // 100 MB
        $freeSpace = disk_free_space($this->fileStorageDir);
        if ($freeSpace !== false && $freeSpace < $maxSize) {
            throw new Exception('Недостаточно свободного места на диске');
        }
        
        error_log("URL Upload - Начинаем скачивание: " . $url);
        
        // Альтернативный способ загрузки без cURL: используем file_get_contents с контекстом
        $options = [
            'http' => [
                'method' => 'GET',
                'header' => [
                    'User-Agent: Mozilla/5.0 (compatible; FileDownloader/1.0)',
                    'Accept: */*'
                ],
                'follow_location' => 1,
                'max_redirects' => 5,
                'timeout' => 300 // 5 минут
            ],
            'ssl' => [
                'verify_peer' => false,
                'verify_peer_name' => false
            ]
        ];
        
        $context = stream_context_create($options);
        
        // Скачиваем файл
        $fileContent = @file_get_contents($url, false, $context);
        
        if ($fileContent === false) {
            // Если не получилось через file_get_contents, пробуем через fopen
            error_log("URL Upload - file_get_contents не сработал, пробуем fopen");
            
            $fp = @fopen($url, 'rb', false, $context);
            if ($fp === false) {
                throw new Exception('Не удалось открыть URL для чтения. Проверьте доступность ссылки.');
            }
            
            $localFp = fopen($filePath, 'wb');
            if ($localFp === false) {
                fclose($fp);
                throw new Exception('Не удалось создать файл для записи');
            }
            
            $downloadedSize = 0;
            while (!feof($fp)) {
                $chunk = fread($fp, 8192);
                if ($chunk === false) {
                    break;
                }
                $bytesWritten = fwrite($localFp, $chunk);
                if ($bytesWritten === false) {
                    break;
                }
                $downloadedSize += $bytesWritten;
                
                // Проверяем лимит размера
                if ($downloadedSize > $maxSize) {
                    fclose($fp);
                    fclose($localFp);
                    unlink($filePath);
                    throw new Exception('Файл слишком большой (макс. 100 MB)');
                }
            }
            
            fclose($fp);
            fclose($localFp);
            
            $actualSize = $downloadedSize;
        } else {
            // Сохраняем содержимое в файл
            $actualSize = file_put_contents($filePath, $fileContent);
            if ($actualSize === false) {
                throw new Exception('Не удалось сохранить файл');
            }
        }
        
        error_log("URL Upload - Размер: " . $actualSize . " байт");
        
        // Проверяем размер скачанного файла
        if ($actualSize == 0) {
            if (file_exists($filePath)) {
                unlink($filePath);
            }
            throw new Exception('Скачанный файл пуст');
        }
        
        if ($actualSize > $maxSize) {
            if (file_exists($filePath)) {
                unlink($filePath);
            }
            throw new Exception('Файл слишком большой: ' . round($actualSize/1024/1024, 2) . ' MB (макс. 100 MB)');
        }
        
        // Получаем реальный размер файла
        clearstatcache();
        $actualSize = filesize($filePath);
        
        error_log("URL Upload - Файл успешно сохранен: " . $filePath);
        
        // Возвращаем успешный ответ
        echo json_encode([
            'success' => true,
            'message' => 'Файл "' . $fileName . '" успешно загружен (' . $this->formatBytes($actualSize) . ')',
            'file' => $fileName,
            'size' => $actualSize,
            'path' => $path
        ], JSON_UNESCAPED_UNICODE);
        
    } catch (Exception $e) {
        error_log("URL Upload - ОШИБКА: " . $e->getMessage());
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'error' => $e->getMessage()
        ], JSON_UNESCAPED_UNICODE);
    }
    exit;
}

/**
 * Вспомогательный метод для форматирования байтов
 */
private function formatBytes($bytes, $precision = 2) {
    $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, $precision) . ' ' . $units[$pow];
}
}
