<?php

/**
 * Get set Form,fields
 */

namespace BitCode\BitForm\Frontend\Form;

/**
 * FrontendFormManager class
 */

use BitCode\BitForm\Admin\Form\AdminFormHandler;
use BitCode\BitForm\Admin\Form\Helpers;
use BitCode\BitForm\Core\Database\FormEntryModel;
use BitCode\BitForm\Core\Form\FormManager;
use BitCode\BitForm\Core\Form\Validator\FormFieldValidator;
use BitCode\BitForm\Core\Integration\IntegrationHandler;
use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
use BitCode\BitForm\Core\Util\ApiResponse as UtilApiResponse;
use BitCode\BitForm\Core\Util\HttpHelper;
use BitCode\BitForm\Core\Util\IpTool;
use BitCode\BitForm\Core\WorkFlow\WorkFlow;
use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
use BitCode\BitForm\Frontend\Form\View\FormViewer;
use BitCode\BitForm\GlobalHelper;
use WP_Error;

final class FrontendFormManager extends FormManager
{
  private $_form_identifier;
  private $_form_token;
  private $_form_id;
  private $_work_flows;
  private $_conf_messages;
  private static $_instance = [];

  // private $_has_upload = false;
  public function __construct($form_id, $shortCodeCounter = null)
  {
    parent::__construct($form_id);
    $this->_form_identifier = 'bitforms_' . $form_id;
    $this->_form_identifier .= !empty(get_post()->ID) ? '_' . get_post()->ID : '';
    $this->_form_identifier .= !empty($shortCodeCounter) ? "_$shortCodeCounter" : '';
    $this->_form_token = wp_create_nonce('bitforms_' . $form_id);
    $this->_form_id = $form_id;
  }

  public static function getInstance($form_id, $shortCodeCounter = null)
  {
    $key = $form_id . ':' . ($shortCodeCounter ?? 'default');

    if (!isset(self::$_instance[$key])) {
      self::$_instance[$key] = new self($form_id, $shortCodeCounter);
    }

    return self::$_instance[$key];
  }

  public function getFormIdentifier()
  {
    return $this->_form_identifier;
  }

  public function getFormID()
  {
    return $this->_form_id;
  }

  public function getFormToken()
  {
    return $this->_form_token;
  }

  public function getSubmittedFields($submitted_data)
  {
    unset($submitted_data[$this->_form_identifier]);
    // unset($submitted_data['bit-form-submit-btn']);
    return array_keys($submitted_data);
  }

