<?php

namespace IGD;

defined( 'ABSPATH' ) || exit();

/**
 * Class Shortcode
 *
 * Handles rendering of various shortcode modules (browser, gallery, media, etc).
 * Manages permissions, access control, authentication, and module-specific logic.
 *
 * @package IGD
 */
class Shortcode {
    /**
     * Singleton instance
     *
     * @var Shortcode|null
     */
    protected static $instance = null;

    /**
     * Current module type (browser, gallery, media, etc)
     *
     * @var string|null
     */
    protected $type = null;

    /**
     * Shortcode configuration data
     *
     * @var array
     */
    protected $data;

    /**
     * Static reference to current shortcode
     *
     * @var mixed
     */
    protected static $current_shortcode;

    /**
     * Current user object (when logged in)
     *
     * @var \WP_User|null
     */
    protected $current_user = null;

    /**
     * Constructor - registers shortcode
     */
    public function __construct() {
        add_shortcode( 'integrate_google_drive', [ $this, 'render_shortcode' ] );
    }

    /**
     * Render the shortcode output.
     *
     * @param array $atts Shortcode attributes (id or data).
     * @param mixed $data Optional shortcode data (if passed directly).
     *
     * @return string|bool|null The rendered HTML output or null if no data.
     */
    public function render_shortcode( $atts = [], $data = null ) {

        $this->fetch_data( $atts, $data );

        // Return early if shortcode data is not found
        if ( empty( $this->data ) ) {
            return null;
        }

        // Check if shortcode is enabled
        if ( ! $this->check_status() ) {
            return null;
        }

        // Set up security and current user
        $this->setup_nonce();

        // Enqueue frontend scripts
        $this->enqueue_scripts();

        // Check access permissions and display appropriate screen if needed
        $access_screen = $this->get_access_screen();
        if ( ! is_null( $access_screen ) ) {
            return $access_screen;
        }

        // Handle async loading
        $async_loading = igd_get_settings( 'asyncLoading' );
        if ( $async_loading && ! empty( $this->data['id'] ) && ! wp_doing_ajax() ) {
            ob_start();
            ?>
            <div class="igd igd-async"
                 data-shortcode-id="<?php echo esc_attr( $this->data['id'] ); ?>"
                 data-nonce="<?php echo ! empty( $this->data['nonce'] ) ? esc_attr( $this->data['nonce'] ) : ''; ?>"
            >
                <div class="igd-spinner spinner-large"></div>
            </div>
            <?php
            return ob_get_clean();
        }

        // Apply module configuration and render
        $this->apply_module_configuration();

        return $this->generate_html();
    }

    /**
     * Fetch shortcode data from attributes or database.
     *
     * @param array $atts Shortcode attributes.
     * @param mixed $data Optional pre-provided data.
     */
    public function fetch_data( $atts, $data ) {

        // Use provided data or retrieve from attributes
        if ( empty( $data ) ) {
            $data = $this->get_data_from_attributes( $atts );
        }

        //if nonce is passed, unset it
        if ( ! empty( $data['nonce'] ) ) {
            unset( $data['nonce'] );
        }

        $this->type = $data['type'] ?? '';
        $this->data = $data;
    }

    /**
     * Set up security measures like nonce and current user.
     */
    public function setup_nonce() {
        if ( ! is_user_logged_in() ) {
            $this->data['nonce'] = wp_create_nonce( 'igd-shortcode-nonce' );
        } else {
            $this->current_user = wp_get_current_user();
        }
    }

    /**
     * Get access denial or login screen if needed.
     *
     * @return string|null The access screen HTML or null if access granted.
     */
    public function get_access_screen() {

        $has_permission = $this->check_user_has_permission();

        if ( $has_permission ) {
            $has_permission = $this->check_use_private_files();
        }

        $screen = $has_permission ? null : false;

        if ( ! $has_permission ) {

            // Display access denied message
            if ( ! isset( $this->data['showAccessDeniedMessage'] ) || ! empty( $this->data['showAccessDeniedMessage'] ) ) {
                $screen = $this->get_access_denied_message();
            }

        }

        return apply_filters( 'igd_shortcode_access_screen', $screen, $this->data, $this->current_user, $has_permission );

    }

