<?php

namespace onespace\tools\widgets\tinyfilebrowser;

use League\Flysystem\DirectoryAttributes as Directory;
use League\Flysystem\FileAttributes as File;
use League\Flysystem\Filesystem;
use onespace\tools\widgets\tinyfilebrowser\base\BaseFileManager;
use onespace\tools\widgets\tinyfilebrowser\errors\FilePathNotSet;
use onespace\tools\widgets\tinyfilebrowser\lib\FMConfig;
use onespace\tools\widgets\tinyfilebrowser\traits\FMActions;
use onespace\tools\widgets\tinyfilebrowser\traits\FMForms;
use onespace\tools\widgets\tinyfilebrowser\traits\FMPageElements;

defined('ROOT_PATH') || define('ROOT_PATH', $_SERVER['DOCUMENT_ROOT']);
defined('HTTP_HOST') || define('HTTP_HOST', $_SERVER['HTTP_HOST']);

final class FileBrowser extends BaseFileManager {

    use FMActions;
    use FMForms;
    use FMPageElements;

    public string $file_path;

    public Filesystem $ftp_filesystem;

    public string $ftp_path = '/';

    public string $ftp_url = '';

    public bool $is_readonly = false;

    public int $selected_estate;

    // Enable ace.js (https://ace.c9.io/) on view's page
    public bool $edit_files = false;

    // Hide Permissions and Owner cols in file-listing
    public bool $hide_columns = true;

    public string $csrfToken = '_csrf';

    public $default_timezone = 'Africa/Johannesburg';
    public $datetime_format = 'd/m/Y g:i A';

    /**
     * {@inheritdoc}
     * 
     * @throws  FilePathNotSet
     */

    public function init() {
        if (is_readable($this->config_file)) {
            @include($config_file);
        }

        if (!isset($this->file_path) && !isset($this->ftp_filesystem)) {
            throw new FilePathNotSet("You must set the parameter 'file_path' or 'ftp_filesystem'. This is the path to scan and read.");
        }

        if (!defined('FM_SESSION_ID')) {
            define('FM_SESSION_ID', 'filemanager');
        }

        define('MAX_UPLOAD_SIZE', $this->max_upload_size_bytes);
        define('UPLOAD_CHUNK_SIZE', $this->upload_chunk_size_bytes);

        $this->cfg = new FMConfig(self::CONFIG);

        if ($this->cfg->data['error_reporting']) {
            @ini_set('error_reporting', E_ALL);
            @ini_set('display_errors', 1);
        } else {
            @ini_set('error_reporting', E_ALL);
            @ini_set('display_errors', 0);
        }

        define('FM_THEME', $this->cfg->data['theme']);

        if (version_compare(PHP_VERSION, '5.6.0', '<') && function_exists('mb_internal_encoding')) {
            mb_internal_encoding('UTF-8');
        }
        if (function_exists('mb_regex_encoding')) {
            mb_regex_encoding('UTF-8');
        }

        if (empty($_SESSION['token'])) {
            $_SESSION['token'] = bin2hex(random_bytes(32));
        }

        $is_https = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1)
            || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https';

        $this->root_url = $this->fm_clean_path($this->root_url);

        // abs path for site
        defined('FM_ROOT_URL') || define('FM_ROOT_URL', ($is_https ? 'https' : 'http') . '://' . $this->http_host . (!empty($this->root_url) ? '/' . $this->root_url : ''));
        defined('FM_SELF_URL') || define('FM_SELF_URL', ($is_https ? 'https' : 'http') . '://' . $this->http_host . ($_SERVER['REDIRECT_URL'] ?? ''));

        if (isset($this->file_path)) {
            $this->root_path = rtrim($this->file_path, '\\/');
        } else {
            $this->root_path = rtrim($this->ftp_path, '\\/');
        }
        $this->root_path = str_replace('\\', '/', $this->root_path);
        defined('FM_ROOT_PATH') || define('FM_ROOT_PATH', $this->root_path);


        defined('FM_SHOW_HIDDEN') || define('FM_SHOW_HIDDEN', $this->cfg->data['show_hidden']);
        defined('FM_LANG') || define('FM_LANG', $this->cfg->data['lang']);
        defined('FM_FILE_EXTENSION') || define('FM_FILE_EXTENSION', $this->allowed_file_extensions);
        defined('FM_UPLOAD_EXTENSION') || define('FM_UPLOAD_EXTENSION', $this->allowed_upload_extensions);
        defined('FM_EXCLUDE_ITEMS') || define('FM_EXCLUDE_ITEMS', (version_compare(PHP_VERSION, '7.0.0', '<') ? serialize($this->exclude_items) : $this->exclude_items));
        defined('FM_DOC_VIEWER') || define('FM_DOC_VIEWER', $this->online_viewer);
        define('FM_READONLY', $this->is_readonly);
        define('FM_IS_WIN', DIRECTORY_SEPARATOR == '\\');

