<?php

/**
 * Front-end - File Upload & Handle Submission
 *
 * This class render list table and columns for file submissions
 *
 * @package EASYFUA
 */
// Prevent direct access.
defined( 'ABSPATH' ) || exit;
if ( !class_exists( 'EASYFUA_File_Handler' ) ) {
    /**
     * List table main class
     */
    class EASYFUA_File_Handler {
        private $debug;

        /**
         * Store user information.
         *
         * @var string $user
         */
        public $user = '';

        /**
         * Custom nonce name.
         *
         * @var string
         */
        const NONCE_NAME = 'easyfua-security-nonce';

        /**
         * File Handler main constructor mostly hooks.
         */
        public function __construct() {
            // File Upload - Ajax Request.
            add_action( 'wp_ajax_easyfua_upload', array($this, 'upload') );
            add_action( 'wp_ajax_nopriv_easyfua_upload', array($this, 'upload') );
            // Delete File - Ajax Request.
            add_action( 'wp_ajax_easyfua_delete', array($this, 'delete') );
            add_action( 'wp_ajax_nopriv_easyfua_delete', array($this, 'delete') );
            // rest API.
            add_action( 'rest_api_init', array($this, 'rest_api_endpoint') );
            // Get user info for logged-in user.
            $this->user = ( is_user_logged_in() ? wp_get_current_user() : null );
            // If debug is enabled.
            $this->debug = easyfua_settings( 'debug_mode' );
        }

        /**
         * Process file upload, handle sanitizatio and file upload validation.
         *
         * @return never
         */
        public function upload() {
            global $wp_filesystem;
            // check and verify ajax request.
            check_ajax_referer( self::NONCE_NAME, 'security' );
            // Modify wp_upload_dir and use our plugin custom folder.
            add_filter( 'upload_dir', 'easyfua_modify_upload_dir' );
            // Create and Get upload dir.
            $path = easyfua_dir();
            // Get allowed ext list @expected : png,jpeg,jpg.
            $allowed_types = str_replace( array(', ', ','), '|', easyfua_settings( 'supported_types' ) );
            // File size limit.
            $max_file_size = preg_replace( '/\\s*mb$/i', '', easyfua_settings( 'max_file_size' ) );
            $size_limit = (int) trim( ( $max_file_size ? trim( $max_file_size ) : 5 ) ) * 1024 * 1024;
            // phpcs:ignore -- Raw $_FILES is required for wp_handle_upload().
            $file = ( isset( $_FILES['upload-file'] ) ? $_FILES['upload-file'] : null );
            // Sanitized $_FILES array.
            if ( isset( $file['name'] ) ) {
                $file['name'] = sanitize_file_name( $file['name'] );
                $file['type'] = sanitize_mime_type( $file['type'] );
                $file['tmp_name'] = sanitize_text_field( $file['tmp_name'] );
                $file['error'] = (int) $file['error'];
                $file['size'] = (int) $file['size'];
            }
            // Clean special character of tmp name file.
            $tmp_file = preg_replace( '/^.*?:\\/\\//', '', $file['tmp_name'] );
            // Check if logged-in user or guest can upload.
            if ( !easyfua_can_upload() ) {
                wp_send_json_error( ( easyfua_settings( 'error_upload_restriction' ) ? esc_html( easyfua_settings( 'error_upload_restriction' ) ) : easyfua_error( 'restriction' ) ) );
            }
            // Tells whether the file was uploaded via HTTP POST.
            if ( !is_uploaded_file( sanitize_text_field( $tmp_file ) ) ) {
                $failed_error = easyfua_settings( 'error_failed_to_upload' );
                wp_send_json_error( '(' . (int) $file['error'] . ') ' . (( $failed_error ? esc_html( $failed_error ) : easyfua_error( 'failed_upload' ) )) );
            }
            $filename = sanitize_file_name( $file['name'] );
            // Get and sanitize filename.
            $supported_type = ( $allowed_types ? $allowed_types : easyfua_ext() );
            // Get allowed extension.
            $file_type_pattern = easyfua_file_types( $supported_type );
            // Create type pattern for anti script.
            $extension = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
            // Get file extension.
            // validate file type.
            if ( !preg_match( $file_type_pattern, $filename ) || !easyfua_validate_type( $extension, $supported_type ) ) {
                wp_send_json_error( ( easyfua_settings( 'error_invalid_file' ) ? esc_html( easyfua_settings( 'error_invalid_file' ) ) : easyfua_error( 'invalid_type' ) ) );
            }
            // validate file size limit.
            if ( $file['size'] > $size_limit ) {
                wp_send_json_error( ( easyfua_settings( 'error_files_too_large' ) ? esc_html( easyfua_settings( 'error_files_too_large' ) ) : easyfua_error( 'large_file' ) ) );
            }
            // Check if string is ascii then proceed with antiscript function ( remove or clean filename ).
            if ( easyfua_ascii( $filename ) ) {
                $filename = easyfua_anti_script( $filename );
                // convert risky file to .txt.
            }
            // @todo: Randomize filename here..
            // Add filter on upload file name.
            $new_name = apply_filters( 'easyfua_upload_file_name', $filename );
            // Assign new filename.
            $file['name'] = sanitize_file_name( $new_name );
            // Load admin file for wp_handle_upload function to work.
            if ( !function_exists( 'wp_handle_upload' ) ) {
                require_once ABSPATH . 'wp-admin/includes/file.php';
            }
            // Begin uploading...
            $uploaded_file = wp_handle_upload( $file, array(
                'test_form' => false,
            ) );
            // Show error.
            if ( isset( $uploaded_file['error'] ) ) {
                $failed_error = easyfua_settings( 'error_failed_to_upload' );
                if ( $failed_error ) {
                    wp_send_json_error( esc_html( $failed_error ) );
                } else {
                    wp_send_json_error( $uploaded_file['error'] );
                    // WordPress Upload Error.
                }
            } else {
                // Setup path and files url.
                $files = array(
                    'path' => wp_basename( dirname( $uploaded_file['file'] ) ),
                    'file' => wp_basename( $uploaded_file['file'] ),
                );
                // Custom filter after upload.
                $files = apply_filters( 'easyfua_after_upload_files', $files );
                // Store temporarily in database with a status of 'temp'.
                $file_id = $this->store_files( $uploaded_file );
                if ( $file_id ) {
                    $files['file'] = sprintf( '%d/%s', (int) $file_id, $files['file'] );
                    //output: "{file_id}/filename.jpg"
                }
                // Setup media response via helper function.
                $files = easyfua_media_response( $files['path'], $files['file'] );
                // Custom action after upload.
                do_action( 'easyfua_after_upload', $files );
                // Send json file data.
                wp_send_json_success( $files );
            }
            // Remove Modify upload_dir filte after the upload.
            remove_filter( 'upload_dir', 'easyfua_modify_upload_dir' );
            die;
        }

        /**
         * Handle file deletion via ajax request using wp_ajax_ hook.
         *
         * @return never
         */
        public function delete() {
            global $wpdb;
            // check and verify ajax request.
            check_ajax_referer( self::NONCE_NAME, 'security' );
            // Get folder directory (dir and url).
            $dir = easyfua_dir();
            // Block curl request.
            $user_agent = ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '' );
            if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && strpos( $user_agent, 'curl' ) !== false ) {
                wp_send_json_error( 'Request blocked: cURL access is forbidden.' );
            }
            // Sanitize Path.
            $path = ( isset( $_POST['path'] ) ? sanitize_text_field( wp_unslash( $_POST['path'] ) ) : null );
            $file_id = dirname( $path );
            // Make sure path is set.
            if ( !is_null( $path ) ) {
                // Check valid filename & extensions.
                if ( preg_match( '/wp-|(\\.php|\\.exe|\\.js|\\.phtml|\\.cgi|\\.aspx|\\.asp|\\.bat)(?!_\\.txt$)/', $path ) ) {
                    wp_send_json_error( esc_html__( 'Error: File not safe', 'easy-file-upload-approval' ) );
                }
                // Validate path if it's match on the current folder.
                $file_name = sanitize_file_name( wp_basename( $path ) );
                $current_path = trailingslashit( $dir['dir'] ) . $file_name;
                // Validate path.
                if ( !file_exists( $current_path ) || preg_match( '#\\.\\.[/\\\\]#', $path ) ) {
                    wp_send_json_error( esc_html__( 'Error: Unauthorized Request!', 'easy-file-upload-approval' ) );
                }
                // Concatenate path and upload directory.
                $file_path = realpath( $current_path );
                // Check if is in the correct upload_dir.
                if ( strpos( $file_path, $dir['dir'] ) === false ) {
                    wp_send_json_error( esc_html__( 'It\'s not a valid upload directory', 'easy-file-upload-approval' ) );
                }
                // Check if file exists.
                if ( !file_exists( $file_path ) ) {
                    wp_send_json_error( esc_html__( 'Unable to find file or file not exists.', 'easy-file-upload-approval' ) );
                }
                // Delete record from database.
                $deleted = $wpdb->delete( $wpdb->prefix . EASYFUA_TABLE_FILES, array(
                    'id' => absint( $file_id ),
                ), array('%d') );
                // Send a confirmation message.
                if ( false !== $deleted && wp_delete_file( $file_path ) ) {
                    wp_send_json_success( 'File Deleted!' );
                }
                wp_send_json_error( 'Unable to delete file.' );
            }
            die;
        }

        /**
         * Store temporary files in the database.
         *
         * @param array $file  File path and dir of uploaded file.
         * @return void|int Record or file id.
         */
        public function store_files( $file = '' ) {
            global $wpdb;
            // No files bail early.
            if ( empty( $file ) ) {
                return;
            }
            // Prepare table name.
            $table_name = $wpdb->prefix . EASYFUA_TABLE_FILES;
            // Insert data safely.
            $inserted = $wpdb->insert( $table_name, array(
                'file_name'    => sanitize_file_name( wp_basename( $file['file'] ) ),
                'file_url'     => esc_url_raw( $file['url'] ),
                'file_type'    => sanitize_text_field( $file['type'] ),
                'file_size'    => absint( filesize( $file['file'] ) ),
                'submitted_at' => current_time( 'mysql' ),
                'admin_note'   => '',
            ), array(
                '%s',
                '%s',
                '%s',
                '%d',
                '%s',
                '%s'
            ) );
            // Return file id.
            if ( $inserted ) {
                return $wpdb->insert_id;
            }
        }

        /**
         * Submission REST API end point.
         *
         * @return void
         */
        public function rest_api_endpoint() {
            register_rest_route( 
                EASYFUA_PLUGIN_NAME . '/v1',
                // Namespace.
                '/submit-form',
                // Route.
                array(
                    'methods'             => 'POST',
                    'callback'            => array($this, 'handle_submission'),
                    'permission_callback' => '__return_true',
                )
             );
        }

        /**
         * Process form submission, store files in the database.
         *
         * @param object $request an API request.
         * @return void
         */
        public function handle_submission( $request ) {
            global $wpdb;
            // Get all params and settings.
            $params = $request->get_params();
            $required = easyfua_settings( 'required' );
            $name = easyfua_attributes( 'name' ) . '_files';
            $file_ids = array();
            $errors = array();
            $has_field_errors = false;
            // Default submission id.
            $submission_id = 0;
            // Get captcha token.
            $captcha_token = ( isset( $params['easyfua-recaptcha-response'] ) ? $params['easyfua-recaptcha-response'] : null );
            // DB Tables.
            $submission_table = $wpdb->prefix . EASYFUA_TABLE_SUBMISSIONS;
            $files_table = $wpdb->prefix . EASYFUA_TABLE_FILES;
            // Getting the files.
            $files = ( isset( $params[$name] ) ? $params[$name] : array() );
            // Get minimum file option.
            $min_file = absint( easyfua_settings( 'min_upload' ) );
            // Honeypot field.
            $honey_pot = ( isset( $params['easyfua_hp'] ) ? sanitize_text_field( wp_unslash( $params['easyfua_hp'] ) ) : '' );
            // Required field validation.
            if ( empty( $files ) && 'yes' === $required ) {
                $error = ( easyfua_settings( 'error_required' ) ? esc_html( easyfua_settings( 'error_required' ) ) : easyfua_error( 'required' ) );
                $errors[] = array(
                    'global' => $error,
                );
            } elseif ( is_array( $files ) && count( $files ) < (int) $min_file ) {
                $error = ( easyfua_settings( 'error_min_file' ) ? esc_html( easyfua_settings( 'error_min_file' ) ) : easyfua_error( 'minFileUpload' ) );
                $errors[] = array(
                    'global' => sprintf( $error . ' %s.', $min_file ),
                );
            }
            // Verify and see if honeypot hidden has a value.
            if ( !empty( $honey_pot ) ) {
                $errors[] = array(
                    'global' => esc_html__( 'Spam detected. Submission blocked.', 'easy-file-upload-approval' ),
                );
            }
            // Validate Name.
            if ( 'yes' !== easyfua_settings( 'hide_name_field' ) && 'yes' === easyfua_settings( 'name_required' ) ) {
                $name = $params['easyfua_name'] ?? null;
                if ( empty( $name ) ) {
                    $has_field_errors = true;
                    $errors[] = array(
                        'el'  => '#easyfua-name',
                        'msg' => esc_html__( 'Please enter your name.', 'easy-file-upload-approval' ),
                    );
                }
            }
            // Validate Email Address.
            if ( 'yes' !== easyfua_settings( 'hide_email_field' ) && 'yes' === easyfua_settings( 'email_required' ) ) {
                $email = $params['easyfua_email'] ?? null;
                // Email is required.
                if ( empty( $email ) ) {
                    $has_field_errors = true;
                    $errors[] = array(
                        'el'  => '#easyfua-email',
                        'msg' => esc_html__( 'Please enter your email address.', 'easy-file-upload-approval' ),
                    );
                } elseif ( !is_email( $email ) ) {
                    $has_field_errors = true;
                    $errors[] = array(
                        'el'  => '#easyfua-email',
                        'msg' => esc_html__( 'Please enter valid email address.', 'easy-file-upload-approval' ),
                    );
                }
            }
            // Show review fields error.
            if ( $has_field_errors ) {
                $errors[] = array(
                    'global' => esc_html__( 'There are errors in the form. Please review and try again.', 'easy-file-upload-approval' ),
                );
            }
            // Filter for errors.
            $errors = apply_filters( 'easyfua_submission_errors', $errors );
            // Send json error.
            if ( !empty( $errors ) ) {
                wp_send_json_error( $errors );
            }
            // Google Recaptcha Validation.
            if ( $captcha_token && easyfua_settings( 'secret_key' ) ) {
                $ip_address = ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '' );
                $response = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', array(
                    'body' => array(
                        'secret'   => easyfua_settings( 'secret_key' ),
                        'response' => $captcha_token,
                        'remoteip' => $ip_address,
                    ),
                ) );
                // Validate result.
                if ( !is_wp_error( $response ) ) {
                    $score = apply_filters( 'easyfua_captcha_score', 0.5 );
                    $result = json_decode( wp_remote_retrieve_body( $response ), true );
                    if ( empty( $result['success'] ) || $result['score'] < $score || 'submit' !== $result['action'] ) {
                        wp_send_json_error( esc_html__( 'Your submission was flagged as spam. Please try again.', 'easy-file-upload-approval' ) );
                    }
                }
            }
            // Get individual file ids.
            if ( !empty( $files ) && is_array( $files ) ) {
                foreach ( $files as $file ) {
                    $file_ids[] = absint( dirname( $file ) );
                }
            }
            // Get user id.
            $user_id = '';
            if ( 'yes' === easyfua_settings( 'login_required' ) || $this->user ) {
                $user_id = ( isset( $this->user->ID ) ? $this->user->ID : null );
            }
            // Extra Fields.
            $extra_fields = array();
            // Field Name.
            if ( isset( $params['easyfua_name'] ) && !empty( $params['easyfua_name'] ) ) {
                $extra_fields['name'] = sanitize_text_field( $params['easyfua_name'] );
            }
            // Field Email.
            if ( isset( $params['easyfua_email'] ) && !empty( $params['easyfua_email'] ) ) {
                $extra_fields['email'] = sanitize_email( $params['easyfua_email'] );
            }
            // Custom action before submitted.
            do_action( 'easyfua_before_post_submitted', $files );
            // Begin inserting to submissions table.
            $added = $wpdb->insert( $submission_table, array(
                'user_id'      => $user_id,
                'form_data'    => wp_json_encode( $extra_fields ),
                'total_files'  => count( $files ),
                'submitted_at' => current_time( 'mysql' ),
            ), array(
                '%d',
                '%s',
                '%s',
                '%s'
            ) );
            // Update submission_id in easyfua_files.
            if ( $added && !empty( $file_ids ) ) {
                $submission_id = (int) $wpdb->insert_id;
                $ids = implode( ',', array_fill( 0, count( $file_ids ), '%d' ) );
                $updated = $wpdb->query( $wpdb->prepare( 
                    "UPDATE {$files_table} SET `submission_id` = %s, `file_status` = 'pending' WHERE id IN ({$ids}) ",
                    // phpcs:ignore
                    array_merge( array($submission_id), $file_ids )
                 ) );
            }
            // Add custom hook after user successfully submitted. (notify user and admin).
            do_action( 'easyfua_after_post_submitted', $submission_id, $files );
            // Send success message.
            wp_send_json_success( esc_html__( 'Your files have been submitted and are awaiting admin review.', 'easy-file-upload-approval' ) );
        }

    }

}