    public function check_use_private_files() {

        if ( ! igd()->is_pro_active() ) {
            return true;
        }

        $allowed_types = [
                'browser',
                'uploader',
                'gallery',
                'media',
                'search',
                'slider',
                'embed',
                'list',
                'review',
        ];

        if ( ! in_array( $this->type, $allowed_types, true ) ) {
            return true;
        }

        $all_folders     = ! empty( $this->data['allFolders'] );
        $private_folders = ! empty( $this->data['privateFolders'] );

        // Only apply logic for private folders
        if ( $all_folders || ! $private_folders ) {
            return true;
        }

        // Private folders require login
        if ( ! $this->current_user ) {
            return false;
        }

        // Do not auto-create folders for form uploader
        if ( ! empty( $this->data['isFormUploader'] ) ) {
            return true;
        }

        $user_id = $this->current_user->ID;
        $folders = get_user_meta( $user_id, 'igd_folders', true );

        // Filter uploader folders to directories only
        if ( 'uploader' === $this->type && ! empty( $folders ) ) {
            $folders = array_values( array_filter( (array) $folders, 'igd_is_dir' ) );
        }

        // Auto-create private folder if enabled
        if ( empty( $folders ) && ! empty( $this->data['createPrivateFolder'] ) ) {
            $folders = \IGD_PRO\PrivateFolders::instance()->create_user_folder( $user_id, $this->data );
        }


        // No folders → access denied
        if ( empty( $folders ) ) {
            return false;
        }

        /*
         * Prepare user folders data
         */
        $user_files = [];

        foreach ( (array) $folders as $folder ) {

            if ( empty( $folder['id'] ) || empty( $folder['accountId'] ) ) {
                continue;
            }

            $user_files[] = App::instance( $folder['accountId'] )->get_file_by_id( $folder['id'] );
        }

        if ( empty( $user_files ) ) {
            return false;
        }

        $this->data['folders'] = array_values( $user_files );

        return true;
    }

    /**
     * Extract shortcode data from attributes.
     *
     * @param array $atts Shortcode attributes.
     *
     * @return array|null The decoded/retrieved shortcode data.
     */
    public function get_data_from_attributes( $atts ) {
        // Check for encoded data attribute
        if ( ! empty( $atts['data'] ) ) {
            return json_decode( base64_decode( $atts['data'], true ), true );
        }

        // Check for shortcode ID attribute
        if ( empty( $atts['id'] ) ) {
            return null;
        }

        $id = intval( $atts['id'] );
        if ( $id <= 0 ) {
            return null;
        }

        // Fetch shortcode configuration from database
        $shortcode = $this->get_shortcode( $id );

        return ! empty( $shortcode ) ? $shortcode['config'] : null;
    }

    /**
     * Check if shortcode is enabled.
     *
     * @return bool True if shortcode is enabled, false otherwise.
     */
    public function check_status() {
        $status = $this->data['status'] ?? 'on';

        return 'off' !== $status;
    }

    /**
     * Get the access denied message HTML.
     *
     * @return string The HTML markup for access denied message.
     */
    public function get_access_denied_message() {
        ob_start();
        include IGD_INCLUDES . '/views/shortcode/access-denied.php';

        return ob_get_clean();
    }

    /**
     * Set file manipulation permissions based on user roles and shortcode configuration.
     *
     * @return void
     */
    public function set_permissions() {


        // Preview
        $this->data['preview'] = ( ! isset( $this->data['preview'] ) || ( ! empty( $this->data['preview'] ) ) ) && $this->check_permission( 'preview' );

        // Download
        $this->data['download'] = ( ! isset( $this->data['download'] ) || ( ! empty( $this->data['download'] ) ) ) && $this->check_permission( 'download' );

        // Delete
        $this->data['canDelete'] = ! empty( $this->data['canDelete'] ) && $this->check_permission( 'canDelete' );

        // Rename
        $this->data['rename'] = ! empty( $this->data['rename'] ) && $this->check_permission( 'rename' );

        // Upload
        $this->data['upload'] = ( ! empty( $this->data['upload'] ) || ( ! isset( $this->data['upload'] ) ) && ! empty( $this->data['isFormUploader'] ) ) && $this->check_permission( 'upload' );

        // New Folder
        $this->data['newFolder'] = ! empty( $this->data['newFolder'] ) && $this->check_permission( 'newFolder' );

        // moveCopy
        $this->data['moveCopy'] = ! empty( $this->data['moveCopy'] ) && $this->check_permission( 'moveCopy' );

        // Share
        $this->data['allowShare'] = ! empty( $this->data['allowShare'] ) && $this->check_permission( 'allowShare' );

        // Search
        $this->data['allowSearch'] = ( 'search' == $this->type || ( ! empty( $this->data['allowSearch'] ) ) && $this->check_permission( 'allowSearch' ) );

        // Create
        $this->data['createDoc'] = ! empty( $this->data['createDoc'] ) && $this->check_permission( 'createDoc' );

        // Edit
        $this->data['edit'] = ! empty( $this->data['edit'] ) && $this->check_permission( 'edit' );

        // Direct Link
        $this->data['copyLink'] = ! empty( $this->data['copyLink'] ) && $this->check_permission( 'copyLink' );

        // Details
        $this->data['details'] = ! empty( $this->data['details'] ) && $this->check_permission( 'details' );

        // Details
        $this->data['comment'] = ! empty( $this->data['comment'] ) && $this->check_permission( 'comment' );

        // photoProof
        $this->data['photoProof'] = ( ! empty( $this->data['photoProof'] ) || 'review' == $this->type ) && $this->check_permission( 'photoProof' );
    }

