<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Handles AJAX requests.
 */
class NNAXFUF_AJAX {

    public function __construct() {
        add_action( 'wp_ajax_nnaxfuf_search_users', array( $this, 'ajax_search_users' ) );
        add_action( 'wp_ajax_nnaxfuf_rename_item',   array( $this, 'ajax_rename_item' ) );
        add_action( 'wp_ajax_nnaxfuf_get_item_details', array( $this, 'ajax_get_item_details' ) );
        add_action( 'wp_ajax_nnaxfuf_get_item_share_state', array( $this, 'ajax_get_item_share_state' ) );
        add_action( 'wp_ajax_nnaxfuf_get_roles', array( $this, 'ajax_get_roles' ) );
        add_action( 'wp_ajax_nnaxfuf_get_history', array( $this, 'ajax_get_history' ) );
    }

    public function ajax_search_users() {
        if ( ! check_ajax_referer( 'nnaxfuf_search_users', 'nonce', false ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request.', 'nnax-frontend-user-files' ) ), 400 );
        }

        if ( ! is_user_logged_in() ) {
            wp_send_json_error( array( 'message' => __( 'Not logged in.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $current_user_id = get_current_user_id();
        $is_admin_user   = current_user_can( 'manage_options' );
        if ( ! $is_admin_user && ! $this->has_valid_share_search_context( $current_user_id ) ) {
            wp_send_json_error( array( 'message' => __( 'No permission.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $term = isset( $_POST['term'] ) ? sanitize_text_field( wp_unslash( $_POST['term'] ) ) : '';

        if ( strlen( $term ) < 2 ) {
            wp_send_json_success( array() );
        }

        $args = array(
            'search'         => '*' . $term . '*',
            'search_columns' => array( 'user_login', 'user_nicename', 'display_name', 'user_email' ),
            'number'         => 10,
            'orderby'        => 'display_name',
            'order'          => 'ASC',
        );

        $users = get_users( $args );

        $results = array();
        foreach ( $users as $user ) {
            if ( (int) $user->ID === (int) $current_user_id ) {
                continue;
            }

            $row = array(
                'id'           => $user->ID,
                'display_name' => $user->display_name,
            );
            if ( $is_admin_user ) {
                $row['user_login'] = $user->user_login;
                $row['user_email'] = $user->user_email;
            }
            $results[] = $row;
        }

        wp_send_json_success( $results );
    }

    public function ajax_rename_item() {
        if ( ! check_ajax_referer( 'nnaxfuf_rename_item', 'rename_nonce', false ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request.', 'nnax-frontend-user-files' ) ), 400 );
        }

        if ( ! is_user_logged_in() ) {
            wp_send_json_error( array( 'message' => __( 'Not logged in.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $item_id  = isset( $_POST['item_id'] ) ? absint( wp_unslash( $_POST['item_id'] ) ) : 0;
        $new_name = isset( $_POST['new_name'] ) ? sanitize_text_field( wp_unslash( $_POST['new_name'] ) ) : '';

        if ( ! $item_id || empty( $new_name ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid data.', 'nnax-frontend-user-files' ) ) );
        }

        $post = get_post( $item_id );
        if ( ! $post || ( $post->post_type !== NNAXFUF_Plugin::CPT && $post->post_type !== NNAXFUF_Plugin::CPT_FOLDER ) ) {
            wp_send_json_error( array( 'message' => __( 'Item not found.', 'nnax-frontend-user-files' ) ) );
        }

        $current_user_id = get_current_user_id();
        if ( ! NNAXFUF_Helpers::user_can_manage_item( $current_user_id, $item_id ) ) {
            wp_send_json_error( array( 'message' => __( 'No permission.', 'nnax-frontend-user-files' ) ) );
        }

        $final_name = $new_name;
        if ( $post->post_type === NNAXFUF_Plugin::CPT ) {
            $locked_extension = $this->get_item_file_extension( $item_id );
            if ( $locked_extension !== '' ) {
                $base_name = preg_replace( '/\.' . preg_quote( $locked_extension, '/' ) . '$/i', '', $new_name );
                $base_name = trim( (string) $base_name );
                if ( $base_name === '' ) {
                    wp_send_json_error( array( 'message' => __( 'Please provide a file name.', 'nnax-frontend-user-files' ) ) );
                }
                $final_name = $base_name . '.' . $locked_extension;
            }
        }

        // Uniqueness check in the SAME parent folder
        $parent_id = $post->post_parent;
        $existing  = get_posts( array(
            'post_type'      => array( NNAXFUF_Plugin::CPT, NNAXFUF_Plugin::CPT_FOLDER ),
            'post_parent'    => $parent_id,
            'title'          => $final_name,
            'author'         => $post->post_author,
            'posts_per_page' => 1,
            // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Required to exclude the currently renamed item from uniqueness check.
            'post__not_in'   => array( $item_id ),
        ) );

        if ( ! empty( $existing ) ) {
            wp_send_json_error( array( 'message' => __( 'An item with this name already exists in this folder.', 'nnax-frontend-user-files' ) ) );
        }

        $old_title = (string) $post->post_title;
        $title_changed = ( $old_title !== $final_name );

        $updated_post = array(
            'ID'         => $item_id,
            'post_title' => $final_name,
        );

        $result = wp_update_post( $updated_post );

        if ( is_wp_error( $result ) ) {
            wp_send_json_error( array( 'message' => __( 'Error while renaming.', 'nnax-frontend-user-files' ) ) );
        }

        NNAXFUF_Helpers::log_activity( $item_id, 'rename', array(
            'old_title' => $old_title,
            'new_title' => $final_name
        ) );

        $meta_changes = array(
            'updated' => 0,
            'cleared' => 0,
            'changed' => 0,
        );

        if ( $post->post_type === NNAXFUF_Plugin::CPT ) {
            $raw_post = wp_unslash( $_POST );
            $posted_custom_meta = isset( $raw_post['custom_meta'] ) && is_array( $raw_post['custom_meta'] )
                ? $raw_post['custom_meta']
                : array();
            $meta_changes = $this->save_item_custom_meta( $item_id, $posted_custom_meta );
        }

        $message_parts = array();
        if ( $title_changed ) {
            $message_parts[] = __( 'Title changed', 'nnax-frontend-user-files' );
        }

        if ( ! empty( $meta_changes['changed'] ) ) {
            /* translators: %d: number of metadata fields changed. */
            $message_parts[] = sprintf( __( '%d metadata field(s) updated', 'nnax-frontend-user-files' ), (int) $meta_changes['changed'] );
        }

        if ( empty( $message_parts ) ) {
            $message = __( 'No changes.', 'nnax-frontend-user-files' );
        } else {
            $message = __( 'Changes saved: ', 'nnax-frontend-user-files' ) . implode( ', ', $message_parts ) . '.';
        }

        wp_send_json_success(
            array(
                'message'      => $message,
                'title_changed'=> (bool) $title_changed,
                'meta_changed' => (int) $meta_changes['changed'],
            )
        );
    }

    public function ajax_get_item_details() {
        if ( ! check_ajax_referer( 'nnaxfuf_item_details', 'nonce', false ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request.', 'nnax-frontend-user-files' ) ), 400 );
        }

        if ( ! is_user_logged_in() ) {
            wp_send_json_error( array( 'message' => __( 'Not logged in.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $item_id = isset( $_POST['item_id'] ) ? absint( wp_unslash( $_POST['item_id'] ) ) : 0;
        if ( $item_id <= 0 ) {
            wp_send_json_error( array( 'message' => __( 'Invalid item ID.', 'nnax-frontend-user-files' ) ), 400 );
        }

        $post = get_post( $item_id );
        if ( ! $post || ( $post->post_type !== NNAXFUF_Plugin::CPT && $post->post_type !== NNAXFUF_Plugin::CPT_FOLDER ) ) {
            wp_send_json_error( array( 'message' => __( 'Item not found.', 'nnax-frontend-user-files' ) ), 404 );
        }

        $current_user_id = get_current_user_id();
        if ( ! $this->can_user_access_item( $current_user_id, $item_id ) ) {
            wp_send_json_error( array( 'message' => __( 'No permission.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $is_folder  = ( $post->post_type === NNAXFUF_Plugin::CPT_FOLDER );
        $owner_id   = (int) get_post_meta( $item_id, NNAXFUF_Plugin::META_OWNER, true );
        if ( $owner_id <= 0 ) {
            $owner_id = (int) $post->post_author;
        }
        $owner_user = $owner_id > 0 ? get_user_by( 'id', $owner_id ) : false;
        $owner_name = $owner_user ? $owner_user->display_name : __( 'Unknown', 'nnax-frontend-user-files' );

        $shared_with  = NNAXFUF_Helpers::get_active_shared_user_ids( $item_id );
        $shared_names = array();
        foreach ( $shared_with as $uid ) {
            $u = get_user_by( 'id', (int) $uid );
            if ( $u ) {
                $shared_names[] = $u->display_name;
            }
        }

        $type_slug     = 'folder';
        $download_url  = '';
        $inline_url    = '';
        $folder_files  = 0;
        $folder_files_recursive = 0;
        $folder_subfolders      = 0;
        $folder_size_bytes      = 0;
        $folder_size_human      = '-';
        $mime_type    = '';
        $size_bytes   = 0;
        $size_human   = '-';
        $locked_extension = '';
        $title         = NNAXFUF_Helpers::get_raw_item_title( $item_id );
        $rename_value  = $title;
        $created_date  = get_the_date( 'd.m.Y H:i', $post );
        $modified_date = get_the_modified_date( 'd.m.Y H:i', $post );
        $custom_meta_fields = array();

        if ( $is_folder ) {
            $download_url = NNAXFUF_Helpers::get_folder_zip_download_url( $item_id );
            $folder_files = count( get_posts( array(
                'post_type'      => NNAXFUF_Plugin::CPT,
                'post_status'    => 'publish',
                'posts_per_page' => -1,
                'post_parent'    => $item_id,
                'fields'         => 'ids',
            ) ) );
            $folder_files_recursive = $this->count_folder_files_recursive( $item_id );
            $folder_subfolders      = $this->count_subfolders_recursive( $item_id );
            $folder_size_bytes      = NNAXFUF_Helpers::sum_folder_size_recursive( $item_id );
            $folder_size_human      = size_format( $folder_size_bytes, 2 );
        } else {
            $file_url       = get_post_meta( $item_id, NNAXFUF_Plugin::META_FILEURL, true );
            $file_type_info = NNAXFUF_Helpers::nnaxfuf_get_file_type_from_url( (string) $file_url );
            $type_slug      = isset( $file_type_info['type'] ) ? (string) $file_type_info['type'] : 'generic';
            $download_url   = NNAXFUF_Helpers::get_download_url( $item_id );
            $inline_url     = NNAXFUF_Helpers::get_download_url( $item_id, 'inline' );
            $locked_extension = $this->get_item_file_extension( $item_id );
            if ( $locked_extension !== '' ) {
                $rename_value = preg_replace( '/\.' . preg_quote( $locked_extension, '/' ) . '$/i', '', $title );
                $rename_value = trim( (string) $rename_value );
                if ( $rename_value === '' ) {
                    $rename_value = $title;
                }
            }

            $file_path = get_post_meta( $item_id, NNAXFUF_Plugin::META_FILEPATH, true );
            if ( ! empty( $file_path ) && file_exists( $file_path ) ) {
                $size_bytes = (int) filesize( $file_path );
                $size_human = size_format( $size_bytes, 2 );
                $mime_info  = wp_check_filetype( $file_path );
                $mime_type  = ! empty( $mime_info['type'] ) ? (string) $mime_info['type'] : '';
            }

            $custom_meta_fields = $this->get_item_custom_meta_fields( $item_id );
        }

        wp_send_json_success( array(
            'item_id'             => $item_id,
            'title'               => $title,
            'type'                => $type_slug,
            'is_folder'           => $is_folder,
            'owner'               => $owner_name,
            'rename_value'        => $rename_value,
            'locked_extension'    => $locked_extension,
            'shared'              => ! empty( $shared_names ) ? implode( ', ', $shared_names ) : '-',
            'date'                => $created_date,
            'modified'            => $modified_date,
            'download_url'        => $download_url,
            'inline_download_url' => $inline_url,
            'folder_file_count'   => $folder_files,
            'folder_file_count_recursive' => $folder_files_recursive,
            'folder_subfolder_count'      => $folder_subfolders,
            'folder_size_bytes'    => $folder_size_bytes,
            'folder_size_human'    => $folder_size_human,
            'mime_type'           => $mime_type,
            'size_bytes'          => $size_bytes,
            'size_human'          => $size_human,
            'custom_meta_fields'  => $custom_meta_fields,
            'can_share'           => NNAXFUF_Helpers::can_user_do( $current_user_id, $item_id, 'share' ),
            'can_rename'          => NNAXFUF_Helpers::user_can_manage_item( $current_user_id, $item_id ),
            'can_view_history'    => NNAXFUF_Helpers::user_can_manage_item( $current_user_id, $item_id ),
        ) );
    }

    public function ajax_get_item_share_state() {
        if ( ! check_ajax_referer( 'nnaxfuf_get_item_share_state', 'share_state_nonce', false ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request.', 'nnax-frontend-user-files' ) ), 400 );
        }

        if ( ! is_user_logged_in() ) {
            wp_send_json_error( array( 'message' => __( 'Not logged in.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $item_id = isset( $_POST['item_id'] ) ? absint( wp_unslash( $_POST['item_id'] ) ) : 0;
        if ( $item_id <= 0 ) {
            wp_send_json_error( array( 'message' => __( 'Invalid item ID.', 'nnax-frontend-user-files' ) ), 400 );
        }

        $post = get_post( $item_id );
        if ( ! $post || ( $post->post_type !== NNAXFUF_Plugin::CPT && $post->post_type !== NNAXFUF_Plugin::CPT_FOLDER ) ) {
            wp_send_json_error( array( 'message' => __( 'Item not found.', 'nnax-frontend-user-files' ) ), 404 );
        }

        $current_user_id = get_current_user_id();
        if ( ! NNAXFUF_Helpers::can_user_do( $current_user_id, $item_id, 'share' ) ) {
            wp_send_json_error( array( 'message' => __( 'No permission.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $owner_id = (int) get_post_meta( $item_id, NNAXFUF_Plugin::META_OWNER, true );
        if ( $owner_id <= 0 ) {
            $owner_id = (int) $post->post_author;
        }

        $this->set_share_search_context( $current_user_id, $item_id, (string) $post->post_type );

        $settings      = NNAXFUF_Helpers::get_settings();
        $share_options = isset( $settings['share_options'] ) && is_array( $settings['share_options'] ) ? $settings['share_options'] : array();
        $allow_time_limit   = ! empty( $share_options['allow_time_limit'] );
        $allow_owner_rights = ! empty( $share_options['allow_owner_rights'] );
        $allow_shared_folder_file_delete = ! empty( $share_options['allow_shared_folder_file_delete'] );

        $share_rules = NNAXFUF_Helpers::get_item_share_rules( $item_id );
        $entities    = array();
        if ( $owner_id > 0 ) {
            $owner_user = get_user_by( 'id', $owner_id );
            $entities[] = array(
                'id'           => (int) $owner_id,
                'display_name' => $owner_user ? (string) $owner_user->display_name : __( 'Unknown', 'nnax-frontend-user-files' ),
                'is_role'      => false,
                'is_owner'     => true,
                'owner_rights' => true,
                'can_delete'   => true,
                'starts_on'    => '',
                'expires_on'   => '',
            );
        }

        foreach ( $share_rules as $sid => $rule ) {
            if ( is_numeric( $sid ) ) {
                $uid = (int) $sid;
                if ( $uid <= 0 || $uid === $owner_id ) {
                    continue;
                }

                $user = get_user_by( 'id', $uid );
                if ( ! $user ) {
                    continue;
                }

                $entities[] = array(
                    'id'           => (int) $user->ID,
                    'display_name' => (string) $user->display_name,
                    'user_login'   => (string) $user->user_login,
                    'user_email'   => (string) $user->user_email,
                    'is_role'      => false,
                    'is_owner'     => false,
                    'owner_rights' => ! empty( $rule['owner_rights'] ),
                    'can_delete'   => ! empty( $rule['can_delete'] ),
                    'starts_on'    => ( ! empty( $rule['starts_ts'] ) )
                        ? wp_date( 'Y-m-d', (int) $rule['starts_ts'], wp_timezone() )
                        : '',
                    'expires_on'   => ( ! empty( $rule['expires_ts'] ) )
                        ? wp_date( 'Y-m-d', (int) $rule['expires_ts'], wp_timezone() )
                        : '',
                );
            } elseif ( strpos( (string) $sid, 'role:' ) === 0 ) {
                $role_slug = substr( (string) $sid, 5 );
                $roles     = wp_roles()->get_names();
                $role_name = isset( $roles[ $role_slug ] ) ? $roles[ $role_slug ] : $role_slug;

                $entities[] = array(
                    'id'           => (string) $sid,
                    /* translators: %s: translated WordPress role name. */
                    'display_name' => sprintf( __( 'Role: %s', 'nnax-frontend-user-files' ), translate_user_role( $role_name ) ),
                    'is_role'      => true,
                    'is_owner'     => false,
                    'owner_rights' => ! empty( $rule['owner_rights'] ),
                    'can_delete'   => ! empty( $rule['can_delete'] ),
                    'starts_on'    => ( ! empty( $rule['starts_ts'] ) )
                        ? wp_date( 'Y-m-d', (int) $rule['starts_ts'], wp_timezone() )
                        : '',
                    'expires_on'   => ( ! empty( $rule['expires_ts'] ) )
                        ? wp_date( 'Y-m-d', (int) $rule['expires_ts'], wp_timezone() )
                        : '',
                );
            }
        }

        wp_send_json_success(
            array(
                'owner_id' => (int) $owner_id,
                'current_user_id' => (int) $current_user_id,
                'users' => $entities,
                'share_options' => array(
                    'allow_time_limit'   => $allow_time_limit,
                    'allow_owner_rights' => $allow_owner_rights,
                    'allow_shared_folder_file_delete' => $allow_shared_folder_file_delete,
                ),
            )
        );
    }

    private function count_folder_files_recursive( $folder_id ) {
        $folder_id = (int) $folder_id;
        if ( $folder_id <= 0 ) {
            return 0;
        }

        $count = count( get_posts( array(
            'post_type'      => NNAXFUF_Plugin::CPT,
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'post_parent'    => $folder_id,
            'fields'         => 'ids',
        ) ) );

        $subfolders = get_posts( array(
            'post_type'      => NNAXFUF_Plugin::CPT_FOLDER,
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'post_parent'    => $folder_id,
            'fields'         => 'ids',
        ) );

        foreach ( $subfolders as $subfolder_id ) {
            $count += $this->count_folder_files_recursive( (int) $subfolder_id );
        }

        return (int) $count;
    }

    private function get_item_file_extension( $item_id ) {
        $item_id = (int) $item_id;
        if ( $item_id <= 0 ) {
            return '';
        }

        $file_path = (string) get_post_meta( $item_id, NNAXFUF_Plugin::META_FILEPATH, true );
        $ext = strtolower( (string) pathinfo( $file_path, PATHINFO_EXTENSION ) );

        if ( $ext === '' ) {
            $file_url = (string) get_post_meta( $item_id, NNAXFUF_Plugin::META_FILEURL, true );
            $url_path = (string) wp_parse_url( $file_url, PHP_URL_PATH );
            $ext = strtolower( (string) pathinfo( $url_path, PATHINFO_EXTENSION ) );
        }

        return preg_replace( '/[^a-z0-9]+/i', '', $ext );
    }

    private function get_item_custom_meta_fields( $item_id ) {
        $settings = NNAXFUF_Helpers::get_settings();
        $fields   = isset( $settings['custom_meta_fields'] ) && is_array( $settings['custom_meta_fields'] )
            ? $settings['custom_meta_fields']
            : array();

        $result = array();
        foreach ( $fields as $field ) {
            $meta_key = isset( $field['key'] ) ? (string) $field['key'] : '';
            $label    = isset( $field['label'] ) ? (string) $field['label'] : '';
            $type     = isset( $field['type'] ) ? (string) $field['type'] : 'text';
            if ( $meta_key === '' || $label === '' ) {
                continue;
            }

            $result[] = array(
                'key'   => $meta_key,
                'label' => $label,
                'type'  => $type,
                'value' => (string) get_post_meta( $item_id, $meta_key, true ),
            );
        }

        return $result;
    }

    private function sanitize_custom_meta_value( $type, $raw_value ) {
        $type      = sanitize_key( (string) $type );
        $raw_value = (string) $raw_value;

        switch ( $type ) {
            case 'textarea':
                return sanitize_textarea_field( $raw_value );
            case 'date':
                $value = sanitize_text_field( $raw_value );
                if ( $value !== '' && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
                    return '';
                }
                return $value;
            case 'number':
                $value = trim( str_replace( ',', '.', $raw_value ) );
                if ( $value === '' || ! is_numeric( $value ) ) {
                    return '';
                }
                return $value;
            case 'email':
                return sanitize_email( $raw_value );
            case 'url':
                return esc_url_raw( $raw_value );
            case 'text':
            default:
                return sanitize_text_field( $raw_value );
        }
    }

    private function save_item_custom_meta( $item_id, $posted_custom_meta ) {
        $settings = NNAXFUF_Helpers::get_settings();
        $fields   = isset( $settings['custom_meta_fields'] ) && is_array( $settings['custom_meta_fields'] )
            ? $settings['custom_meta_fields']
            : array();

        $updated = 0;
        $cleared = 0;
        $changed = 0;

        foreach ( $fields as $field ) {
            $meta_key = isset( $field['key'] ) ? (string) $field['key'] : '';
            $type     = isset( $field['type'] ) ? (string) $field['type'] : 'text';
            if ( $meta_key === '' ) {
                continue;
            }

            $raw_value = isset( $posted_custom_meta[ $meta_key ] ) ? $posted_custom_meta[ $meta_key ] : '';
            $value     = $this->sanitize_custom_meta_value( $type, $raw_value );
            $old_value = (string) get_post_meta( $item_id, $meta_key, true );

            if ( $value === '' ) {
                if ( $old_value !== '' ) {
                    $changed++;
                    $cleared++;
                }
                delete_post_meta( $item_id, $meta_key );
            } else {
                if ( $old_value !== $value ) {
                    $changed++;
                    $updated++;
                }
                update_post_meta( $item_id, $meta_key, $value );
            }
        }

        return array(
            'updated' => $updated,
            'cleared' => $cleared,
            'changed' => $changed,
        );
    }

    private function count_subfolders_recursive( $folder_id ) {
        $folder_id = (int) $folder_id;
        if ( $folder_id <= 0 ) {
            return 0;
        }

        $subfolders = get_posts( array(
            'post_type'      => NNAXFUF_Plugin::CPT_FOLDER,
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'post_parent'    => $folder_id,
            'fields'         => 'ids',
        ) );

        $count = count( $subfolders );
        foreach ( $subfolders as $subfolder_id ) {
            $count += $this->count_subfolders_recursive( (int) $subfolder_id );
        }

        return (int) $count;
    }

    private function can_user_access_item( $current_user_id, $item_id ) {
        return NNAXFUF_Helpers::user_can_access_item( $current_user_id, $item_id );
    }

    private function set_share_search_context( $current_user_id, $item_id, $post_type ) {
        $current_user_id = (int) $current_user_id;
        $item_id         = (int) $item_id;
        $post_type       = (string) $post_type;

        if ( $current_user_id <= 0 || $item_id <= 0 ) {
            return;
        }

        if ( $post_type !== NNAXFUF_Plugin::CPT && $post_type !== NNAXFUF_Plugin::CPT_FOLDER ) {
            return;
        }

        update_user_meta(
            $current_user_id,
            '_nnaxfuf_last_share_search_context',
            array(
                'item_id'   => $item_id,
                'post_type' => $post_type,
                'ts'        => time(),
            )
        );
    }

    private function has_valid_share_search_context( $current_user_id ) {
        $current_user_id = (int) $current_user_id;
        if ( $current_user_id <= 0 ) {
            return false;
        }

        $context = get_user_meta( $current_user_id, '_nnaxfuf_last_share_search_context', true );
        if ( ! is_array( $context ) ) {
            return false;
        }

        $item_id   = isset( $context['item_id'] ) ? (int) $context['item_id'] : 0;
        $post_type = isset( $context['post_type'] ) ? (string) $context['post_type'] : '';
        $ts        = isset( $context['ts'] ) ? (int) $context['ts'] : 0;
        if ( $item_id <= 0 || $ts <= 0 ) {
            return false;
        }

        // Keep context short-lived to avoid broad user enumeration.
        if ( ( time() - $ts ) > 900 ) {
            return false;
        }

        $post = get_post( $item_id );
        if ( ! $post ) {
            return false;
        }

        if ( $post_type !== (string) $post->post_type ) {
            return false;
        }

        if ( $post->post_type !== NNAXFUF_Plugin::CPT && $post->post_type !== NNAXFUF_Plugin::CPT_FOLDER ) {
            return false;
        }

        return NNAXFUF_Helpers::can_user_do( $current_user_id, $item_id, 'share' );
    }

    public function ajax_get_roles() {
        if ( ! check_ajax_referer( 'nnaxfuf_search_users', 'nonce', false ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request.', 'nnax-frontend-user-files' ) ), 400 );
        }

        if ( ! is_user_logged_in() ) {
            wp_send_json_error( array( 'message' => __( 'Not logged in.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $settings = NNAXFUF_Helpers::get_settings();
        $share_options = isset( $settings['share_options'] ) && is_array( $settings['share_options'] ) ? $settings['share_options'] : array();
        if ( empty( $share_options['allow_role_sharing'] ) ) {
            wp_send_json_error( array( 'message' => __( 'Role sharing is disabled.', 'nnax-frontend-user-files' ) ), 403 );
        }

        $allowed_slugs = NNAXFUF_Helpers::get_allowed_share_roles_for_user( get_current_user_id() );
        $all_roles = wp_roles()->get_names();

        $results = array();
        foreach ( $all_roles as $slug => $name ) {
            if ( in_array( $slug, $allowed_slugs, true ) ) {
                $results[] = array(
                    'id'           => 'role:' . $slug,
                    /* translators: %s: translated WordPress role name. */
                    'display_name' => sprintf( __( 'Role: %s', 'nnax-frontend-user-files' ), translate_user_role( $name ) ),
                    'is_role'      => true,
                );
            }
        }

        wp_send_json_success( $results );
    }

    public function ajax_get_history() {
        check_ajax_referer( 'nnaxfuf_item_details', 'nonce' );

        $item_id = isset( $_POST['item_id'] ) ? absint( wp_unslash( $_POST['item_id'] ) ) : 0;
        if ( ! NNAXFUF_Helpers::user_can_manage_item( get_current_user_id(), $item_id ) ) {
            wp_send_json_error( array( 'message' => __( 'No permission.', 'nnax-frontend-user-files' ) ) );
        }

        global $wpdb;
        $table_name = $wpdb->prefix . 'nnaxfuf_activities';
        // Static table suffix; keep explicit whitelist for scanner safety.
        $allowed_tables = array( $wpdb->prefix . 'nnaxfuf_activities' );
        if ( ! in_array( $table_name, $allowed_tables, true ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request.', 'nnax-frontend-user-files' ) ), 400 );
        }
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is fixed and allowlisted above; item_id is prepared.
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is fixed and allowlisted above.
                "SELECT * FROM {$table_name} WHERE item_id = %d ORDER BY created_at DESC LIMIT 50",
                $item_id
            )
        );

        $results = array();
        foreach ( $rows as $row ) {
            $user = get_user_by( 'id', $row->user_id );
            $user_name = $user ? $user->display_name : __( 'Unknown System', 'nnax-frontend-user-files' );

            $details = array();
            if ( ! empty( $row->details ) ) {
                $decoded = json_decode( (string) $row->details, true );
                if ( is_array( $decoded ) ) {
                    $details = $decoded;
                }
            }

            $action_label = $row->action;
            $detail_label  = '';
            switch( $row->action ) {
                case 'upload':
                    $action_label = __( 'uploaded file', 'nnax-frontend-user-files' );
                    break;
                case 'share':
                    $action_label = __( 'updated shares', 'nnax-frontend-user-files' );
                    if ( ! empty( $details['shared_with'] ) && is_array( $details['shared_with'] ) ) {
                        $count = count( $details['shared_with'] );
                        $detail_label = sprintf(
                            /* translators: %d: number of shared recipients. */
                            _n( 'shared with %d recipient', 'shared with %d recipients', $count, 'nnax-frontend-user-files' ),
                            $count
                        );
                    }
                    break;
                case 'download':
                    $action_label = ( isset( $details['mode'] ) && (string) $details['mode'] === 'inline' )
                        ? __( 'previewed file', 'nnax-frontend-user-files' )
                        : __( 'downloaded file', 'nnax-frontend-user-files' );
                    break;
                case 'zip_download':
                    $action_label = __( 'downloaded ZIP', 'nnax-frontend-user-files' );
                    $zip_name = isset( $details['zip_name'] ) ? trim( (string) $details['zip_name'] ) : '';
                    $requested_items = isset( $details['requested_items'] ) ? max( 0, (int) $details['requested_items'] ) : 0;
                    $zip_files = isset( $details['zip_files'] ) ? max( 0, (int) $details['zip_files'] ) : 0;
                    $zip_dirs  = isset( $details['zip_dirs'] ) ? max( 0, (int) $details['zip_dirs'] ) : 0;
                    $parts = array();
                    if ( $zip_name !== '' ) {
                        $parts[] = $zip_name;
                    }
                    if ( $requested_items > 0 ) {
                        $parts[] = sprintf(
                            /* translators: %d: number of requested items. */
                            _n( '%d item', '%d items', $requested_items, 'nnax-frontend-user-files' ),
                            $requested_items
                        );
                    }
                    if ( $zip_files > 0 || $zip_dirs > 0 ) {
                        $stats = array();
                        if ( $zip_files > 0 ) {
                            $stats[] = sprintf(
                                /* translators: %d: number of files in ZIP. */
                                _n( '%d file', '%d files', $zip_files, 'nnax-frontend-user-files' ),
                                $zip_files
                            );
                        }
                        if ( $zip_dirs > 0 ) {
                            $stats[] = sprintf(
                                /* translators: %d: number of folders in ZIP. */
                                _n( '%d folder', '%d folders', $zip_dirs, 'nnax-frontend-user-files' ),
                                $zip_dirs
                            );
                        }
                        $parts[] = implode( ', ', $stats );
                    }
                    $detail_label = implode( ' - ', array_filter( $parts ) );
                    break;
                case 'rename':
                    $old_title = isset( $details['old_title'] ) ? (string) $details['old_title'] : '';
                    $new_title = isset( $details['new_title'] ) ? (string) $details['new_title'] : '';

                    if ( $old_title !== '' || $new_title !== '' ) {
                        $detail_label = sprintf(
                            /* translators: 1: previous item title, 2: new item title. */
                            __( '%1$s → %2$s', 'nnax-frontend-user-files' ),
                            $old_title !== '' ? $old_title : __( 'Unknown', 'nnax-frontend-user-files' ),
                            $new_title !== '' ? $new_title : __( 'Unknown', 'nnax-frontend-user-files' )
                        );
                    }
                    $action_label = __( 'renamed item', 'nnax-frontend-user-files' );
                    break;
                case 'trash':    $action_label = __( 'moved to trash', 'nnax-frontend-user-files' ); break;
                case 'trash_recursive': $action_label = __( 'moved folder and contents to trash', 'nnax-frontend-user-files' ); break;
                case 'move_file': $action_label = __( 'moved file', 'nnax-frontend-user-files' ); break;
                case 'move_folder': $action_label = __( 'moved folder', 'nnax-frontend-user-files' ); break;
                case 'restore': $action_label = __( 'restored item', 'nnax-frontend-user-files' ); break;
                case 'permanent_delete': $action_label = __( 'deleted item permanently', 'nnax-frontend-user-files' ); break;
                case 'bulk_delete': $action_label = __( 'deleted item in bulk action', 'nnax-frontend-user-files' ); break;
                case 'bulk_move': $action_label = __( 'moved item in bulk action', 'nnax-frontend-user-files' ); break;
                case 'bulk_delete_recursive': $action_label = __( 'deleted item in recursive bulk action', 'nnax-frontend-user-files' ); break;
                case 'share_folder': $action_label = __( 'updated folder shares', 'nnax-frontend-user-files' ); break;
                case 'sync_folder_share': $action_label = __( 'synced folder shares', 'nnax-frontend-user-files' ); break;
                case 'ownership_transfer':
                    $from_owner_id = isset( $details['from_owner_id'] ) ? (int) $details['from_owner_id'] : 0;
                    $to_owner_id   = isset( $details['to_owner_id'] ) ? (int) $details['to_owner_id'] : 0;
                    $total_items   = isset( $details['total'] ) ? max( 0, (int) $details['total'] ) : 0;

                    $from_name = __( 'Unknown user', 'nnax-frontend-user-files' );
                    if ( $from_owner_id > 0 ) {
                        $from_user = get_user_by( 'id', $from_owner_id );
                        if ( $from_user ) {
                            $from_name = (string) $from_user->display_name;
                        }
                    }

                    $to_name = __( 'Unknown user', 'nnax-frontend-user-files' );
                    if ( $to_owner_id > 0 ) {
                        $to_user = get_user_by( 'id', $to_owner_id );
                        if ( $to_user ) {
                            $to_name = (string) $to_user->display_name;
                        }
                    }

                    if ( $from_owner_id > 0 && $to_owner_id > 0 ) {
                        $action_label = __( 'transferred ownership', 'nnax-frontend-user-files' );
                        $detail_label = sprintf(
                            /* translators: 1: previous owner display name, 2: new owner display name. */
                            __( 'from %1$s to %2$s', 'nnax-frontend-user-files' ),
                            $from_name,
                            $to_name
                        );
                    } elseif ( $to_owner_id > 0 && $total_items > 0 ) {
                        $action_label = __( 'transferred ownership', 'nnax-frontend-user-files' );
                        $detail_label = sprintf(
                            /* translators: 1: new owner display name, 2: number of transferred items. */
                            __( 'to %1$s (%2$d item(s))', 'nnax-frontend-user-files' ),
                            $to_name,
                            $total_items
                        );
                    } elseif ( $to_owner_id > 0 ) {
                        $action_label = __( 'transferred ownership', 'nnax-frontend-user-files' );
                        $detail_label = sprintf(
                            /* translators: %s: user display name. */
                            __( 'to %s', 'nnax-frontend-user-files' ),
                            $to_name
                        );
                    } else {
                        $action_label = __( 'transferred ownership', 'nnax-frontend-user-files' );
                    }
                    break;
                case 'open_detail': $action_label = __( 'opened details', 'nnax-frontend-user-files' ); break;
            }

            $action_label = apply_filters( 'nnaxfuf_history_action_label', $action_label, $row, $details );
            $detail_label  = apply_filters( 'nnaxfuf_history_detail_label', $detail_label, $row, $details );

            $results[] = array(
                'user'   => $user_name,
                'action' => $action_label,
                'details' => $detail_label,
                'date'   => mysql2date( 'd.m.Y H:i', $row->created_at ),
            );
        }

        wp_send_json_success( $results );
    }

}