        // always use ?p=
        if (!isset($_GET['p']) && empty($_FILES)) {
            $this->fm_redirect(FM_SELF_URL . '?p=' . '&id=' . ($_GET['id'] ?? ''));
        }

        $p = isset($_GET['p']) ? $_GET['p'] : (isset($_POST['p']) ? $_POST['p'] : '');

        // clean path
        $p = $this->fm_clean_path($p);

        $input = file_get_contents('php://input');
        $_POST = (strpos($input, 'ajax') != FALSE && strpos($input, 'save') != FALSE) ? json_decode($input, true) : $_POST;

        // instead globals vars
        define('FM_PATH', $p);
        define('FM_USE_AUTH', $this->use_auth);
        define('FM_EDIT_FILE', $this->edit_files);
        defined('FM_ICONV_INPUT_ENC') || define('FM_ICONV_INPUT_ENC', $this->iconv_input_encoding);
        defined('FM_USE_HIGHLIGHTJS') || define('FM_USE_HIGHLIGHTJS', $this->use_highlightjs);
        defined('FM_HIGHLIGHTJS_STYLE') || define('FM_HIGHLIGHTJS_STYLE', $this->highlightjs_style);
        defined('FM_DATETIME_FORMAT') || define('FM_DATETIME_FORMAT', $this->datetime_format);
    }


    /**
     * {@inheritDoc}
     * 
     * @return string
     */

    public function run(): string {
        $this->view->registerCss(file_get_contents(__DIR__ . '/css/styles.css'));
        if (!isset($this->ftp_path) && !@is_dir($this->root_path)) {
            echo "<h1>" . 'Root path' . " \"{$this->root_path}\" " . 'not found!' . " </h1>";
            exit;
        }

        // Handle all AJAX Request
        if ((isset($_SESSION[FM_SESSION_ID]['logged'], $auth_users[$_SESSION[FM_SESSION_ID]['logged']]) || !FM_USE_AUTH) && isset($_POST['ajax'], $_POST['token']) && !FM_READONLY) {
            $this->fm_ajax();
        }

        // Delete file / folder
        if (isset($_GET['del'], $_POST['token']) && !FM_READONLY) {
            $this->fm_delete();
        }

        // Create a new file/folder
        if (isset($_POST['newfilename'], $_POST['newfile'], $_POST['token']) && !FM_READONLY) {
            $this->fm_create();
        }

        // Copy folder / file
        if (isset($_GET['copy'], $_GET['finish']) && !FM_READONLY) {
            $this->fm_copy_files();
        }

        // Mass copy files/ folders
        if (isset($_POST['file'], $_POST['copy_to'], $_POST['finish'], $_POST['token']) && !FM_READONLY) {
            $this->fm_mass_copy();
        }

        // Rename
        if (isset($_POST['rename_from'], $_POST['rename_to'], $_POST['token']) && !FM_READONLY) {
            $this->fm_rename_files();
        }

        // // Download
        // if (isset($_GET['dl'], $_POST['token'])) {
        //     return $this->fm_download();
        // }

        // Upload
        if (!empty($_FILES) && !FM_READONLY) {
            return $this->fm_upload();
        }

        // Mass deleting
        if (isset($_POST['group'], $_POST['delete'], $_POST['token']) && !FM_READONLY) {
            $this->fm_mass_delete();
        }

        // Pack files zip, tar
        if (isset($_POST['group'], $_POST['token']) && (isset($_POST['zip']) || isset($_POST['tar'])) && !FM_READONLY) {
            $this->fm_zip_files();
        }

        // Unpack zip, tar
        if (isset($_POST['unzip'], $_POST['token']) && !FM_READONLY) {
            $this->fm_unzip_files();
        }

        // Change Perms (not for Windows)
        if (isset($_POST['chmod'], $_POST['token']) && !FM_READONLY && !FM_IS_WIN) {
            $this->fm_change_perms();
        }

        /*************************** ACTIONS ***************************/

        $folders = $files = [];
        if (isset($this->file_path)) {
            // Standard FS

            // get current path
            $path = FM_ROOT_PATH;
            if (FM_PATH != '') {
                $path .= '/' . FM_PATH;
            }

            // check path
            if (!is_dir($path)) {
                $this->fm_redirect(FM_SELF_URL . '?p=');
            }

            // get parent folder
            $parent = $this->fm_get_parent_path(FM_PATH);
            $objects = is_readable($path) ? scandir($path) : [];
            $current_path = array_slice(explode("/", $path), -1)[0];
            if (is_array($objects) && $this->fm_is_exclude_items($current_path)) {
                foreach ($objects as $file) {
                    if ($file == '.' || $file == '..') {
                        continue;
                    }
                    if (!FM_SHOW_HIDDEN && substr($file, 0, 1) === '.') {
                        continue;
                    }
                    $new_path = $path . '/' . $file;
                    if (@is_file($new_path) && $this->fm_is_exclude_items($file)) {
                        $files[] = $file;
                    } elseif (@is_dir($new_path) && $file != '.' && $file != '..' && $this->fm_is_exclude_items($file)) {
                        $folders[] = $file;
                    }
                }
            }
        } else {
            /**
             * - If there is no parent, $parent = false
             * - If you are in the first subdir from the main dir $parent = ""
             * - If you are in a further subdir, $parent = 'private' or 'private/mouse'
             */
            $path = $this->ftp_path;

            $count = substr_count($path, '/');
            if (!FM_READONLY) {
                if ($count === 1) {
                    $parent = false;
                } else if ($count === 2) {
                    $parent = "";
                } else {
                    $parts = explode('/', $this->ftp_path);
                    array_shift($parts);
                    array_shift($parts);
                    array_pop($parts);
                    $parent = implode('/', $parts);
                }
            } else {
                if ($count === 2) {
                    $parent = false;
                } else if ($count === 3) {
                    $parent = "";
                } else {
                    $parts = explode('/', $this->ftp_path);
                    array_shift($parts);
                    array_shift($parts);
                    array_pop($parts);
                    $parent = implode('/', $parts);
                }
            }

            /** @var \League\Flysystem\DirectoryListing $listing */
            $listing = $this->ftp_filesystem->listContents($this->ftp_path, false);

            $file_items = $folder_items = [];
            /** @var \League\Flysystem\StorageAttributes $item */
            foreach ($listing as $item) {
                $search_path = $item->path();
                $parts = array_reverse(explode('/', $search_path));

                if ($item instanceof File) {
                    // handle the file
                    $files[] = $parts[0];
                    $file_items[$parts[0]] = $item;
                } else if ($item instanceof Directory) {
                    // handle the directory
                    $folders[] = $parts[0];
                    $folder_items[$parts[0]] = $item;
                }
            }
        }

        if (!empty($files)) {
            natcasesort($files);
            if (isset($file_items)) {
                $hold = [];
                foreach ($files as $file) {
                    if (str_starts_with($file, '.')) {
                        continue;
                    }
                    $hold[$file] = $file_items[$file];
                }
                $files = $hold;
            }
        }
        if (!empty($folders)) {
            natcasesort($folders);
            if (isset($folder_items)) {
                $hold = [];
                foreach ($folders as $file) {
                    if (str_starts_with($file, '.')) {
                        continue;
                    }
                    $hold[$file] = $folder_items[$file];
                }
                $folders = $hold;
            }
        }

        // upload form
        if (isset($_GET['upload']) && !FM_READONLY) {
            return $this->upload_form();
        }

        // copy form POST
        if (isset($_POST['copy']) && !FM_READONLY) {
            $this->copy_form_post();
        }

        // copy form
        if (isset($_GET['copy']) && !isset($_GET['finish']) && !FM_READONLY) {
            $this->copy_form($parent, $folders);
        }

        if (isset($_GET['settings']) && !FM_READONLY) {
            $this->settings_form();
        }

        if (isset($_GET['help'])) {
            $this->help_form(self::VERSION);
        }

        // file viewer
        if (isset($_GET['view'])) {
            return $this->view_file_form($path);
        }

        // file editor
        if (isset($_GET['edit']) && !FM_READONLY) {
            $this->file_editor_form($path);
        }

        // chmod (not for Windows)
        if (isset($_GET['chmod']) && !FM_READONLY && !FM_IS_WIN) {
            $this->chmod_form($path);
        }

        return $this->main_form($files, $folders, $path, $parent);
    }
}