    /**
     * Check if user has specific permission.
     *
     * @param string $permission_type The permission type to check.
     *
     * @return bool True if user has permission, false otherwise.
     */
    public function check_permission( $permission_type ) {
        $type_user_key_map = [
                'preview'     => 'previewUsers',
                'download'    => 'downloadUsers',
                'upload'      => 'uploadUsers',
                'allowShare'  => 'shareUsers',
                'createDoc'   => 'createDocumentUsers',
                'edit'        => 'editUsers',
                'copyLink'    => 'copyLinkUsers',
                'details'     => 'detailsUsers',
                'allowSearch' => 'searchUsers',
                'canDelete'   => 'deleteUsers',
                'rename'      => 'renameUsers',
                'moveCopy'    => 'moveCopyUsers',
                'newFolder'   => 'newFolderUsers',
                'comment'     => 'commentUsers',
                'photoProof'  => 'photoProofUsers',
        ];

        $user_key = $type_user_key_map[ $permission_type ] ?? null;
        $users    = ( $user_key && isset( $this->data[ $user_key ] ) ) ? $this->data[ $user_key ] : [ 'everyone' ];

        // No users defined means everyone has permission
        if ( empty( $users ) ) {
            return true;
        }

        // Everyone has permission by default
        if ( in_array( 'everyone', $users, true ) ) {
            return true;
        }

        // Check if current user has permission
        if ( ! $this->current_user ) {
            return false;
        }

        // Check if user's role matches
        if ( ! empty( array_intersect( $this->current_user->roles, $users ) ) ) {
            return true;
        }

        // Check if current user ID is in allowed list
        if ( in_array( $this->current_user->ID, $users, true ) ) {
            return true;
        }

        return false;
    }

    public function get_initial_search_term() {
        if ( ! empty( $this->data['allowSearch'] ) && ! empty( $this->data['initialSearchTerm'] ) && strpos( $this->data['initialSearchTerm'], '%' ) !== false ) {

            $search_template = $this->data['initialSearchTerm'];
            $tag_args        = [
                    'name' => $search_template,
            ];

            // Add user data
            if ( igd_contains_tags( 'user', $search_template ) ) {
                if ( $this->current_user ) {
                    $tag_args['user'] = get_userdata( get_current_user_id() );
                }
            }

            // Add the current post to the args
            if ( igd_contains_tags( 'post', $search_template ) ) {
                global $post;
                if ( ! empty( $post ) ) {
                    $tag_args['post'] = $post;

                    // if post is a product get the product
                    if ( $post->post_type == 'product' ) {
                        $product = wc_get_product( $post->ID );
                        if ( ! empty( $product ) ) {
                            $tag_args['wc_product'] = $product;
                        }
                    }

                }
            }

            $this->data['initialSearchTerm'] = igd_replace_template_tags( $tag_args );

        }

    }

    public function process_files() {

        // Check ACF dynamic field files
        if ( ! empty( $this->data['acfDynamicFiles'] ) ) {
            $this->data['folders'] = ! empty( $this->data['acfFieldKey'] ) ? array_values( $this->get_acf_dynamic_field_files( $this->data['acfFieldKey'] ) ) : [];
        }

        // Check if uploader module and selected folder is enabled
        if ( 'uploader' == $this->type && ! empty( $this->data['uploadFolderSelection'] ) ) {
            if ( empty( $this->data['folders'] ) ) {
                $this->data['folders'] = array_values( $this->data['uploadFolders'] ) ?? [];
            }
        }

        // First, we check if the 'type' is one of the specified values and 'folders' is not empty.
        if ( ! in_array( $this->type, [
                        'browser',
                        'gallery',
                        'media',
                        'slider',
                        'embed',
                        'list',
                        'review',
                ] ) || empty( $this->data['folders'] ) ) {
            return;
        }

        // Process based on whether there is a single folder or multiple.
        $is_single_folder = 'list' != $this->type && count( $this->data['folders'] ) == 1 && igd_is_dir( reset( $this->data['folders'] ) );

        if ( $is_single_folder ) {
            $this->process_single_folder();
        } else {
            //$this->get_files_from_server();
        }

        // If the type is 'slider' and the user can use premium code, process the files accordingly.
        if ( $this->type == 'slider' ) {
            $this->get_slider_files();
        }

        // If merge folderFiles is enabled get the files from the folders.
        if ( in_array( $this->type, [
                        'browser',
                        'gallery',
                        'embed',
                        'media',
                        'slider',
                        'review',
                        'list',
                ] ) && ! empty( $this->data['folderFiles'] ) ) {

            $files = [];

            foreach ( $this->data['folders'] as $item ) {
                if ( igd_is_dir( $item ) ) {
                    // Merge files from folder into $files array if it's a directory.
                    if ( ! empty( $this->data['mergeSubFolders'] ) ) {
                        $files = array_merge( $files, igd_get_all_child_files( $item ) );
                    } else {
                        $files = array_merge( $files, igd_get_child_items( $item ) );
                    }

                } else {
                    // Otherwise, just add the single file.
                    $files[] = $item;
                }
            }

            $this->data['folders'] = array_values( $files );
        }

    }