  public function formView($fields = null, $hasFile = false, $errorMessages = null, $previousValue = null)
  {
    $formContents = $this->getFormContent();
    $formAtomicClsMap = $this->getAtomicClsMap();
    if (!empty($fields)) {
      $formContents->fields = is_string($fields) ? json_decode($fields) : $fields;
    } else {
      $workFlowRunHelper = new WorkFlow($this->form_id);
      $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
        'create',
        $formContents->fields
      );
      $formContents->fields = empty($workFlowreturnedOnLoad['fields']) ? $formContents->fields : $workFlowreturnedOnLoad['fields'];
    }
    $formViewer = new FormViewer($this, $formContents, $formAtomicClsMap, $errorMessages, $previousValue);
    $isRestricted = $this->checkSubmissionRestriction(false);
    $msg = !empty($isRestricted) ? $isRestricted[0] : '';
    return $formViewer->getView($hasFile, $msg);
  }

  public function conversationalFormView($fields = null, $hasFile = false, $errorMessages = null, $previousValue = null)
  {
    $formContents = $this->getFormContent();
    $formAtomicClsMap = $this->getAtomicClsMap();
    if (!empty($fields)) {
      $formContents->fields = is_string($fields) ? json_decode($fields) : $fields;
    } else {
      $workFlowRunHelper = new WorkFlow($this->form_id);
      $workFlowreturnedOnLoad = $workFlowRunHelper->executeOnLoad(
        'create',
        $formContents->fields
      );
      $formContents->fields = empty($workFlowreturnedOnLoad['fields']) ? $formContents->fields : $workFlowreturnedOnLoad['fields'];
    }
    $formViewer = new FormViewer($this, $formContents, $formAtomicClsMap, $errorMessages, $previousValue);
    $isRestricted = $this->checkSubmissionRestriction(false);
    $msg = !empty($isRestricted) ? $isRestricted[0] : '';
    return $formViewer->getConversationalView($hasFile, $msg);
  }

  public function checkEmptySubmission($data, $file)
  {
    $formFields = $this->getFields();
    foreach ($formFields as $key => $field) {
      $fieldType = $field['type'];
      if ('button' === $fieldType) {
        continue;
      }
      $fileUploadFieldTypes = ['file-up', 'advanced-file-up'];
      if ('decision-box' === $fieldType || 'gdpr' === $fieldType) {
        continue;
      }
      $isFileType = in_array($fieldType, $fileUploadFieldTypes);
      if ($this->isRepeatedField($key)) {
        $fileData = !empty($file[$key]) ? $file[$key] : [];
        $dataVal = !empty($data[$key]) ? $data[$key] : [];
        if (!$this->checkRepeatedFieldEmptySubmission($isFileType, $dataVal, $fileData)) {
          return false;
        }
        continue;
      }
      if (!$isFileType && (!empty($data[$key]) || (isset($data[$key]) && is_numeric($data[$key])))) {
        return false;
      }
      if ($isFileType && !empty($file[$key]['name']) && is_string($file[$key]['name'])) {
        return false;
      }
      if ($isFileType && !empty($file[$key]['name'][0])) {
        return false;
      }
    }
    return true;
  }

  private function checkRepeatedFieldEmptySubmission($isFileType, $data, $file = [])
  {
    if (!$isFileType) {
      foreach ($data as $value) {
        if (!empty($value)) {
          return false;
        }
      }
    }
    if ($isFileType) {
      foreach ($file['name'] as $value) {
        if (!empty($value) && is_string($value)) {
          return false;
        }
        if (is_array($value) && !empty($value[0])) {
          return false;
        }
      }
    }
    return true;
  }

  private function getParams()
  {
    $url = wp_parse_url(wp_get_referer());
    $parameter = [];
    if (isset($url['query'])) {
      $queries = explode('&', $url['query']);
      foreach ($queries as $query) {
        list($field, $value) = explode('=', $query);
        $parameter[$field] = $value;
      }
    }
    return $parameter;
  }

  private function getFormFields($formID)
  {
    $adminFormHandler = new AdminFormHandler();
    $post = new \stdClass();
    $post = (object) [
      'id' => $formID
    ];
    $getForm = $adminFormHandler->getAForm('', $post);
    $formContainer = $getForm['form_content'];

    return $formContainer['fields'];
  }

  private function transformDrpdwnValue($post)
  {
    $formFields = $this->getFormFields($this->_form_id);

    foreach ($post as $key => $value) {
      if (!str_starts_with($key, 'repeater') && isset($formFields->{$key}) && 'select' === $formFields->{$key}->typ) {
        if (is_array($value)) {
          foreach ($value as $k => $v) {
            $post[$key][$k] = !is_array($v) && is_string($v) ? explode(BITFORMS_BF_SEPARATOR, $v) : $v;
          }
        } else {
          $post[$key] = explode(BITFORMS_BF_SEPARATOR, $value);
        }
      };
    }

    return $post;
  }

  public function handleSubmission()
  {
    // CSRF verified via verifySubmissionNonce() before this method is called. All $_POST reads below occur after that verification.
    $this->fieldNameReplaceOfPost();

    $validated = $this->beforeSubmittedValidate();

    $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);

    if (true === $validated) {
      do_action('bitform_validation_success', $this->_form_id);
      unset($_POST['hidden_fields']);

      $redirectPage = '';
      $regSuccMsg = '';

      $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
      $unslashed_post = wp_unslash($_POST);
      if (!is_wp_error($existAuth) && count($existAuth) > 0) {
        $parameter = $this->getParams();
        $existAuthFilter = has_filter('bitform_wp_user_auth');

        if (true === $existAuthFilter) {
          $result = apply_filters('bitform_wp_user_auth', $existAuth[0], $unslashed_post, $parameter);

          $result = apply_filters('bitform_filter_wp_user_auth_response', $result, $this->_form_id, $unslashed_post, $parameter);

          do_action('bitform_wp_user_auth_response', $result, $this->_form_id, $unslashed_post, $parameter);

          if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
            if (!$result['success']) {
              return new WP_Error('errors', esc_html($result['message']));
            } elseif (isset($result['success'])) {
              $redirectPage = $result['redirectPage'];
              $regSuccMsg = $result['message'];
            }
          } else {
            if (!$result['success']) {
              return new WP_Error('errors', esc_html($result['message']));
            } else {
              return $result;
            }
          }
        }
      }

      $saveResponse = $this->saveFormEntry($unslashed_post);
      if (is_wp_error($saveResponse)) {
        return $saveResponse;
      }

      $entryID = $saveResponse['entry_id'];

      // transformed dropdown value from string to array
      $newPost = $this->transformDrpdwnValue($unslashed_post);
      $filesData = GlobalHelper::sanitize_files_input($_FILES);
      do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $filesData);

      $captchaV3Settings = $this->getCaptchaV3Settings();
      if ($captchaV3Settings) {
        $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
        $integrationHandler = new IntegrationHandler(0);
        $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
        if (!is_wp_error($allFormIntegrations)) {
          foreach ($allFormIntegrations as $integration) {
            if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
              $integrationDetails = json_decode($integration->integration_details);
              $integrationDetails->id = $integration->id;
              $reCAPTCHA = $integrationDetails;
            }
          }
        }
        if (!empty($reCAPTCHA->secretKey)) {
          $gRecaptchaResponse = HttpHelper::post(
            'https://www.google.com/recaptcha/api/siteverify',
            ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
          );
          if ($captchaV3Settings && !empty($saveResponse['triggerData'])) {
            $logID = $saveResponse['triggerData']['logID'];
            $integId = $reCAPTCHA->id;
            $saveApiResponse = new UtilApiResponse();
            $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
          }
        }
        unset($_POST['g-recaptcha-response']);
      }
      if (!empty($redirectPage) && empty($saveResponse['redirectPage']) || null === $saveResponse['redirectPage']) {
        $saveResponse['redirectPage'] = $redirectPage;
      }
      if (!empty($regSuccMsg) && isset($saveResponse['dflt_message'])) {
        $saveResponse['message'] = $regSuccMsg;
      }
      $saveResponse['new_nonce'] = wp_create_nonce('bitforms_' . $this->_form_id);

      $saveResponse = IntegrationHandler::maybeSetCronForIntegration($saveResponse, 'create');
      $entryId = $saveResponse['entry_id'];

      $responseMsg = is_array($saveResponse) && !empty($saveResponse) ? $saveResponse : __('Form Submitted Successfully', 'bit-form');
      $_POST = [];
      $responseMsg['entry_id'] = $entryId;
      return $responseMsg;
    }
    do_action('bitform_validation_error', $this->_form_id, $validated);
    return $validated;
  }

  public function handleUpdateEntry()
  {
    // Entry token or capability verified by caller (FrontendAjax::update_entry). All $_POST reads occur after that check.
    $this->fieldNameReplaceOfPost();
    $validated = $this->beforeSubmittedValidate();
    $validated = apply_filters('bitform_filter_form_validation', $validated, $this->_form_id);

    $entryID = isset($_REQUEST['entryID']) ? sanitize_text_field(wp_unslash($_REQUEST['entryID'])) : null;
    $GLOBALS['bitform_entry_id'] = $entryID;
    if (is_null($entryID)) {
      return new WP_Error('empty_form', __('Entries id is invalid', 'bit-form'));
    }
    if (true === $validated) {
      do_action('bitform_validation_success', $this->_form_id);
      unset($_POST['hidden_fields'], $_POST['entryID']);

      $redirectPage = '';
      $regSuccMsg = '';
      $postData = wp_unslash($_POST);

      $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);
      if (!is_wp_error($existAuth) && count($existAuth) > 0) {
        $parameter = $this->getParams();
        $existAuthFilter = has_filter('bitform_wp_user_auth');

        if (true === $existAuthFilter) {
          $result = apply_filters('bitform_wp_user_auth', $existAuth[0], $postData, $parameter);

          if (isset($result['auth_type']) && 'register' === $result['auth_type']) {
            if (!$result['success']) {
              return new WP_Error('errors', esc_html($result['message']));
            } elseif (isset($result['success'])) {
              $redirectPage = $result['redirectPage'];
              $regSuccMsg = $result['message'];
            }
          } else {
            if (!$result['success']) {
              return new WP_Error('errors', esc_html($result['message']));
            } else {
              return $result;
            }
          }
        }
      }

      $updateResponse = $this->updateFormEntry(wp_unslash($_POST), $this->getFormID(), $entryID);
      if (is_wp_error($updateResponse)) {
        return $updateResponse;
      }

      // transformed dropdown value from string to array
      $newPost = $this->transformDrpdwnValue($postData);
      $filesData = GlobalHelper::sanitize_files_input($_FILES);

      //TO DO:: submit success action temporarily added for solution of a issue
      do_action('bitform_submit_success', $this->_form_id, $entryID, $newPost, $filesData);
      do_action('bitform_update_success', $this->_form_id, $entryID, $newPost, $filesData);

      $captchaV3Settings = $this->getCaptchaV3Settings();
      if ($captchaV3Settings) {
        $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
        $integrationHandler = new IntegrationHandler(0);
        $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
        if (!is_wp_error($allFormIntegrations)) {
          foreach ($allFormIntegrations as $integration) {
            if (!is_null($integration->integration_type) && 'gReCaptchaV3' === $integration->integration_type) {
              $integrationDetails = json_decode($integration->integration_details);
              $integrationDetails->id = $integration->id;
              $reCAPTCHA = $integrationDetails;
            }
          }
        }
        if (!empty($reCAPTCHA->secretKey)) {
          $gRecaptchaResponse = HttpHelper::post(
            'https://www.google.com/recaptcha/api/siteverify',
            ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
          );
          if ($captchaV3Settings && !empty($updateResponse['triggerData'])) {
            $logID = $updateResponse['triggerData']['logID'];
            $integId = $reCAPTCHA->id;
            $saveApiResponse = new UtilApiResponse();
            $saveApiResponse->apiResponse($logID, $integId, ['type_name' => 'ReCaptcha', 'type' => 'v3'], 'success', $gRecaptchaResponse);
          }
        }
        unset($_POST['g-recaptcha-response']);
      }
      if (!empty($redirectPage) && empty($updateResponse['redirectPage']) || null === $updateResponse['redirectPage']) {
        $updateResponse['redirectPage'] = $redirectPage;
      }
      if (!empty($regSuccMsg) && isset($updateResponse['dflt_message'])) {
        $updateResponse['message'] = $regSuccMsg;
      }
      $updateResponse['new_nonce'] = wp_create_nonce('bitforms_' . $this->_form_id);
      $updateResponse = IntegrationHandler::maybeSetCronForIntegration($updateResponse, 'update');
      $entryId = $updateResponse['entry_id'];

      $responseMsg = is_array($updateResponse) && !empty($updateResponse) ? $updateResponse : __('Entry Update Successfully', 'bit-form');

      $_POST = [];
      $responseMsg['entry_id'] = $entryId;
      return $responseMsg;
    }
    do_action('bitform_validation_error', $this->_form_id, $validated);
    return $validated;
  }

  public function validateFormSubmission($submitted_data)
  {
    $hidden_fields = isset($submitted_data['hidden_fields']) ? $submitted_data['hidden_fields'] : '';
    $submitted_fields = $this->getSubmittedFields($submitted_data);
    $form_fields = $this->getFields();
    $form_fields_names = array_keys($form_fields);
    if ($this->isGCLIDEnabled()) {
      array_push($form_fields_names, 'GCLID');
    }
    foreach ($submitted_fields as $field) {
      if ('hidden_fields' !== $field && !in_array($field, $form_fields_names) || false !== strpos($hidden_fields, $field)) {
        unset($submitted_data[$field]);
      }
    }
    return $submitted_data;
  }

  public function beforeSubmittedValidate($verifyCaptcha = true)
  {
    if ($this->verifySubmissionNonce()) {
      if ($this->isExist()) {
        $isRestricted = $this->checkSubmissionRestriction();
        if ($isRestricted && !empty($isRestricted)) {
          return new WP_Error('spam_detection', $isRestricted[0]);
        }
        $postData = wp_unslash($_POST);
        $filesData = GlobalHelper::sanitize_files_input($_FILES);
        $isHoneypot = apply_filters('bitform_check_honeypot', false, $this->_form_id, $postData);
        if ($isHoneypot) {
          return new WP_Error('spam_detection', __('Token verification failed', 'bit-form'));
        }
        $formCurrentStep = isset($_POST['form-current-step']) ? sanitize_text_field(wp_unslash($_POST['form-current-step'])) : null;
        // TODO: Temporary parameter to skip captcha verification in step change of multi step form
        if ($verifyCaptcha) {
          $verifyGRecaptchaResult = $this->verifyGRecaptcha();
          if (is_wp_error($verifyGRecaptchaResult)) {
            return $verifyGRecaptchaResult;
          }
          $verifyHCaptchaResult = $this->verifyHCaptcha();
          if (is_wp_error($verifyHCaptchaResult)) {
            return $verifyHCaptchaResult;
          }
          /* Implement Turnstile Captcha start */
          $verifyTurnstileCaptchaResult = $this->verifyTurnstileCaptcha();
          if (is_wp_error($verifyTurnstileCaptchaResult)) {
            return $verifyTurnstileCaptchaResult;
          }
        }
        /* Implement Turnstile Captcha end */

        $existAuth = (new IntegrationHandler($this->_form_id))->getAllIntegration('wp_user_auth', 'wp_auth', 1);

        // check if user is already logged in and form has auth integration
        do_action('bitform_checked_exist_auth', $this->_form_id, $existAuth);
        if (!is_wp_error($existAuth) && count($existAuth) > 0 && is_user_logged_in()) {
          return new WP_Error('auth_error', __('You are already logged in', 'bit-form'));
        }
        $validateForm = $this->validateFormSubmission($postData);
        $validateFormFiles = $this->validateFormSubmission($filesData);
        $validateForm = array_merge($validateForm, $validateFormFiles);
        $form_fields = $this->getFields();
        // check if form-current-step is set and form is multi-step
        $formCurrentStep = isset($_POST['form-current-step']) ? sanitize_text_field(wp_unslash($_POST['form-current-step'])) : null;
        if (!is_null($formCurrentStep)) {
          $formContents = $this->getFormContent();
          $layout = $formContents->layout;
          $stepIndex = (int) $formCurrentStep - 1;
          $stepLayout = $layout[$stepIndex]->layout->lg;
          $nestedLayout = $formContents->nestedLayout;
          $step_fields = [];
          foreach ($stepLayout as $lay) {
            $fk = $lay->i;
            if (isset($nestedLayout->{$fk})) {
              $nestedLg = $nestedLayout->{$fk}->lg;
              foreach ($nestedLg as $nestedLay) {
                $nestedFk = $nestedLay->i;
                $step_fields[$nestedFk] = $form_fields[$nestedFk];
              }
            }
            $step_fields[$fk] = $form_fields[$fk];
          }
          $form_fields = $step_fields;
        }
        $formFieldValidator = new FormFieldValidator($form_fields, $postData, $filesData);
        $validUniuqFields = [];
        $existFilter = has_filter('bitform_check_duplicate_entry');
        if (true === $existFilter) {
          $validUniuqFields = apply_filters('bitform_check_duplicate_entry', $form_fields, $postData);

          $fieldKeys = array_keys($validUniuqFields);
          $form_fields_keys = array_keys($form_fields);
          $uniqueFields = [];
          foreach ($fieldKeys as $key) {
            if (in_array($key, $form_fields_keys)) {
              $uniqueFields[] = $form_fields[$key];
            }
          }
          do_action('bitform_Unique_entry', $uniqueFields, $validUniuqFields, $this->_form_id, $postData);
        }
        $validateField = $formFieldValidator->validate('create', $this->_form_id);

        if ($validateForm && $validateField && 0 === count($validUniuqFields)) {
          return true;
        } else {
          $error = __('Please submit form with valid fields', 'bit-form');
          if (!$validateForm) {
            $errorMessages = $error;
          } elseif (count($formFieldValidator->getMessage()) > 0) {
            $errorMessages = $formFieldValidator->getMessage();
          } else {
            $errorMessages = 0 === count($validUniuqFields) ? $error : $validUniuqFields;
          }
          return new WP_Error('validation_error', $errorMessages);
        }
      }
      return new WP_Error('unknown_form', __('Form does not exist', 'bit-form'));
    } else {
      return new WP_Error('token_expired', __('Token expired', 'bit-form'));
    }
  }

  private function verifyGRecaptcha()
  {
    $captchaSettings = $this->getCaptchaSettings();
    $captchaV3Settings = $this->getCaptchaV3Settings();
    if ($captchaSettings || $captchaV3Settings) {
      $token = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
      if (!isset($_POST['g-recaptcha-response'])) {
        return new WP_Error('spam_detection', __('Please recheck your reCaptcha Configuration', 'bit-form'));
      }
      $integrationHandler = new IntegrationHandler(0);
      $allFormIntegrations = $integrationHandler->getAllIntegration('app', $captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3');
      if (!is_wp_error($allFormIntegrations)) {
        foreach ($allFormIntegrations as $integration) {
          if (!is_null($integration->integration_type) && $integration->integration_type === ($captchaSettings ? 'gReCaptcha' : 'gReCaptchaV3')) {
            $integrationDetails = json_decode($integration->integration_details);
            $integrationDetails->id = $integration->id;
            $reCAPTCHA = $integrationDetails;
          }
        }
      }
      if (!empty($reCAPTCHA->secretKey)) {
        $gRecaptchaResponse = HttpHelper::post(
          'https://www.google.com/recaptcha/api/siteverify',
          ['secret' => $reCAPTCHA->secretKey, 'response' => $token]
        );
        $isgReCaptchaVerified = false;
        if (!is_wp_error($gRecaptchaResponse)) {
          if (
            $captchaV3Settings
            && !empty($gRecaptchaResponse->score)
            && ((float) $gRecaptchaResponse->score < (float) $captchaV3Settings->score)
          ) {
            wp_send_json_error(
              sanitize_text_field((string) $captchaV3Settings->message)
            );
          }

          $isgReCaptchaVerified = $gRecaptchaResponse->success;
        }
        if (!$isgReCaptchaVerified) {
          return new WP_Error('spam_detection', __('Please verify reCAPTCHA', 'bit-form'));
        }
      }
    }
  }

  private function verifyHCaptcha()
  {
    $hCaptchaExist = $this->isFieldTypeExist('hcaptcha'); // You can rename this to getHCaptchaSettings() if needed
    if ($hCaptchaExist) {
      if (!isset($_POST['h-captcha-response'])) {
        return new WP_Error('spam_detection', __('Please verify hCaptcha', 'bit-form'));
      }

      $token = sanitize_text_field(wp_unslash($_POST['h-captcha-response']));

      $integrationHandler = new IntegrationHandler(0);
      $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'hcaptcha');

      if (!is_wp_error($allFormIntegrations)) {
        foreach ($allFormIntegrations as $integration) {
          if (!is_null($integration->integration_type) && 'hcaptcha' === $integration->integration_type) {
            $integrationDetails = json_decode($integration->integration_details);
            $integrationDetails->id = $integration->id;
            $hCaptcha = $integrationDetails;
          }
        }
      }

      if (!empty($hCaptcha->secretKey)) {
        $hCaptchaResponse = HttpHelper::post(
          'https://api.hcaptcha.com/siteverify',
          [
            'secret'   => $hCaptcha->secretKey,
            'response' => $token,
            'remoteip' => (isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '')
          ]
        );

        $isVerified = false;
        if (!is_wp_error($hCaptchaResponse)) {
          $isVerified = $hCaptchaResponse->success;
        }

        if (!$isVerified) {
          return new WP_Error('spam_detection', __('hCaptcha verification failed', 'bit-form'));
        }
      }
    }
  }

  private function verifyTurnstileCaptcha()
  {
    $turnstileExist = $this->isFieldTypeExist('turnstile');
    if ($turnstileExist) {
      if (!isset($_POST['cf-turnstile-response'])) {
        return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
      }
      $token = sanitize_text_field(wp_unslash($_POST['cf-turnstile-response']));
      $turnstileCaptcha = null;
      $integrationHandler = new IntegrationHandler(0);
      $turnstileIntegration = $integrationHandler->getAllIntegration('app', 'turnstileCaptcha')[0];
      if (!is_wp_error($turnstileIntegration && !is_null($turnstileIntegration->integration_type))) {
        $turnstileCaptcha = json_decode($turnstileIntegration->integration_details);
        // $integrationDetails->id = $turnstileIntegration->id;
        // $turnstileCaptcha = $integrationDetails;
      }
      if (!is_null($turnstileCaptcha)) {
        $isTurnstileCaptchaVerified = false;
        $turnstileRecaptchaResponse = HttpHelper::post(
          'https://challenges.cloudflare.com/turnstile/v0/siteverify',
          ['secret' => $turnstileCaptcha->secretKey, 'response' => $token]
        );
        if (!is_wp_error($turnstileRecaptchaResponse)) {
          if (!$turnstileRecaptchaResponse->success) {
            $errorCodes = implode(', ', (array) ($turnstileRecaptchaResponse->{'error-codes'} ?? []));
            wp_send_json_error(
              sprintf(
                /* translators: %s: dynamic value. */
                __('Cloudflare Turnstile Validation Error: %s', 'bit-form'),
                $errorCodes
              )
            );
          }

          $isTurnstileCaptchaVerified = $turnstileRecaptchaResponse->success;
        }
        if (!$isTurnstileCaptchaVerified) {
          return new WP_Error('spam_detection', __('Please verify Cloudflare Turnstile Captcha', 'bit-form'));
        }
      }
    }
  }

  public function verifySubmissionNonce()
  {
    if (!isset($_POST['t_identity']) || !isset($_POST['csrf'])) {
      return false;
    }
    $tIdenty = sanitize_text_field(wp_unslash($_POST['t_identity']));
    $csrf = sanitize_text_field(wp_unslash($_POST['csrf']));
    unset($_POST['t_identity'], $_POST['action'], $_POST['bitforms_id'], $_POST['csrf']);
    return Helpers::csrfDecrypted($tIdenty, $csrf);
  }

  public function setViewCount()
  {
    if (!current_user_can('manage_options')) {
      $update_status = $this->formModel->update(
        [
          'views' => intval(static::$form[0]->views) + 1
        ],
        [
          'id' => $this->form_id
        ]
      );
    }
  }

  public function checkSubmissionRestriction($checkedEmptySubmitted = true)
  {
    $formContents = $this->getFormContent();
    $additionalSettings = isset($formContents->additional) ? $formContents->additional : null;
    $fromRestrictionSetitingsEnabled = empty($additionalSettings->enabled) ? [] : $additionalSettings->enabled;
    $fromRestrictionSetitings = empty($additionalSettings->settings) ? null : $additionalSettings->settings;

    if (is_null($additionalSettings) || is_null($fromRestrictionSetitings) || empty((array) $fromRestrictionSetitingsEnabled)) {
      return false;
    }

    $restrictionMessage = [];
    $ipTool = new IpTool();
    $ipAddress = $ipTool->getIP();
    $currentUserId = get_current_user_id();

    foreach ($fromRestrictionSetitingsEnabled as $restrictionKey => $isEnabled) {
      if ($isEnabled) {
        /**
         * Allow add-ons to handle any restriction key (Pro-only restrictions
         * should be implemented in the add-on, not shipped in the free plugin).
         *
         * Return a non-null string to block submission.
         */
        $addonMsg = apply_filters(
          'bitform_submission_restriction',
          null,
          $restrictionKey,
          $this->form_id,
          $fromRestrictionSetitingsEnabled,
          $fromRestrictionSetitings,
          $ipAddress,
          $currentUserId
        );

        if (!is_null($addonMsg) && '' !== $addonMsg) {
          $restrictionMessage[] = $addonMsg;
          continue;
        }

        if ('onePerIp' === $restrictionKey) {
          $formEntry = new FormEntryModel();

          $getResult = $formEntry->get(
            ['user_ip', 'status'],
            [
              'form_id' => $this->form_id,
              'user_ip' => (int) ip2long((string) $ipAddress)
            ],
          );

          $count = 0;
          $status = 0;

          if (!is_wp_error($getResult) && count($getResult) > 0) {
            $count = count($getResult);

            foreach ($getResult as $row) {
              if (9 === (int) $row->status) {
                $status = 9;
                break;
              }
            }
          }

          if ($count > 0 && 9 !== (int) $status) {
            $onePerIp = __('Sorry!! You have already submitted from this IP address', 'bit-form');

            $onePerIp = apply_filters(
              'bitform_filter_restriction_one_per_ip_message',
              $onePerIp,
              $this->form_id
            );

            $restrictionMessage[] = $onePerIp;
          }
        }
        if ('is_login' === $restrictionKey && 0 === get_current_user_id()) {
          $is_login_messages = $fromRestrictionSetitings->is_login->message;

          $is_login_messages = apply_filters(
            'bitform_filter_restriction_is_login_message',
            $is_login_messages,
            $this->form_id
          );

          $restrictionMessage[] = $is_login_messages;
        }
        if ($checkedEmptySubmitted && 'empty_submission' === $restrictionKey) {
          $isEmpty = $this->checkEmptySubmission(wp_unslash($_POST), GlobalHelper::sanitize_files_input($_FILES));
          if ($isEmpty) {
            $restriction = $fromRestrictionSetitings->empty_submission->message;

            $restriction = apply_filters(
              'bitform_filter_restriction_empty_submission_message',
              $restriction,
              $this->form_id
            );

            $restrictionMessage[] = $restriction;
          }
        }
      }
    }
    return $restrictionMessage;
  }

  /**
   * Will check if form is submitted by a bot
   *
   * @return Boolean true - if submitted by bot else false
   */
  public function isTrappedInHoneypot()
  {
    // Honeypot is implemented by add-ons (e.g. Pro) via filter.
    return (bool) apply_filters('bitform_check_honeypot', false, $this->_form_id, wp_unslash($_POST));
  }

  public function isHoneypotActive()
  {
    return (bool) apply_filters('bitform_is_honeypot_active', false, $this->_form_id, $this->getFormContent());
  }

  public function checkPaymentFields()
  {
    $formContents = $this->getFormContent();
    $fields = $formContents->fields;

    $payments = [];
    foreach ($fields as $fldData) {
      if ('paypal' === $fldData->typ && property_exists($fldData, 'payIntegID')) {
        $payments['paypalKey'] = $this->getClientKey($fldData->payIntegID, 'clientID');
      } elseif ('razorpay' === $fldData->typ && property_exists($fldData->options, 'payIntegID')) {
        $payments['razorpayKey'] = $this->getClientKey($fldData->options->payIntegID, 'apiKey');
      }
    }

    return $payments;
  }

  private function getClientKey($integID, $keyName)
  {
    $client = '';
    if (!empty($integID)) {
      $integrationHandler = new IntegrationHandler(0);
      $integration = $integrationHandler->getAIntegration($integID, 'app', 'payments');
      if (!is_wp_error($integration)) {
        $integration_details = json_decode($integration[0]->integration_details);
        $client = base64_encode($integration_details->{$keyName});
      }
    }
    return $client;
  }

  public function getSuccessMessageMarkups()
  {
    if (is_null($this->_work_flows)) {
      $workFlowManager = new WorkFlowHandler($this->form_id);
      $this->_work_flows = $workFlowManager->getAllworkFlow();
    }

    $ids = [];
    foreach ($this->_work_flows as $msgItem) {
      foreach ($msgItem['conditions'] as $condition) {
        if (isset($condition->actions->success)) {
          foreach ($condition->actions->success as $msg) {
            if ('successMsg' === $msg->type && isset($msg->details->id)) {
              $idObj = json_decode(stripslashes($msg->details->id));
              if (is_object($idObj) && !empty($idObj->id)) {
                array_push($ids, $idObj->id);
              }
            }
          }
        }
        if (isset($condition->actions->failure)) {
          $idObj = json_decode(stripslashes($condition->actions->failure));
          if (is_object($idObj) && !empty($idObj->id)) {
            array_push($ids, $idObj->id);
          }
        }
      }
    }
    $ids = array_unique($ids);
    if (is_null($this->_conf_messages)) {
      $successMsgHandler = new SuccessMessageHandler($this->form_id);
      $this->_conf_messages = $successMsgHandler->getMessages($ids);
    }

    $messageMarkups = '';
    if (is_wp_error($this->_conf_messages)) {
      return $messageMarkups;
    }

    foreach ($this->_conf_messages as $msgItem) {
      $messageMarkups .= $this->messageMarkup($msgItem);
    }

    return $messageMarkups;
  }

  public function getFormAbandonmentMessage()
  {
    $msg = apply_filters('bitform_form_abandonment_warning_markup', '', $this->form_id);
    return is_string($msg) ? $msg : '';
  }

  public function getFormAbandonmentSettings()
  {
    return apply_filters('bitform_form_abandonment_settings', null, $this->form_id);
  }

  private function messageMarkup($msg)
  {
    $msgId = $msg->id;
    $msgConfig = json_decode($msg->message_config);
    $scrollClass = 'below' === $msgConfig->msgType ? 'scroll' : '';

    return '<div 
              role="dialog"
              aria-hidden="true"
              data-modal-backdrop="true"
              class="' . $this->getAtomicCls("msg-container-{$msgId}") . ' deactive ' . $scrollClass . '">
	              <div 
                data-contentid="' . $this->getFormIdentifier() . '" 
                data-msgid="' . $msgId . '" 
                role="button" 
                class="' . $this->getAtomicCls("msg-background-{$msgId}") . ' msg-backdrop">
	                <div class="bf-msg-content ' . $this->getAtomicCls("msg-content-{$msgId}") . '">
	                  <button 
                       data-contentid="' . $this->getFormIdentifier() . '" 
                       data-msgid="' . $msgId . '" 
                       class="' . $this->getAtomicCls("close-{$msgId}") . ' bf-msg-close" 
                       type="button">
	                        <svg class="' . $this->getAtomicCls("close-icn-{$msgId}") . '" viewBox="0 0 30 30">
	                        <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="4" y1="3.88" x2="26" y2="26.12"></line>
	                        <line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" x1="26" y1="3.88" x2="4" y2="26.12"></line>
	                        </svg>
	                  </button>
	                  <div class="msg-content"></div>
	                </div>
	              </div>
	            </div>';
  }
}
