<?php

namespace PWH_DCFH\App\Frontend\Request;

use Exception;
use PWH_DCFH\App\Base\PWH_DCFH_Strings;
use PWH_DCFH\App\Frontend\Controllers\PWH_DCFH_Email_Handler;

if (!defined('ABSPATH')) {
    exit;
}
if (!class_exists('PWH_DCFH_Process_Form_Request')) {
    class PWH_DCFH_Process_Form_Request
    {
        private $post_request;

        private $post_repository;

        private $upload_tmp_dir;

        private $upload_tmp_url;

        private $process_request = true;

        /**
         * Initializer Of The Class
         * Add/Remove Necessary Actions/Filters
         */
        public function init()
        {
            add_filter('et_contact_page_email_to', [$this, 'maybe_filter_page_email_to'], 10);
            add_filter('et_contact_page_headers', [$this, 'maybe_filter_contact_page_headers'], 10, 3);
            add_action('wp_ajax_upload_file', [$this, 'maybe_upload_file']);
            add_action('wp_ajax_nopriv_upload_file', [$this, 'maybe_upload_file']);
            add_action('wp_ajax_remove_file', [$this, 'maybe_remove_file']);
            add_action('wp_ajax_nopriv_remove_file', [$this, 'maybe_remove_file']);
            add_action('wp_ajax_nopriv_sync_ics_url', [$this, 'maybe_remove_file']);
            add_action('wp_ajax_save_digital_signature', [$this, 'maybe_save_digital_signature']);
            add_action('wp_ajax_nopriv_save_digital_signature', [$this, 'maybe_save_digital_signature']);
            add_action('wp_ajax_delete_digital_signature', [$this, 'maybe_delete_digital_signature']);
            add_action('wp_ajax_nopriv_delete_digital_signature', [$this, 'maybe_delete_digital_signature']);
            add_action('et_pb_contact_form_submit', [$this, 'maybe_log_entries'], 10, 3);
            $this->upload_tmp_dir = pwh_dcfh_file_helpers()::get_tmp_upload_dir();
            $this->upload_tmp_url = pwh_dcfh_file_helpers()::get_tmp_upload_url();
        }

        /**
         * Maybe Filter Email To
         *
         * @param $email
         *
         * @return mixed|string
         * @since 1.4
         */
        public function maybe_filter_page_email_to($email)
        {
            $this->post_request = new PWH_DCFH_Post_Request();
            $this->post_repository = PWH_DCFH_Post_Repository::instance();
            $email_handler = PWH_DCFH_Email_Handler::instance();
            /**
             * Admin Email Disabled
             * @since 1.3.1
             */
            if (!$this->post_repository->is_admin_email_enabled) {
                $email = '';
            }
            /**
             * If Threshold Reached Don't Process Form
             * @since 1.6.5
             */
            if ($this->post_repository->is_threshold_reached) {
                $this->process_request = false;
            }
            /**
             * Is Honey Pot Filled?
             * @since 1.4
             */
            if ($this->post_request->is_honeypot_filled()) {
                $this->process_request = false;
            }
            /**
             * Is Google Recaptcha Success?
             * @since 1.7
             */
            if (!$this->post_request->is_googlerecaptchav2_success()) {
                $this->process_request = false;
            }
            /**
             * Is Cloudflare Turnstile Success?
             * @since 1.7
             */
            if (!$this->post_request->is_cloudflare_turnstile_success()) {
                $this->process_request = false;
            }
            /**
             * Is blocklisted Email?
             * @since 1.4.1
             */
            if ($email_handler->is_blacklisted_email()) {
                $this->process_request = false;
            }
            /**
             * Set Admin Mail From Email
             * @since 1.5
             */
            if ($this->post_repository->is_admin_email_from_submitter_email) {
                $email_handler->set_mail_from($this->post_repository->sender_email);
            }
            /**
             * Set Admin Mail From Name
             * @since 1.4.1
             */
            $admin_email_from_name = $this->post_repository->get_admin_email_from_name();
            $admin_email_from_name = pwh_dcfh_email_merge_tags_handler()::get_merge_tags_values($admin_email_from_name);
            $email_handler->set_mail_from_name($admin_email_from_name);
            /**
             * Get Admin Emails Routing
             * @since 1.4.3
             */
            $admin_emails_routing = $email_handler->get_admin_emails_routing();
            if (!empty($admin_emails_routing)) {
                $email = $admin_emails_routing;
            }
            /**
             * Handles the process request, saves entries to the database,
             * moves files to the appropriate location, updates the repository with attachments,
             * handles email merge tags, and sends the payload to the endpoint.
             */
            if ($this->process_request && $this->post_repository->save_entry_to_db && !empty($this->post_repository->contact_form_id)) {
                $this->save_entry();
                $this->post_repository->attachments = $this->post_repository->save_files_to_media
                    ? $this->post_request->move_files_to_media_library($this->post_repository->entry_number)
                    : $this->post_request->move_files_to_form_dir();
                $this->post_request->post_payload_endpoint();
            }
            /**
             * Adds a filter to modify outgoing emails before sending.
             */
            add_filter('wp_mail', [$this, 'maybe_filter_wp_mail'], 999);
            /**
             * Don't Send Admin/Confirmation Email
             * @since 1.4.1
             */
            $this->maybe_disabled_emailing();

            return $email;
        }

        /**
         * Filter et_contact_page_headers
         *
         * @param $headers
         * @param $contact_name
         * @param $contact_email
         *
         * @return mixed
         */
        public function maybe_filter_contact_page_headers($headers, $contact_name, $contact_email)
        {
            // Return If Send Email False
            if (!$this->process_request) {
                return $headers;
            }
            /**
             * If Zapier settings enabled send email BCC to zapier
             * @since 1.0.0
             */
            if ($this->post_repository->is_zapier_enabled) {
                $headers[] = sprintf('Bcc: %s', sanitize_email($this->post_repository->zapier_mailbox_address));
            }
            /**
             * If Pabbly settings enabled send email BCC to zapier
             * @since 1.0.3
             */
            if ($this->post_repository->is_pabbly_enabled) {
                $headers[] = sprintf('Bcc: %s', sanitize_email($this->post_repository->pabbly_mailbox_address));
            }
            /**
             * Admin Email Cc
             * @since 1.1
             */
            $headers = $this->post_repository->get_admin_cc_headers($headers);
            $headers = $this->post_repository->get_admin_bcc_headers($headers);
            /**
             * Admin Formatted Message
             * @since 1.3
             */
            if ($this->post_repository->is_custom_message_richtext) {
                $headers[] = 'Content-Type: text/html; charset=UTF-8';
            }

            return $headers;
        }

        /**
         * Process Save Entries
         * @since 1.0.0
         */
        private function save_entry()
        {
            add_filter('wp_insert_post_data', [$this, 'maybe_modify_post_dates'], PHP_INT_MAX, 2);
            global $current_user;
            $user_id = $current_user->ID ?? 0;
            $post_status = 'draft';
            $sender_email = $this->post_repository->sender_email;
            $is_spam_submitter = pwh_dcfh_db()::is_spam($sender_email);
            if ($is_spam_submitter) {
                $post_status = 'spam';
            }
            $post_id = wp_insert_post([
                'post_type' => 'pwh_dcfh',
                'post_status' => $post_status,
                'post_name' => uniqid('entry-', true),
                'post_content' => maybe_serialize($this->post_repository->contact_form_fields),
                'post_excerpt' => maybe_serialize($this->post_repository->contact_form_data),
                'post_author' => $user_id,
                'comment_status' => 'closed',
                'ping_status' => 'closed',
                'post_date' => current_time('mysql'),
                'post_date_gmt' => current_time('mysql', 1),
                'post_modified' => '0000-00-00 00:00:00',
                'post_modified_gmt' => '0000-00-00 00:00:00',
            ], true);
            if (!is_wp_error($post_id)) {
                $meta_data = [
                    '_pwh_dcfh_page_id' => get_the_ID(),
                    '_pwh_dcfh_contact_form_id' => $this->post_repository->contact_form_id,
                    '_pwh_dcfh_form_fields' => $this->post_repository->contact_form_fields_str,
                    '_pwh_dcfh_contact_email' => $sender_email,
                    '_pwh_dcfh_referer_url' => $this->post_request->get_referer_url(),
                ];
                if ($this->post_repository->collect_ip_useragent_details) {
                    $meta_data['_pwh_dcfh_ip_address'] = pwh_dcfh_helpers()::get_ip_address();
                    $meta_data['_pwh_dcfh_user_agent'] = pwh_dcfh_helpers()::get_user_agent();
                }
                foreach ($meta_data as $key => $value) {
                    update_post_meta($post_id, $key, $value);
                }
                $this->post_repository->entry_number = $post_id;
                wp_update_post(['ID' => $post_id, 'post_title' => 'entry-'.$post_id]);
                pwh_dcfh_db()::set_post_status_and_modified($post_id, $post_status);
                // Update Form Settings > Total Entries
                $contact_form_entries = pwh_dcfh_db()::get_contact_form_total_entries($this->post_repository->contact_form_id);
                pwh_dcfh_db()::update_contact_form_total_entries($this->post_repository->contact_form_id, $contact_form_entries);
                remove_filter('wp_insert_post_data', [$this, 'maybe_modify_post_dates'], PHP_INT_MAX);
            }
        }

        /**
         * Modiy Post Data
         *
         * @param $data
         * @param $post_arr
         *
         * @return mixed
         */
        public function maybe_modify_post_dates($data, $post_arr)
        {
            if (!empty($post_arr['post_modified']) && !empty($post_arr['post_modified_gmt'])) {
                $data['post_modified'] = '0000-00-00 00:00:00';
                $data['post_modified_gmt'] = '0000-00-00 00:00:00';
            }

            return $data;
        }

        /**
         * Filter WP Mail
         * @return void
         */
        public function maybe_filter_wp_mail($attr)
        {
            $contact_form_num = $this->post_request->contact_form_num;
            $_wpnonce = isset($_POST["_wpnonce-et-pb-contact-form-submitted-$contact_form_num"]) && wp_verify_nonce(sanitize_text_field($_POST["_wpnonce-et-pb-contact-form-submitted-$contact_form_num"]),
                    'et-pb-contact-form-submit');
            if ($_wpnonce && array_key_exists("et_pb_contactform_submit_$contact_form_num", wp_unslash($_POST))) {
                if (!isset($_POST["pwh_dcfh_et_pb_contactform_submit_$contact_form_num"])) {
                    $_POST["pwh_dcfh_et_pb_contactform_submit_$contact_form_num"] = 'et_contact_proccess';
                }
                unset($_POST["et_pb_contactform_submit_$contact_form_num"]);
                $message = $attr['message'];
                /*
                * This section of code checks if the message contains the string "et_pb_html_field" using regular expression.
                * If it does, it replaces everything before "et_pb_html_field" with an empty string.
                */
                if (str_contains($message, "et_pb_html_field")) {
                    $message = preg_replace('/.*et_pb_html_field/', '', $message);
                }
                /**
                 * Retrieve files from temporary directory and attach them to the post.
                 * If sending files as attachments is enabled and there are files in the temporary directory,
                 * attach them to the post and set a message about the attached files.
                 */
                $attachments = $this->post_request->get_files_from_temp_dir();
                $attached_file_message = null;
                if (!empty($attachments['uploaded_dir']) && $this->post_repository->send_files_as_attachment) {
                    $attr['attachments'] = $attachments['uploaded_dir'];
                    $attached_file_message = $this->post_repository->attached_files_message;
                }
                // Email Subject Merge Tags
                $subject = $this->post_repository->admin_email_subject;
                if (!empty($subject)) {
                    $subject = pwh_dcfh_email_merge_tags_handler()::get_merge_tags_values($subject);
                    $attr['subject'] = $subject;
                }
                /**
                 * Extract merge tag values from the given message.
                 */
                $message = pwh_dcfh_email_merge_tags_handler()::get_merge_tags_values($message);
                /**
                 * Replaces the placeholder '%%dcfh_all_fields%%'
                 * in the given message with dynamic content fetched using the pwh_dcfh_email_merge_tags_handler().
                 */
                if (preg_match('/%%dcfh_all_fields%%/i', $message)) {
                    $message = str_replace("%%dcfh_all_fields%%", pwh_dcfh_email_merge_tags_handler()::get_all_fields_data($this->post_repository->is_custom_message_richtext), $message);
                }
                /**
                 * Replaces file names in a message with their corresponding URLs.
                 * Iterates through an array of file URLs, extracts the file names, and replaces them in a given message.
                 * The replacement can be either a hyperlink to the URL (if specified as richtext) or the URL followed by a line break.
                 */
                if (!empty($attachments['uploaded_urls'])) {
                    foreach ($attachments['uploaded_urls'] as $url) {
                        $filename_with_ext = wp_basename($url);
                        $filename = pathinfo($filename_with_ext, PATHINFO_FILENAME); // Remove file extension
                        $replacement = $this->post_repository->is_custom_message_richtext
                            ? "<a href='$url'>$filename</a><br>"
                            : "$url\n";
                        $message = str_replace([$filename_with_ext, ','], [$replacement, ''], $message);
                    }
                }
                /*
                * This section of code checks for blacklisted keywords in both the subject and message.
                * If any blacklisted keyword is found in either the subject or message,
                * the email recipient is set to false effectively blocking the email.
                */
                $blacklisted_keywords = array_filter(array_map('trim', explode(',', strtolower(et_get_option('pwh_dcfh_blacklisted_keywords')))));
                if (!empty($blacklisted_keywords)) {
                    foreach ($blacklisted_keywords as $keyword) {
                        $keyword = strtolower($keyword);
                        if (stripos(strtolower($subject), $keyword) !== false
                            || stripos(strtolower($message), $keyword) !== false) {
                            $attr['to'] = false;
                            wp_delete_post($this->post_repository->entry_number, true);
                            break;
                        }
                    }
                }
                // Replaced Final Message
                $attr['message'] = $message.PHP_EOL.$attached_file_message;

            }

            return $attr;
        }

        /**
         * Process File Upload
         * @since 1.0.0
         */
        public function maybe_upload_file()
        {
            $file_error_types = [
                1 => __('The file you tried to upload is too large. Please choose a smaller file.', 'pwh-dcfh'),
                2 => __('The file size exceeds the maximum allowed. Please choose a smaller file.', 'pwh-dcfh'),
                3 => __('The file upload was incomplete. Please try uploading the file again.', 'pwh-dcfh'),
                4 => __('No file was selected for upload. Please choose a file and try again.', 'pwh-dcfh'),
                6 => __('Temporary folder is missing. Please contact the system administrator.', 'pwh-dcfh'),
                7 => __('Failed to save the file to disk. Please try again or contact support.', 'pwh-dcfh'),
                8 => __('A PHP extension prevented the file upload. Please try again or contact support.', 'pwh-dcfh')
            ];
            if (!empty($_POST['_wpnonce'])) {
                $_wpnonce = sanitize_text_field($_POST['_wpnonce']);
                if (wp_verify_nonce($_wpnonce, 'pwh-dcfh-nonce') && !empty($_FILES)) {
                    $error_message = '';
                    $json_response = [
                        'success' => [],
                        'errors' => [],
                    ];
                    $upload_temp_dir = $this->upload_tmp_dir;
                    $upload_temp_url = $this->upload_tmp_url;
                    $token = isset($_POST['token']) ? sanitize_text_field($_POST['token']) : null;
                    if (empty($token)) {
                        wp_send_json_error(__('The file token is missing. Please contact the system administrator.', 'pwh-dcfh'));
                        wp_die();
                    }
                    $wp_allowed_mime_types = pwh_dcfh_wp_helpers()::get_wp_allowed_mime_types();
                    $token = json_decode(pwh_dcfh_helpers()::encrypt_decrypt($token, 'd'), ARRAY_A);
                    $allowd_filesize = $token['size'] ?? '';
                    $allowd_mimes = isset($token['mimetypes']) ? explode(',', $token['mimetypes']) : [];
                    $frontend_i10 = PWH_DCFH_Strings::instance()->strings('frontend_i10');
                    $security_reason_text = $frontend_i10['security_reason_text'];
                    $allow_filesize_text = $frontend_i10['allow_filesize_text'];
                    // Check for errors in uploaded files
                    foreach ($_FILES as $file) {
                        $filename = sanitize_file_name($file['name']);
                        if (isset($file['error']) && UPLOAD_ERR_OK !== $file['error']) {
                            $error_message = $file_error_types[$file['error']] ?? __('Something went wrong.', 'pwh-dcfh');
                        } else {
                            $file_tmpname = $file['tmp_name'];
                            $file_type = $file['type'];
                            $file_size = $file['size'];
                            // Ensure the correct MIME type is detected
                            if (extension_loaded('fileinfo')) {
                                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                                $file_real_mime = finfo_file($finfo, $file_tmpname);
                                finfo_close($finfo);
                            } else {
                                $file_real_mime = $file_type;
                            }
                            $wp_filetype = wp_check_filetype_and_ext($file_tmpname, $filename, $wp_allowed_mime_types);
                            // Validate file type and size
                            if (empty($wp_filetype['type']) || empty($wp_filetype['ext'])) {
                                $error_message = str_replace('{filename}', $filename, $security_reason_text);
                            } elseif (!in_array($file_real_mime, $allowd_mimes, true) || !in_array($file_real_mime, $wp_allowed_mime_types, true)) {
                                $error_message = str_replace('{filename}', $filename, $security_reason_text);
                            } elseif (($file_size > wp_max_upload_size()) || ($file_size > $allowd_filesize)) {
                                $error_message = str_replace('{filename}', $filename, $allow_filesize_text);
                            }
                        }
                        if (!empty($error_message)) {
                            $json_response['errors'][] = [
                                'name' => $filename,
                                'message' => $error_message,
                            ];
                        }
                    }
                    // If errors are present, return the error response
                    if (!empty(array_filter($json_response['errors']))) {
                        wp_send_json_success($json_response);
                        wp_die();
                    }
                    // Upload valid files
                    foreach ($_FILES as $file) {
                        $filename = sanitize_file_name($file['name']);
                        $file_tmpname = $file['tmp_name'];
                        $wp_filetype = wp_check_filetype_and_ext($file_tmpname, $filename, $wp_allowed_mime_types);
                        $filename_renamed = strtolower(pathinfo($filename, PATHINFO_FILENAME));
                        $filename_renamed = preg_replace('/[^A-Za-z\d\-]/', ' ', $filename_renamed);
                        $file_extension = pathinfo($filename, PATHINFO_EXTENSION);
                        $filename_unique = wp_unique_filename(
                            $upload_temp_dir,
                            sprintf('%1$s-%2$s-%3$s.%4$s', mb_substr($filename_renamed, 0, 30, 'utf-8'), str_pad(wp_rand(999, time()), 5, 0, STR_PAD_BOTH), time(), $file_extension)
                        );
                        $file_dir = path_join($upload_temp_dir, $filename_unique);
                        if (move_uploaded_file($file_tmpname, $file_dir)) {
                            $file_url = path_join($upload_temp_url, $filename_unique);
                            $json_response['success'][] = [
                                'tmp_name' => $filename,
                                'name' => $filename_unique,
                                'size' => size_format(filesize($file_dir)),
                                'mime' => $wp_filetype['type'],
                                'url' => $file_url,
                            ];
                        } else {
                            $json_response['errors'][] = [
                                'name' => $filename,
                                'message' => __('Failed to move the uploaded file.', 'pwh-dcfh'),
                            ];
                        }
                    }
                    // Return response for successful uploads
                    if (!empty(array_filter($json_response['success']))) {
                        wp_send_json_success($json_response);
                        wp_die();
                    }
                }
            }
            // Fallback error if nonce or files are missing
            wp_send_json_error(__('Unable to upload this file. Please contact the system administrator.', 'pwh-dcfh'));
            wp_die();
        }

        /**
         * Process File Remove
         * @since 1.0.0
         */
        public function maybe_remove_file()
        {
            if (!empty($_POST['_wpnonce'])) {
                $_wpnonce = sanitize_text_field($_POST['_wpnonce']);
                if (wp_verify_nonce($_wpnonce, 'pwh-dcfh-nonce')) {
                    $filename = isset($_POST['file_name']) ? sanitize_text_field($_POST['file_name']) : null;
                    if (!empty($filename)) {
                        $tmp_path = path_join($this->upload_tmp_dir, $filename);
                        if (et_()->WPFS()->is_file($tmp_path) && et_()->WPFS()->exists($tmp_path)) {
                            wp_delete_file($tmp_path);
                            wp_send_json_success(__('The file has been deleted successfully!', 'pwh-dcfh'));
                        } else {
                            wp_send_json_error(__('Something went wrong. Please upload file again.', 'pwh-dcfh'));
                        }
                        wp_die();
                    }
                }
            }
        }

        /**
         * Save Digital Signature
         * @return void
         */
        public function maybe_save_digital_signature()
        {
            if (!empty($_POST['_wpnonce'])) {
                $_wpnonce = sanitize_text_field($_POST['_wpnonce']);
                if (wp_verify_nonce($_wpnonce, 'pwh-dcfh-nonce')) {
                    $data_url = isset($_POST['data_url']) ? sanitize_text_field($_POST['data_url']) : null;
                    if (!empty($data_url)) {
                        $upload_temp_dir = $this->upload_tmp_dir;
                        $upload_temp_url = $this->upload_tmp_url;
                        $filename = wp_unique_filename($upload_temp_dir, sprintf('digital-signature-%1$s-%2$s.png', str_pad(wp_rand(999, time()), 5, 0, STR_PAD_BOTH), time()));
                        $file_path = path_join($upload_temp_dir, $filename);
                        $data_url = str_replace('data:image/png;base64,', '', $data_url);
                        if (et_()->WPFS()->put_contents($file_path, base64_decode($data_url))) {  // phpcs:ignore
                            $frontend_i10 = PWH_DCFH_Strings::instance()->strings('frontend_i10');
                            wp_send_json_success([
                                'message' => $frontend_i10['siganture_save_text'],
                                'filename' => $filename,
                                'file_url' => path_join($upload_temp_url, $filename),
                            ]);
                        } else {
                            wp_send_json_error();
                        }
                        wp_die();
                    }
                }
            }
        }

        /**
         * Delete Signature
         */
        public function maybe_delete_digital_signature()
        {
            if (!empty($_POST['_wpnonce'])) {
                $_wpnonce = sanitize_text_field($_POST['_wpnonce']);
                if (wp_verify_nonce($_wpnonce, 'pwh-dcfh-nonce')) {
                    $filename = isset($_POST['filename']) ? sanitize_text_field($_POST['filename']) : null;
                    if (!empty($filename)) {
                        $tmp_path = path_join($this->upload_tmp_dir, $filename);
                        if (et_()->WPFS()->is_file($tmp_path) && et_()->WPFS()->exists($tmp_path)) {
                            wp_delete_file($tmp_path);
                            $frontend_i10 = PWH_DCFH_Strings::instance()->strings('frontend_i10');
                            wp_send_json_success($frontend_i10['siganture_delete_text']);
                        } else {
                            wp_send_json_error(__('Something went wrong. Please upload file again.', 'pwh-dcfh'));
                        }
                        wp_die();
                    }
                }
            }
        }

        /**
         * Log Raw Entries
         *
         * @param $processed_fields_values
         * @param $et_contact_error
         * @param $contact_form_info
         *
         * @return void
         */
        public function maybe_log_entries($processed_fields_values, $et_contact_error, $contact_form_info)
        {
            if (pwh_dcfh_db()::is_setting_enabled('pwh_dcfh_logs_enabled')) {
                pwh_dcfh_log($_POST, false, 'divi_contact_form'); // phpcs:ignore WordPress.Security.NonceVerification
            }
        }

        /**
         * Sync ICS URL
         * @throws Exception
         * @return void
         */
        /**
         * Disabled Emailing
         * @return void
         */
        private function maybe_disabled_emailing()
        {
            if (!$this->process_request) {
                add_filter('is_email', '__return_false');
            }
        }
    }
}