    public function process_single_folder() {
        $folder = reset( $this->data['folders'] );

        $this->data['initParentFolder'] = $folder;

        if ( is_array( $folder ) ) {

            $args = [
                    'folder'      => $folder,
                    'fileNumbers' => ! empty( $this->data['fileNumbers'] ) ? $this->data['fileNumbers'] : - 1,
                    'filters'     => ! empty( $this->data['filters'] ) ? $this->data['filters'] : [],
            ];

            if ( ! empty( $this->data['sort'] ) ) {
                $args['sort'] = $this->data['sort'];
            }

            // lazy load items
            if ( in_array( $this->type, [ 'browser', 'gallery', 'review', ] ) ) {
                if ( ! isset( $this->data['lazyLoad'] ) || ! empty( $this->data['lazyLoad'] ) ) {
                    $args['limit'] = ! empty( $this->data['lazyLoadNumber'] ) ? $this->data['lazyLoadNumber'] : 100;
                }
            }

            // Fetch files
            $account_id = ! empty( $folder['accountId'] ) ? $folder['accountId'] : '';
            $files_data = App::instance( $account_id )->get_files( $args );

            if ( isset( $files_data['files'] ) ) {
                $this->data['folders'] = array_values( $files_data['files'] );
            }

        }

    }

    public function get_files_from_server() {

        $folders = $this->data['folders'] ?? [];

        if ( empty( $folders ) || ! is_array( $folders ) ) {
            return;
        }

        // Collect valid folder IDs for cache key
        $folder_ids = array_values( array_filter( array_map(
                fn( $folder ) => is_array( $folder ) && ! empty( $folder['id'] ) ? $folder['id'] : null,
                $folders
        ) ) );

        if ( empty( $folder_ids ) ) {
            return;
        }

        $cache_key = 'igd_latest_fetch_' . md5( maybe_serialize( $folder_ids ) );

        /*
         * ------------------------------------------------------------------
         * Fetch from Google Drive (cache miss)
         * ------------------------------------------------------------------
         */
        if ( ! get_transient( $cache_key ) ) {

            set_transient( $cache_key, true, 2 * HOUR_IN_SECONDS );

            $first_folder = reset( $folders );
            $account_id   = $first_folder['accountId'] ?? igd_get_active_account_id();

            $app     = App::instance( $account_id );
            $client  = $app->client;
            $service = $app->getService();

            $client->setUseBatch( true );
            $batch = new \IGDGoogle_Http_Batch( $client );

            foreach ( $folders as $key => $folder ) {

                if ( empty( $folder['id'] ) ) {
                    unset( $folders[ $key ] );
                    continue;
                }

                try {
                    $request = ! empty( $folder['shared-drives'] )
                            ? $service->drives->get( $folder['id'], [ 'fields' => '*' ] )
                            : $service->files->get( $folder['id'], [
                                    'supportsAllDrives' => true,
                                    'fields'            => $app->file_fields,
                            ] );

                    $batch->add( $request, (string) $key );

                } catch ( \Exception $e ) {
                    error_log( 'IGD SDK ERROR (Request): ' . $e->getMessage() );
                }
            }

            try {
                $batch_result = $batch->execute();
            } catch ( \Exception $e ) {
                error_log( 'IGD SDK ERROR (Batch Execute): ' . $e->getMessage() );
                $client->setUseBatch( false );

                return;
            }

            $client->setUseBatch( false );

            $removed_ids = [];

            foreach ( $batch_result as $key => $file ) {

                $index     = (int) str_replace( 'response-', '', $key );
                $folder_id = $folders[ $index ]['id'] ?? null;

                // Invalid / failed response
                if (
                        empty( $file ) ||
                        empty( $file->getId() ) ||
                        $file instanceof \IGDGoogle_Service_Exception ||
                        $file instanceof \IGDGoogle_Exception
                ) {

                    if ( $folder_id ) {
                        $removed_ids[] = $folder_id;
                    }

                    unset( $folders[ $index ] );
                    continue;
                }

                // Normalize response
                $file = $file instanceof \IGDGoogle_Service_Drive_DriveList
                        ? igd_drive_map( $file, $account_id )
                        : igd_file_map( $file, $account_id );

                Files::add_file( $file );
                $folders[ $index ] = $file;
            }

            // Cleanup removed files
            if ( ! empty( $removed_ids ) ) {

                foreach ( $removed_ids as $removed_id ) {
                    Files::delete( [ 'id' => $removed_id ], [ '%s' ] );
                }

                $this->data['folders'] = array_values( $folders );

                // update the shortcode folders in db
                $raw_data                      = $this->get_shortcode( $this->data['id'] );
                $raw_data['config']['folders'] = $this->data['folders'];
                self::update_shortcode( $raw_data );
            }

            /*
             * ------------------------------------------------------------------
             * Load from cache
             * ------------------------------------------------------------------
             */
        } else {

            foreach ( $folders as $key => $file ) {

                if ( empty( $file['id'] ) || empty( $file['accountId'] ) ) {
                    unset( $folders[ $key ] );
                    continue;
                }

                $fetched = App::instance( $file['accountId'] )->get_file_by_id( $file['id'] );

                if ( empty( $fetched ) || ! is_array( $fetched ) ) {
                    unset( $folders[ $key ] );
                    continue;
                }

                $folders[ $key ] = $fetched;
            }

            $this->data['folders'] = array_values( $folders );
        }

        /*
         * ------------------------------------------------------------------
         * Final normalization
         * ------------------------------------------------------------------
         */

        // Apply file number limit
        if (
                isset( $this->data['fileNumbers'] ) &&
                $this->data['fileNumbers'] > 0 &&
                count( $this->data['folders'] ) > $this->data['fileNumbers']
        ) {
            $this->data['folders'] = array_slice(
                    $this->data['folders'],
                    0,
                    $this->data['fileNumbers']
            );
        }

        // Initial sorting
        if ( ! empty( $this->data['initialFilesSorting'] ) ) {
            $this->data['folders'] = igd_sort_files(
                    $this->data['folders'],
                    $this->data['sort'] ?? []
            );
        }

        // Ensure clean numeric indexes for JS
        $this->data['folders'] = array_values( $this->data['folders'] );
    }

