<?php
/**
 * @author WP Cloud Plugins
 * @copyright Copyright (c) 2023, WP Cloud Plugins
 *
 * @since       2.0
 * @see https://www.wpcloudplugins.com
 */

namespace TheLion\UseyourDrive;

defined('ABSPATH') || exit;

class UserFolders
{
    /**
     * The single instance of the class.
     *
     * @var UserFolders
     */
    protected static $_instance;

    /**
     * @var string
     */
    private $_user_name_template;

    /**
     * @var string
     */
    private $_user_folder_name;

    /**
     * @var \TheLion\UseyourDrive\Entry
     */
    private $_user_folder_entry;

    public function __construct()
    {
        $this->_user_name_template = Settings::get('userfolder_name');

        $shortcode = Processor::instance()->get_shortcode();
        if (!empty($shortcode) && !empty($shortcode['user_folder_name_template'])) {
            $this->_user_name_template = $shortcode['user_folder_name_template'];
        }
    }

    /**
     * UserFolders Instance.
     *
     * Ensures only one instance is loaded or can be loaded.
     *
     * @return UserFolders - UserFolders instance
     *
     * @static
     */
    public static function instance()
    {
        if (is_null(self::$_instance)) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    public static function user_register($user_id)
    {
        if ('Yes' !== Settings::get('userfolder_oncreation')) {
            return;
        }

        foreach (Accounts::instance()->list_accounts() as $account) {
            if (false === $account->get_authorization()->has_access_token()) {
                continue;
            }

            App::set_current_account($account);
            self::instance()->create_user_folders_for_shortcodes($user_id);
        }
    }

    public static function user_profile_update($user_id, $old_user_data = false)
    {
        if ('Yes' !== Settings::get('userfolder_update')) {
            return;
        }

        foreach (Accounts::instance()->list_accounts() as $account) {
            if (false === $account->get_authorization()->has_access_token()) {
                continue;
            }

            App::set_current_account($account);
            self::instance()->update_user_folder($user_id, $old_user_data);
        }
    }

    public static function user_delete($user_id)
    {
        if ('Yes' !== Settings::get('userfolder_remove')) {
            return;
        }

        foreach (Accounts::instance()->list_accounts() as $account) {
            if (false === $account->get_authorization()->has_access_token()) {
                continue;
            }

            App::set_current_account($account);
            self::instance()->remove_user_folder($user_id);
        }
    }

    public function get_auto_linked_folder_name_for_user()
    {
        $shortcode = Processor::instance()->get_shortcode();
        if (!isset($shortcode['user_upload_folders']) || 'auto' !== $shortcode['user_upload_folders']) {
            return false;
        }

        if (!empty($this->_user_folder_name)) {
            return $this->_user_folder_name;
        }

        if (is_user_logged_in()) {
            $current_user = wp_get_current_user();
            $userfoldername = $this->get_user_name_template($current_user);
        } else {
            $userfoldername = $this->get_guest_user_name();
        }

        $this->_user_folder_name = $userfoldername;

        return $userfoldername;
    }

    public function get_auto_linked_folder_for_user()
    {
        $shortcode = Processor::instance()->get_shortcode();
        if (!isset($shortcode['user_upload_folders']) || 'auto' !== $shortcode['user_upload_folders']) {
            return false;
        }

        if (!empty($this->_user_folder_entry)) {
            return $this->_user_folder_entry;
        }

        // Add folder if needed
        $result = $this->create_user_folder($this->get_auto_linked_folder_name_for_user(), Processor::instance()->get_shortcode(), 0);

        do_action('useyourdrive_after_private_folder_added', $result, Processor::instance());

        if (false === $result) {
            error_log('[WP Cloud Plugin message]: Cannot find auto folder link for user');

            exit;
        }

        $this->_user_folder_entry = $result;

        return $this->_user_folder_entry;
    }

    public function get_manually_linked_folder_for_user()
    {
        $shortcode = Processor::instance()->get_shortcode();
        if (!isset($shortcode['user_upload_folders']) || 'manual' !== $shortcode['user_upload_folders']) {
            return false;
        }

        if (!empty($this->_user_folder_entry)) {
            return $this->_user_folder_entry;
        }

        $userfolder = get_user_option('use_your_drive_linkedto');
        if (is_array($userfolder) && isset($userfolder['foldertext'])) {
            if (false === isset($userfolder['accountid'])) {
                $linked_account = Accounts::instance()->get_primary_account();
            } else {
                $linked_account = Accounts::instance()->get_account_by_id($userfolder['accountid']);
            }

            App::set_current_account($linked_account);

            $this->_user_folder_entry = Client::instance()->get_entry($userfolder['folderid'], false);
        } else {
            $defaultuserfolder = get_site_option('use_your_drive_guestlinkedto');
            if (is_array($defaultuserfolder) && isset($defaultuserfolder['folderid'])) {
                if (false === isset($defaultuserfolder['accountid'])) {
                    $linked_account = Accounts::instance()->get_primary_account();
                } else {
                    $linked_account = Accounts::instance()->get_account_by_id($defaultuserfolder['accountid']);
                }

                App::set_current_account($linked_account);
                $this->_user_folder_entry = Client::instance()->get_entry($defaultuserfolder['folderid'], false);
            } else {
                if (is_user_logged_in()) {
                    $current_user = wp_get_current_user();
                    error_log('[WP Cloud Plugin message]: '.sprintf('Cannot find manual folder link for user: %s', $current_user->user_login));
                } else {
                    error_log('[WP Cloud Plugin message]: Cannot find manual folder link for guest user');
                }

                exit(-1);
            }
        }

        return $this->_user_folder_entry;
    }

    public function manually_link_folder($user_id, $linkedto)
    {
        App::set_current_account_by_id($linkedto['accountid']);
        $node = Client::instance()->get_folder($linkedto['folderid'], false);
        $linkedto['foldertext'] = $node['folder']->get_name();

        if ('GUEST' === $user_id) {
            update_site_option('use_your_drive_guestlinkedto', $linkedto);
        } else {
            update_user_option($user_id, 'use_your_drive_linkedto', $linkedto, false);
        }

        $response = [
            'folderid' => sanitize_text_field($node['folder']->get_id()),
            'foldertext' => sanitize_text_field($node['folder']->get_name()),
            'accountid' => sanitize_text_field($node['folder']->get_account_id()),
            'path' => sanitize_text_field($node['folder']->get_path(API::get_root_folder()->get_id())),
        ];

        echo json_encode($response);

        exit;
    }

    public function manually_unlink_folder($user_id)
    {
        if ('GUEST' === $user_id) {
            $result = delete_site_option('use_your_drive_guestlinkedto');
        } else {
            $result = delete_user_option($user_id, 'use_your_drive_linkedto', false);
        }

        if (false !== $result) {
            exit('1');
        }
    }

    public function create_user_folder($userfoldername, $shortcode, $mswaitaftercreation = 0)
    {
        Helpers::set_time_limit(60);

        $parent_folder_data = Client::instance()->get_folder($shortcode['root'], false);

        // If root folder doesn't exists
        if (empty($parent_folder_data) || '0' === $parent_folder_data['folder']->get_id()) {
            return false;
        }
        $parent_folder = $parent_folder_data['folder'];

        // Create Folder structure if required (e.g. it contains a /)
        if (true === apply_filters('useyourdrive_private_folder_name_allow_subfolders', true)) {
            $subfolders = array_filter(explode('/', $userfoldername));
            $userfoldername = array_pop($subfolders);

            foreach ($subfolders as $subfolder) {
                $parent_folder = API::get_sub_folder_by_path($parent_folder->get_id(), $subfolder, true);
            }
        }

        // First try to find the User Folder in Cache
        $userfolder = API::search_for_name_in_folder($userfoldername, $parent_folder->get_id());

        // If User Folder still isn't found, create new folder in the Cloud
        if (false === $userfolder) {
            $newfolder = new \UYDGoogle_Service_Drive_DriveFile();
            $newfolder->setName($userfoldername);
            $newfolder->setMimeType('application/vnd.google-apps.folder');
            $newfolder->setParents([$parent_folder->get_id()]);

            try {
                $api_entry = App::instance()->get_drive()->files->create($newfolder, ['fields' => Client::instance()->apifilefields, 'supportsAllDrives' => true]);

                // Wait a moment in case many folders are created at once
                usleep($mswaitaftercreation);
            } catch (\Exception $ex) {
                error_log('[WP Cloud Plugin message]: '.sprintf('Failed to add user folder: %s', $ex->getMessage()));

                return new \WP_Error('broke', esc_html__('Failed to add user folder', 'wpcloudplugins'));
            }

            // Add new file to our Cache
            $newentry = new Entry($api_entry);
            $userfolder = Cache::instance()->add_to_cache($newentry);
            Cache::instance()->update_cache();

            do_action('useyourdrive_log_event', 'useyourdrive_created_entry', $userfolder);

            do_action('useyourdrive_dynamic_folder_created', $userfolder, $shortcode);
        }

        // Check if Template folder should be created
        // 1: Is there a template folder set?
        if (empty($shortcode['user_template_dir'])) {
            return $userfolder;
        }

        // Wait a moment
        usleep(50000);

        // Make sure that the folder is completely loaded before we proceed, perhaps the folder already existed and contains the template folders
        $user_folder_node = Client::instance()->get_folder($userfolder->get_id(), false);

        // In some case the API doesn't yet response with the information for the newly created folder. In that case, fallback to the folder information received earlier
        if (!empty($user_folder_node)) {
            $userfolder = $user_folder_node['folder'];
        } else {
            error_log('[WP Cloud Plugin message]: '.sprintf('Fallback to earlier received folder information for folder %s', $userfolder->get_id()));
            $user_folder_node = $userfolder;
        }

        // 2: Has the User Folder already sub folders?
        if ($userfolder->has_children()) {
            return $userfolder;
        }

        // 3: Get the Template folder
        $cached_template_folder = Client::instance()->get_folder($shortcode['user_template_dir'], false);

        // 4: Make sure that the Template folder can be used
        if (false === $cached_template_folder || false === $cached_template_folder['folder'] || false === $cached_template_folder['folder']->has_children()) {
            return $userfolder;
        }

        if ($userfolder->is_in_folder($cached_template_folder['folder']->get_id())) {
            error_log('[WP Cloud Plugin message]: '.sprintf('Failed to add user folder: %s', esc_html__('User folder is inside Template folder. Please select another template folder.')));

            return new \WP_Error('broke', esc_html__('User folder is inside Template folder. Please select another template folder.', 'wpcloudplugins'));
        }

        // Copy the contents of the Template Folder into the User Folder
        API::copy_folder_recursive($cached_template_folder['folder']->get_id(), $userfolder->get_id());

        return $userfolder;
    }

    public function create_user_folders_for_shortcodes($user_id)
    {
        $new_user = get_user_by('id', $user_id);

        $useyourdrivelists = Shortcodes::instance()->get_all_shortcodes();
        $current_account = App::get_current_account();

        foreach ($useyourdrivelists as $list) {
            if (!isset($list['user_upload_folders']) || 'auto' !== $list['user_upload_folders']) {
                continue;
            }

            if (!isset($list['account']) || $current_account->get_id() !== $list['account']) {
                continue; // Skip shortcodes that don't belong to the account that is being processed
            }

            if (false === Helpers::check_user_role($list['view_role'], $new_user)) {
                continue; // Skip shortcodes that aren't accessible for user
            }

            if (false !== strpos($list['class'], 'disable-create-private-folder-on-registration')) {
                continue; // Skip shortcodes that explicitly have set to skip automatic folder creation
            }

            if (!empty($list['user_folder_name_template'])) {
                $this->_user_name_template = $list['user_folder_name_template'];
            } else {
                $this->_user_name_template = Settings::get('userfolder_name');
            }

            if (false === strpos($this->_user_name_template, '%')) {
                continue; // Skip shortcodes that don't have a dynamic folder template
            }

            $new_userfoldersname = $this->get_user_name_template($new_user);

            $result = $this->create_user_folder($new_userfoldersname, $list);

            do_action('useyourdrive_after_private_folder_added', $result, Processor::instance());
        }
    }

    public function create_user_folders($users = [])
    {
        if (0 === count($users)) {
            return;
        }

        foreach ($users as $user) {
            $userfoldersname = $this->get_user_name_template($user);

            $result = $this->create_user_folder($userfoldersname, Processor::instance()->get_shortcode());

            do_action('useyourdrive_after_private_folder_added', $result, Processor::instance());
        }
    }

    public function remove_user_folder($user_id)
    {
        $deleted_user = get_user_by('id', $user_id);

        $useyourdrivelists = Shortcodes::instance()->get_all_shortcodes();
        $current_account = App::get_current_account();

        // Apply Batch
        $do_delete = false;

        $batch = new \UYDGoogle_Http_Batch(App::instance()->get_sdk_client());

        foreach ($useyourdrivelists as $list) {
            if (!isset($list['user_upload_folders']) || 'auto' !== $list['user_upload_folders']) {
                continue;
            }

            if (!isset($list['account']) || $current_account->get_id() !== $list['account']) {
                continue; // Skip shortcodes that don't belong to the account that is being processed
            }

            if (false === Helpers::check_user_role($list['view_role'], $deleted_user)) {
                continue; // Skip shortcodes that aren't accessible for user
            }

            if (!empty($list['user_folder_name_template'])) {
                $this->_user_name_template = $list['user_folder_name_template'];
            } else {
                $this->_user_name_template = Settings::get('userfolder_name');
            }

            if (false === strpos($this->_user_name_template, '%')) {
                continue; // Skip shortcodes that don't have a dynamic folder template
            }

            $userfoldername = $this->get_user_name_template($deleted_user);

            $params = [
                'q' => "'".$list['root']."' in parents and name='".$userfoldername."' and mimeType='application/vnd.google-apps.folder' and trashed = false",

                'supportsAllDrives' => true,
                'includeItemsFromAllDrives' => true,
            ];

            try {
                App::instance()->get_sdk_client()->setUseBatch(false);
                $api_list = App::instance()->get_drive()->files->listFiles($params);
            } catch (\Exception $ex) {
                error_log('[WP Cloud Plugin message]: '.sprintf('Failed to remove user folder: %s', $ex->getMessage()));

                return false;
            }

            $api_files = $api_list->getFiles();

            // Stop when no User Folders are found
            if (0 === count($api_files)) {
                continue;
            }

            $do_delete = true;
            // Delete all the user folders that are found
            // 1: Create an the entry for Patch
            $updateentry = new \UYDGoogle_Service_Drive_DriveFile();
            $updateentry->setTrashed(true);

            App::instance()->get_sdk_client()->setUseBatch(true);
            foreach ($api_files as $api_file) {
                $batch->add(App::instance()->get_drive()->files->update($api_file->getId(), $updateentry, ['supportsAllDrives' => true]));
            }
        }

        if ($do_delete) {
            try {
                $batch->execute();
            } catch (\Exception $ex) {
                error_log('[WP Cloud Plugin message]: '.sprintf('Failed to remove user folder: %s', $ex->getMessage()));
            }
        }

        App::instance()->get_sdk_client()->setUseBatch(false);

        Processor::reset_complete_cache(false);

        return true;
    }

    public function update_user_folder($user_id, $old_user)
    {
        $updated_user = get_user_by('id', $user_id);

        $useyourdrivelists = Shortcodes::instance()->get_all_shortcodes();
        $current_account = App::get_current_account();

        foreach ($useyourdrivelists as $list) {
            if (!isset($list['user_upload_folders']) || 'auto' !== $list['user_upload_folders']) {
                continue;
            }

            if (!isset($list['account']) || $current_account->get_id() !== $list['account']) {
                continue; // Skip shortcodes that don't belong to the account that is being processed
            }

            if (false === Helpers::check_user_role($list['view_role'], $updated_user)) {
                continue; // Skip shortcodes that aren't accessible for user
            }

            if (!empty($list['user_folder_name_template'])) {
                $this->_user_name_template = $list['user_folder_name_template'];
            } else {
                $this->_user_name_template = Settings::get('userfolder_name');
            }

            if (false === strpos($this->_user_name_template, '%')) {
                continue; // Skip shortcodes that don't have a dynamic folder template
            }

            $new_userfoldersname = $this->get_user_name_template($updated_user);
            $old_userfoldersname = $this->get_user_name_template($old_user);

            if ($new_userfoldersname === $old_userfoldersname) {
                continue;
            }

            if (defined('use_your_drive_update_user_folder_'.$list['root'].'_'.$new_userfoldersname)) {
                continue;
            }

            define('use_your_drive_update_user_folder_'.$list['root'].'_'.$new_userfoldersname, true);

            $params = [
                'q' => "'".$list['root']."' in parents and name='".$old_userfoldersname."' and mimeType='application/vnd.google-apps.folder' and trashed = false",

                'supportsAllDrives' => true,
                'includeItemsFromAllDrives' => true,
            ];

            try {
                $api_list = App::instance()->get_drive()->files->listFiles($params);
            } catch (\Exception $ex) {
                error_log('[WP Cloud Plugin message]: '.sprintf('Failed to update user folder: %s', $ex->getMessage()));

                return false;
            }

            $api_files = $api_list->getFiles();

            // Stop when no User Folders are found
            if (0 === count($api_files)) {
                continue;
            }

            // Delete all the user folders that are found
            // 1: Create an the entry for Patch
            $updateentry = new \UYDGoogle_Service_Drive_DriveFile();
            $updateentry->setName($new_userfoldersname);

            foreach ($api_files as $api_file) {
                try {
                    API::patch($api_file->getId(), $updateentry);
                } catch (\Exception $ex) {
                    error_log('[WP Cloud Plugin message]: '.sprintf('Failed to update user folder: %s', $ex->getMessage()));

                    continue;
                }
            }
        }

        Processor::reset_complete_cache(false);

        return true;
    }

    public function get_user_name_template($user_data)
    {
        $user_folder_name = Helpers::apply_placeholders($this->_user_name_template, Processor::instance(), ['user_data' => $user_data]);

        return apply_filters('useyourdrive_private_folder_name', $user_folder_name, Processor::instance());
    }

    public function get_guest_user_name()
    {
        $username = $this->get_guest_id();

        $current_user = new \stdClass();
        $current_user->user_login = $username;
        $current_user->display_name = $username;
        $current_user->ID = $username;
        $current_user->user_role = esc_html__('Anonymous user', 'wpcloudplugins');

        $user_folder_name = $this->get_user_name_template($current_user);

        if (empty($user_folder_name)) {
            $user_folder_name = $username;
        }

        $prefix = Settings::get('userfolder_name_guest_prefix');

        return apply_filters('useyourdrive_private_folder_name_guests', $prefix.$user_folder_name, Processor::instance());
    }

    public function get_guest_id()
    {
        if (!isset($_COOKIE['WPCP_UUID'])) {
            error_log('[WP Cloud Plugin message]: No UUID found.');

            exit;
        }

        return $_COOKIE['WPCP_UUID'];
    }
}