    private function get_acf_dynamic_field_files( $field_key ) {
        // Return early if the field key is empty or the 'get_field' function does not exist
        if ( empty( $field_key ) || ! function_exists( 'get_field' ) ) {
            return [];
        }

        // Retrieve the files using the field key
        $files = get_field( $field_key );

        // If files are not empty, process each file
        if ( ! empty( $files ) && is_array( $files ) ) {
            $files = array_map( function ( $file ) {
                if ( isset( $file['account_id'] ) ) {
                    // Rename the 'account_id' key to 'accountId'
                    $file['accountId']   = $file['account_id'];
                    $file['webViewLink'] = $file['view_link'];
                }

                return $file;
            }, $files );
        }

        return $files ?: []; // Ensure the function always returns an array
    }

    private function get_slider_files() {

        $files = [];

        foreach ( $this->data['folders'] as $key => $folder ) {
            if ( igd_is_dir( $folder ) ) {
                // Merge files from folder into $files array if it's a directory.
                $files = array_merge( $files, igd_get_all_child_files( $folder ) );
            } else {
                // Otherwise, just add the single file.
                $files[] = $folder;
            }
        }

        // Filter the $files array to exclude directories and files without 'thumbnailLink'.
        $filtered_files = array_filter( $files, function ( $file ) {
            return ! igd_is_dir( $file ) && ! empty( $file['thumbnailLink'] );
        } );

        // Merge and re-index the folders with the filtered files.
        $this->data['folders'] = array_values( $filtered_files );

    }

    public function set_filters() {

        $filters = [
                'allowExtensions'       => ! empty( $this->data['allowExtensions'] ) ? str_replace( ' ', '', $this->data['allowExtensions'] ) : '',
                'allowAllExtensions'    => $this->data['allowAllExtensions'] ?? false,
                'allowExceptExtensions' => ! empty( $this->data['allowExceptExtensions'] ) ? str_replace( ' ', '', $this->data['allowExceptExtensions'] ) : '',

                'allowNames'       => $this->data['allowNames'] ?? '',
                'allowAllNames'    => $this->data['allowAllNames'] ?? '',
                'allowExceptNames' => $this->data['allowExceptNames'] ?? '',

                'nameFilterOptions' => $this->data['nameFilterOptions'] ?? [ 'files' ],

                'showFiles' => $this->data['showFiles'] ?? true,
                'showFolders' => $this->data['showFolders'] ?? true,
        ];

        if ( 'search' != $this->type ) {
            //$filters['showFolders'] = $this->data['showFolders'] ?? true;
        }

        if ( 'gallery' == $this->type ) {
            $filters['isGallery'] = true;
        }

        if ( 'media' == $this->type ) {
            $filters['isMedia'] = true;
        }

        $this->data['filters'] = $filters;

    }

    public function check_filters() {

        if ( in_array( $this->type, [
                        'browser',
                        'gallery',
                        'media',
                        'slider',
                        'review',
                        'list',
                        'embed',
                ] ) && ! empty( $this->data['folders'] ) ) {

            $this->data['folders'] = array_values( igd_filter_files( $this->data['folders'], $this->data['filters'] ) );
        }

    }

    public function set_notifications() {

        $type = $this->type;
        $data = $this->data;

        // Determine if default notifications should be enabled
        $enable_default = ( $type === 'review' || ( $type === 'gallery' && ! empty( $data['photoProof'] ) ) ) && ! isset( $data['enableNotification'] );

        // Exit early if notifications are disabled
        if ( ! $enable_default && empty( $data['enableNotification'] ) ) {
            return;
        }

        // Assign default or provided notification values
        $this->data['notifications'] = [
                'downloadNotification'        => $data['downloadNotification'] ?? true,
                'proofNotification'           => $data['proofNotification'] ?? true,
                'uploadNotification'          => $data['uploadNotification'] ?? true,
                'deleteNotification'          => $data['deleteNotification'] ?? true,
                'playNotification'            => $data['playNotification'] ?? ( $type === 'media' ),
                'searchNotification'          => $data['searchNotification'] ?? ( $type === 'search' ),
                'viewNotification'            => $data['viewNotification'] ?? true,
                'notificationEmail'           => $data['notificationEmail'] ?? '%admin_email%',
                'skipCurrentUserNotification' => $data['skipCurrentUserNotification'] ?? true,
        ];

    }

    // Set active account
    public function set_account() {

        $this->data['accounts'] = Account::instance()->get_accounts();
        $this->data['account']  = Account::instance()->get_active_account();

        if ( empty( $this->data['allFolders'] ) && ! empty( $this->data['folders'] ) ) {
            $folder = reset( $this->data['folders'] );

            $this->data['account'] = Account::instance()->get_accounts( $folder['accountId'] );
        }
    }

    public function generate_html() {

        $width  = ! empty( $this->data['moduleWidth'] ) ? $this->data['moduleWidth'] : '100%';
        $height = ! empty( $this->data['moduleHeight'] ) ? $this->data['moduleHeight'] : '';

        switch ( $this->type ) {
            case 'embed':
                $html = sprintf( '<div class="igd igd-shortcode-wrap igd-shortcode-embed">%s</div>', igd_get_embed_content( $this->data ) );
                break;
            default:
                ob_start();
                ?>
                <div class="igd igd-shortcode-wrap igd-shortcode-<?php echo esc_attr( $this->type ); ?>"
                     data-shortcode-data="<?php echo esc_attr( base64_encode( json_encode( $this->data ) ) ); ?>"
                     style="--module-width: <?php echo esc_attr( $width ); ?>; --module-height: <?php echo esc_attr( $height ); ?>;"
                ></div>
                <?php
                $html = ob_get_clean();
                break;
        }

        return $html;
    }

    /**
     * Check if current logged-in user has permission to view.
     *
     * @return bool True if user has permission, false otherwise.
     */
    public function check_user_has_permission() {

        $display_for = $this->data['displayFor'] ?? 'everyone';

        /*
         * 1. Everyone
         */
        if ( 'everyone' === $display_for ) {
            return true;
        }

        /*
         * 2. Logged-in users only
         */
        if ( 'loggedIn' !== $display_for || ! is_user_logged_in() || ! $this->current_user ) {
            return false;
        }

        /*
         * 3. Private folders require login (extra safety)
         */
        if ( ! empty( $this->data['privateFolders'] ) && ! is_user_logged_in() ) {
            return false;
        }

        $display_users    = $this->data['displayUsers'] ?? [];
        $display_except   = $this->data['displayExcept'] ?? [];
        $display_everyone = filter_var( $this->data['displayEveryone'] ?? false, FILTER_VALIDATE_BOOLEAN );

        $user_id    = $this->current_user->ID;
        $user_roles = $this->current_user->roles;

        // Separate roles from IDs
        $allowed_roles = array_filter( $display_users, 'is_string' );
        $except_roles  = array_filter( $display_except, 'is_string' );

        /*
         * 4. Display for everyone except specific users / roles
         */
        if ( $display_everyone ) {

            // Excluded by user ID
            if ( in_array( $user_id, $display_except, true ) ) {
                return false;
            }

            // Excluded by role
            if ( ! empty( array_intersect( $user_roles, $except_roles ) ) ) {
                return false;
            }

            return true;
        }

        /*
         * 5. Explicit allow rules
         */
        $is_allowed =
                in_array( 'everyone', $allowed_roles, true ) ||
                in_array( $user_id, $display_users, true ) ||
                ! empty( array_intersect( $user_roles, $allowed_roles ) );

        /*
         * 6. No restrictions → allow
         */
        if ( empty( $display_users ) && ! $display_everyone ) {
            $is_allowed = true;
        }

        if ( ! $is_allowed ) {
            return false;
        }

        return true;
    }

    public function enqueue_scripts() {

        do_action( 'igd_shortcode_scripts', $this->data, $this->current_user );

        wp_enqueue_style( 'igd-frontend' );
        wp_enqueue_script( 'igd-frontend' );
    }

    /**
     * Apply module configuration settings.
     *
     * @return void
     */
    public function apply_module_configuration() {
        $this->set_permissions();

        $this->get_initial_search_term();

        $this->set_filters();

        $this->set_notifications();

        $this->process_files();

        // Filter files if necessary.
        $this->check_filters();

        $this->set_account();
    }

    /**
     * @return Shortcode|null
     */
    public static function instance() {
        if ( is_null( self::$instance ) ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /***---- Module Builder Methods ----***/
    public static function get_shortcode( $id ) {
        global $wpdb;

        $table = $wpdb->prefix . 'integrate_google_drive_shortcodes';

        $shortcode = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $id ), ARRAY_A );

        if ( ! empty( $shortcode ) ) {
            $config = maybe_unserialize( $shortcode['config'] );

            if (
                    ! empty( $config['privateFolders'] )
                    || ( 'uploader' === $shortcode['type'] && ! empty( $config['uploadFolderSelection'] ) )
                    || ( 'review' === $shortcode['type'] )
            ) {
                $instance       = new self();
                $instance->data = $config;
                $instance->apply_module_configuration();
                $config = $instance->data;
            }

            $shortcode['config']    = $config;
            $shortcode['locations'] = ! empty( $shortcode['locations'] ) ? maybe_unserialize( $shortcode['locations'] ) : [];
        }

        return $shortcode;

    }

    public static function get_shortcodes() {
        global $wpdb;

        $table = $wpdb->prefix . 'integrate_google_drive_shortcodes';

        $shortcodes = $wpdb->get_results( "SELECT * FROM $table ORDER BY id DESC", ARRAY_A );

        if ( ! empty( $shortcodes ) ) {
            foreach ( $shortcodes as &$shortcode ) {
                $config = maybe_unserialize( $shortcode['config'] );

                if ( 'uploader' === $shortcode['type'] && ( ! empty( $config['uploadFolderSelection'] ) || ! empty( $config['privateFolders'] ) ) ) {
                    $instance       = new self();
                    $instance->data = $config;
                    $instance->apply_module_configuration();
                    $config = $instance->data;
                }

                $shortcode['config']    = $config;
                $shortcode['locations'] = ! empty( $shortcode['locations'] ) ? maybe_unserialize( $shortcode['locations'] ) : [];
            }
        }

        return $shortcodes;

    }

    public static function get_shortcodes_count() {
        global $wpdb;

        $table = $wpdb->prefix . 'integrate_google_drive_shortcodes';

        return $wpdb->get_var( "SELECT COUNT(*) FROM $table" );
    }

    public static function update_shortcode( $posted, $force_insert = false ) {

        global $wpdb;

        $table = $wpdb->prefix . 'integrate_google_drive_shortcodes';
        $id    = ! empty( $posted['id'] ) ? intval( $posted['id'] ) : 0;

        // Sanitize & prepare fields
        $status = ! empty( $posted['status'] ) ? sanitize_key( $posted['status'] ) : 'on';
        $type   = ! empty( $posted['type'] ) ? sanitize_key( $posted['type'] ) : 'embed';
        $title  = ! empty( $posted['title'] ) ? sanitize_text_field( $posted['title'] ) : ''; // fallback added after insert

        $config = ! empty( $posted['config'] ) ? maybe_serialize( $posted['config'] ) : maybe_serialize( $posted );

        $data = [
                'title'   => $title, // will update later if empty
                'status'  => $status,
                'type'    => $type,
                'user_id' => get_current_user_id(),
                'config'  => $config,
        ];

        $data_format = [ '%s', '%s', '%s', '%d', '%s' ];

        if ( ! empty( $posted['created_at'] ) ) {
            $data['created_at'] = sanitize_text_field( $posted['created_at'] );
            $data_format[]      = '%s';
        }

        if ( ! empty( $posted['updated_at'] ) ) {
            $data['updated_at'] = sanitize_text_field( $posted['updated_at'] );
            $data_format[]      = '%s';
        }

        // Insert or update
        if ( $force_insert || ! $id ) {
            $wpdb->insert( $table, $data, $data_format );

            return $wpdb->insert_id;
        }

        // Update
        $wpdb->update( $table, $data, [ 'id' => $id ], $data_format, [ '%d' ] );

        return $id;
    }

    public static function duplicate_shortcode( $id ) {

        if ( empty( $id ) ) {
            return false;
        }

        $shortcode = self::get_shortcode( $id );

        if ( $shortcode ) {
            $shortcode               = (array) $shortcode;
            $shortcode['title']      = 'Copy of ' . $shortcode['title'];
            $shortcode['created_at'] = current_time( 'mysql' );
            $shortcode['updated_at'] = current_time( 'mysql' );
            $shortcode['locations']  = serialize( [] );

            $insert_id = self::update_shortcode( $shortcode, true );

            $data = array_merge( $shortcode, [
                    'id'        => $insert_id,
                    'config'    => $shortcode['config'],
                    'locations' => [],
            ] );

            return $data;
        }

        return false;
    }

    public static function delete_shortcode( $id = false ) {
        global $wpdb;
        $table = $wpdb->prefix . 'integrate_google_drive_shortcodes';

        if ( $id ) {
            $wpdb->delete( $table, [ 'id' => $id ], [ '%d' ] );
        } else {
            $wpdb->query( "TRUNCATE TABLE $table" );
        }

    }

    public static function view() { ?>
        <div id="igd-shortcode-builder"></div>
    <?php }

    /***---- Shortcode Data Methods ----***/
    public static function get_shortcode_data( $shortcode_id, $apply_configuration = false ) {
        $data = [];

        $shortcode = Shortcode::get_shortcode( $shortcode_id );

        if ( ! empty( $shortcode ) ) {
            $data = $shortcode['config'];
        }

        return $data;
    }

    public static function set_current_shortcode( $shortcode_id ) {

        $shortcode_data = self::get_shortcode_data( $shortcode_id );

        if ( ! empty( $shortcode_data ) ) {
            self::$current_shortcode = $shortcode_data;
        }

    }

    public static function get_current_shortcode() {
        return self::$current_shortcode;
    }

    /**
     * Check if the user has specific action permission
     *
     * @param string $action
     * @param $data
     * @param $shortcode_data
     *
     * @return bool
     */
    public static function can_do( $action = '', $posted = [], $shortcode_data = false ) {

        if ( ! $shortcode_data ) {
            $shortcode_data = self::get_current_shortcode();
        }

        // Early exit if shortcode_data is empty
        if ( empty( $shortcode_data ) ) {
            return is_user_logged_in();
        }

        // Handle case where all folders are accessible
        if ( ! empty( $shortcode_data['allFolders'] ) || ! empty( $shortcode_data['privateFolders'] ) ) {
            if ( in_array( $action, [ 'get_files', 'search_files', 'switch_account' ] ) ) {
                return true;
            }
        }

        $module_type = $shortcode_data['type'] ?? '';

        switch ( $action ) {
            case 'get_files':

                // Handle specific folder access
                if ( ! empty( $folder = $posted['folder'] ) ) {

                    $shortcode_folder_ids = array_map( function ( $folder ) {
                        return $folder['id'];
                    }, $shortcode_data['folders'] );

                    $breadcrumbs_keys = array_keys( igd_get_breadcrumb( $folder ) );

                    // check if any breadcrumb is in shortcode folders
                    if ( ! empty( array_intersect( $breadcrumbs_keys, $shortcode_folder_ids ) ) ) {
                        return true;
                    }

                    return false;
                }

                break;
            case 'search_files':
                // Handle file search with specific conditions
                return ! empty( $shortcode_data['allowSearch'] ) || 'search' == $module_type;
            case 'get_file';
                return ! empty( $shortcode_data['details'] );
            case 'delete_files';
                return ! empty( $shortcode_data['canDelete'] );
            case 'new_folder';
                return ! empty( $shortcode_data['newFolder'] );
            case 'move_copy';
                return ! empty( $shortcode_data['moveCopy'] );
            case 'rename';
                return ! empty( $shortcode_data['rename'] );
            case 'preview';
                return ! isset( $shortcode_data['preview'] ) || ! empty( $shortcode_data['preview'] );
            case 'create_doc';
                return ! empty( $shortcode_data['createDoc'] );
            case 'update_file_permission';
                return 'media' == $module_type && ! empty( $shortcode_data['allowEmbedPlayer'] );
            case 'photo_proof';
                return 'gallery' == $module_type && ! empty( $shortcode_data['photoProof'] );
            case 'upload';
                return in_array( $module_type, [ 'uploader', 'browser' ] );
            case 'share';
                return ! empty( $shortcode_data['allowShare'] );
            case 'download';
                return ! empty( $shortcode_data['download'] );

            default:
                // No valid action matched
                return false;
        }

        // Action provided does not result in a clear decision
        return false;
    }

}

Shortcode::instance();
