<?php

/**
 * Get set Form,fields
 */

namespace BitCode\BitForm\Core\Form;

/**
 * FrontendFormManager class
 */

use BitCode\BitForm\Admin\Form\CustomFieldHandler;
use BitCode\BitForm\Admin\Form\Helpers;
use BitCode\BitForm\Core\Database\FormEntryLogModel;
use BitCode\BitForm\Core\Database\FormEntryMetaModel;
use BitCode\BitForm\Core\Database\FormEntryModel;
use BitCode\BitForm\Core\Database\FormModel;
use BitCode\BitForm\Core\Integration\IntegrationHandler;
use BitCode\BitForm\Core\Messages\SuccessMessageHandler;
use BitCode\BitForm\Core\Util\FieldValueHandler;
use BitCode\BitForm\Core\Util\FileHandler;
use BitCode\BitForm\Core\Util\FrontendHelpers;
use BitCode\BitForm\Core\Util\IpTool;
use BitCode\BitForm\Core\Util\Log;
use BitCode\BitForm\Core\WorkFlow\WorkFlow;
use BitCode\BitForm\Core\WorkFlow\WorkFlowHandler;
use stdClass;
use WP_Error;

class FormManager
{
  // Cache for instances of FormManager by form_id
  private static $formManagerCache = [];
  protected static $form;
  protected $formModel;
  protected $form_id;
  private $_has_upload;
  private $_field_label;
  private $_fields;
  private $_repeaterFields;
  private $_work_flows;
  private $_conf_messages;
  private $_atomic_class_map;
  private $_saveFormAsDraft;

  public function __construct($form_id)
  {
    $this->form_id = $form_id;
    $this->formModel = new FormModel();

    static::$form = $this->formModel->get(
      [
        'id',
        'form_content',
        'form_name',
        'created_at',
        'views',
        'entries',
        'status',
        'builder_helper_state',
        'atomic_class_map',
        'generated_script_page_ids',
      ],
      [
        'id' => $form_id,
      ]
    );
    if (!is_wp_error(static::$form)) {
      $this->_atomic_class_map = json_decode(static::$form[0]->atomic_class_map);
      $bfMultipleFormsExists = FrontendHelpers::hasMultipleForms();
      if ($bfMultipleFormsExists && isset($this->_atomic_class_map->atomic_class_map_with_form_id)) {
        $this->_atomic_class_map = $this->_atomic_class_map->atomic_class_map_with_form_id;
      } elseif (isset($this->_atomic_class_map->atomic_class_map)) {
        $this->_atomic_class_map = $this->_atomic_class_map->atomic_class_map;
      }
    } else {
      // Log the error if needed
      Log::debug_log('Error fetching form: ' . "Form Id = ($form_id)" . static::$form->get_error_message());
    }
  }

  // Static method to get the instance of FormManager
  public static function getInstance($form_id)
  {
    // Check if an instance of FormManager is already cached
    if (!isset(self::$formManagerCache[$form_id])) {
      // Create and cache the FormManager instance if not found
      self::$formManagerCache[$form_id] = new self($form_id);
    }

    // Return the cached instance
    return self::$formManagerCache[$form_id];
  }

  public function isExist()
  {
    return (!static::$form || is_wp_error(static::$form)) ? false : true;
  }

  public function checkStatus()
  {
    return '1' === static::$form[0]->status ? true : false;
  }

  public function getFieldsContent()
  {
    return self::$form[0]->form_content;
  }

  public function getFont()
  {
    $atomicClassMap = $this->_atomic_class_map;
    $font = isset($atomicClassMap->font) ? $atomicClassMap->font : '';
    return $font;
  }

  public function getStyle()
  {
    $builerState = \json_decode(static::$form[0]->builder_helper_state);
    $style = '';
    $themeVars = $builerState->themeVars;
    $themeColors = $builerState->themeColors;

    if (!empty($themeVars)) {
      $style .= ':root {';
      foreach ($themeVars->lgLightThemeVars as $key => $value) {
        $style .= "$key: $value; ";
      }
      $style .= '} ';
    }
    if (!empty($themeColors)) {
      $style .= ' :root {';
      foreach ($themeColors->lightThemeColors as $k => $v) {
        $style .= "$k:$v; ";
      }
      $style .= '} ';
    }

    $field = $builerState->style->lgLightStyles->fields;
    foreach ($field as $value) {
      $classes = $value->classes;
      foreach ($classes as $key => $value) {
        $style .= "{$key} {";
        foreach ($value as $k => $v) {
          $style .= "$k:$v; ";
        }
        $style .= '} ';
      }
    }
    return $style;
  }

  public function getCustomStyle()
  {
    $customCSSPath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-styles' . DIRECTORY_SEPARATOR . "bitform-custom-{$this->form_id}.css";
    return FileHandler::readFile($customCSSPath);
  }

  public function getCustomJS()
  {
    $customJsPath = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'form-scripts' . DIRECTORY_SEPARATOR . "bitform-custom-{$this->form_id}.js";
    return FileHandler::readFile($customJsPath);
  }

  public function getFormContentWithValue($defaultValues = [])
  {
    $form_content = \json_decode(static::$form[0]->form_content);
    // this filter just use private purpose
    $form_content->fields = apply_filters('bitform_dynamic_field_filter', $form_content->fields);
    if (!is_array($defaultValues) || 0 === count($defaultValues)) {
      return $form_content;
    }
    foreach ($form_content->fields as $fieldKey => $fieldDetails) {
      // $field_name = empty($fieldDetails->lbl) ? null : \preg_replace('/[\`\~\!\@\#\$\'\.\s\?\+\-\*\&\|\/\\!]/', '_', $fieldDetails->lbl);
      $fieldName = $fieldDetails->fieldName;
      $defaultValue = isset($defaultValues[$fieldName]) ? $defaultValues[$fieldName] : null;
      $defaultValue = isset($defaultValues[$fieldKey]) ? $defaultValues[$fieldKey] : $defaultValue;
      if ((isset($fieldDetails->mul) || 'check' === $fieldDetails->typ) && isset($defaultValue)) {
        // if (is_array($defaultValue)) {
        //     $fieldDetails->val =
        //         wp_json_encode(
        //             array_map('sanitize_text_field', $defaultValue)
        //         );
        // } else {
        //     $fieldDetails->val = sanitize_text_field($defaultValue);
        // }
        if ((isset($fieldDetails->mul) && true === $fieldDetails->mul) || is_array($defaultValue)) {
          $fieldDetails->val = wp_json_encode(array_map('sanitize_text_field', $defaultValue));
        } elseif (!is_array($defaultValue)) {
          $fieldDetails->val = sanitize_text_field($defaultValue);
        }
      } elseif (!is_null($defaultValue)) {
        $fieldDetails->val = is_string($defaultValue) ?
            sanitize_text_field($defaultValue) :
            sanitize_text_field($defaultValue[count($defaultValue) - 1]);
      }
    }
    return $form_content;
  }

  public function getFormContent()
  {
    $formContent = json_decode(static::$form[0]->form_content);
    $types = ['check', 'radio', 'select'];
    $filter = false;
    foreach ($formContent->fields as $field) {
      if (in_array($field->typ, $types) && property_exists($field, 'customType')) {
        $filter = true;
        break; // reduce unnecessary loop
      }
    }
    if (true === $filter) {
      $updateFields = apply_filters('bitform_dynamic_field_filter', $formContent->fields);
      $formContent->fields = $updateFields;
    }
    return $formContent;
  }

  public function getFormInfo()
  {
    $formContent = json_decode(static::$form[0]->form_content);
    $formInfo = isset($formContent->formInfo) ? $formContent->formInfo : null;
    return $formInfo;
  }

  public function getFormPermission()
  {
    $formContent = json_decode(static::$form[0]->form_content);
    $formPermission = isset($formContent->formPermissions) ? $formContent->formPermissions : null;
    return $formPermission;
  }

  public function getFormHelperStates()
  {
    $formHelperStates = json_decode(static::$form[0]->builder_helper_state);
    return $formHelperStates;
  }

  public function getAtomicClsMap()
  {
    return $this->_atomic_class_map;
  }

  private function is_json($str)
  {
    $json = json_decode($str);
    return $json && $str !== $json;
  }

  public function getFormData($columnName = '')
  {
    if (empty($columnName)) {
      return null;
    }

    $form = static::$form[0];
    if (!isset($form->{$columnName})) {
      return null;
    }

    $data = $form->{$columnName};
    if ($this->is_json($data)) {
      return json_decode($data);
    }

    return $data;
  }

  public function getFormName()
  {
    return static::$form[0]->form_name;
  }

  public function getFormLayout()
  {
    $formContent = $this->getFormContent();
    return $formContent->layout;
  }

  public function getFormNestedLayout()
  {
    $formContent = $this->getFormContent();
    return $formContent->nestedLayout;
  }

  private function mergeNestedLayout(&$layout, $nestedLayout)
  {
    foreach ($nestedLayout as $key => $brkpnts) {
      foreach ($brkpnts as $brkpnt=>$nLayout) {
        $layout->{$brkpnt} = array_merge($layout->{$brkpnt}, $nLayout);
      }
    }
  }

  public function flatMultistepFormLayout()
  {
    $formLayout = $this->getFormLayout();
    $multistepLayout = new stdClass();
    foreach ($formLayout as $stpLayout) {
      $lyout = $stpLayout->layout;

      foreach ($lyout as $brkpnt=>$fields) {
        $multistepLayout->{$brkpnt} = array_merge($multistepLayout->{$brkpnt} ?? [], $fields);
      }
    }

    return $multistepLayout;
  }

  public function getFlatenFormLayout()
  {
    $layout = $this->getFormLayout();
    $nestedLayout = $this->getFormNestedLayout();
    if ('array' === gettype($layout)) {
      // multi step form layout
      $layout = $this->flatMultistepFormLayout();
    }
    if (!empty((array) $nestedLayout)) {
      $this->mergeNestedLayout($layout, $nestedLayout);
    }

    return $layout;
  }

  public function getFieldsBasedOnLayout()
  {
    $layout = $this->getFlatenFormLayout();

    $fieldKeyOrderbasedOnLayout = array_map(function ($fld) {
      return $fld->i;
    }, $layout->lg);
    $orderedFields = [];
    $fields = $this->getFields();

    foreach ($fieldKeyOrderbasedOnLayout as $key) {
      if (array_key_exists($key, $fields)) {
        $orderedFields[$key] = $fields[$key];
      }
    }

    foreach ($fields as $k=>$v) {
      if (!array_key_exists($k, $fieldKeyOrderbasedOnLayout)) {
        $orderedFields[$k] = $fields[$k];
      }
    }

    return $orderedFields;
  }

  public function getFields()
  {
    if (!is_null($this->_fields)) {
      return $this->_fields;
    }
    $form_content = \json_decode(static::$form[0]->form_content);
    $layout = $form_content->layout;
    $fields = $form_content->fields;
    $field_details = [];
    foreach ($fields as $key => $field) {
      if ('recaptcha' === $field->typ || 'hcaptcha' === $field->typ) {
        continue;
      }
      // $field_name = empty($field->lbl) ? null : \preg_replace('/[\`\~\!\@\#\$\'\.\s\?\+\-\*\&\|\/\\\!]/', '_', $field->lbl);
      $field_type = $field->typ;
      $field_details[$key]['label'] = !empty($field->lbl) ? $field->lbl : (!empty($field->adminLbl) ? $field->adminLbl : (!empty($field->fieldName) ? $field->fieldName : null));
      $field_details[$key]['type'] = $field_type;
      $field_details[$key]['key'] = $key;
      $field_details[$key]['name'] = isset($field->fieldName) ? $field->fieldName : '';
      if (isset($field->customType)) {
        $field_details[$key]['customType'] = $field->customType;
      }
      // fields with confirm field
      if (isset($field->childFields)) {
        $field_details[$key]['childFields'] = $field->childFields;
      }
      if (isset($field->parentFieldKey)) {
        $field_details[$key]['parentFieldKey'] = $field->parentFieldKey;
        if (isset($field->isDeactive)) {
          $field_details[$key]['isDeactive'] = $field->isDeactive;
        }
      }
      if (isset($field->err)) {
        if (isset($field->err->entryUnique)) {
          $field_details[$key]['entryUnique'] = $field->err->entryUnique;
        }
        if (isset($field->err->userUnique)) {
          $field_details[$key]['userUnique'] = $field->err->userUnique;
        }
      }

      if (isset($field->mul)) {
        $field_details[$key]['mul'] = $field->mul;
      }
      if (in_array($field_type, ['name'])) {
        $field_details[$key]['label'] = $field->adminLbl ?? $field->lbl;
      }
      if ('file-up' === $field_type && isset($field->exts)) {
        $field_details[$key]['valid']['type'] = $field->exts;
      }
      if ('file-up' === $field_type && isset($field->mxUp)) {
        $field_details[$key]['valid']['upload_size'] = (int) $field->mxUp;
      }
      if (isset($field->valid) && !is_null($field->valid)) {
        if (isset($field->valid->req)) {
          $field_details[$key]['valid']['req'] = $field->valid->req;
        }
        if (isset($field->valid->reqMsg)) {
          $field_details[$key]['valid']['reqMsg'] = $field->valid->reqMsg;
        }
        if (isset($field->valid->typMsg)) {
          $field_details[$key]['valid']['typMsg'] = $field->valid->typMsg;
        }
        if (isset($field->valid->hide)) {
          $field_details[$key]['valid']['hide'] = $field->valid->hide;
        }
      }
      if ($this->isRepeatedField($key)) {
        $field_details[$key]['repeated'] = true;
      }
    }
    if ($this->isGCLIDEnabled()) {
      $field_details['GCLID']['name'] = 'GCLID';
      $field_details['GCLID']['adminLbl'] = 'GCLID';
      $field_details['GCLID']['key'] = 'GCLID';
      $field_details['GCLID']['type'] = 'hidden';
    }
    $this->_fields = $field_details;
    return $field_details;
  }

  public function getFieldsKey()
  {
    $form_content = \json_decode(static::$form[0]->form_content);
    $fields = $form_content->fields;
    $field_details = [];
    foreach ($fields as $key => $field) {
      if ('recaptcha' === $field->typ || 'hcaptcha' === $field->typ) {
        continue;
      }
      // $field_name = empty($field->lbl) ? null : \preg_replace('/[\`\~\!\@\#\$\'\.\s\?\+\-\*\&\|\/\\\!]/', '_', $field->lbl);
      $field_details[$key] = $key;
    }
    if ($this->isGCLIDEnabled()) {
      $field_details['GCLID'] = 'GCLID';
    }
    return $field_details;
  }

  public function getFieldLabel($forQuery = false)
  {
    if (!is_null($this->_field_label)) {
      return $this->_field_label;
    }
    $form_content = \json_decode(static::$form[0]->form_content);
    $fields = $form_content->fields;
    $field_details = [];
    $fieldCounter = 0;
    foreach ($fields as $key => $field) {
      if ('recaptcha' === $field->typ || 'turnstile' === $field->typ || 'html' === $field->typ || 'button' === $field->typ) {
        continue;
      }
      $field_details[$fieldCounter]['name'] = empty($field->lbl) ? null : $field->lbl;
      $field_details[$fieldCounter]['adminLbl'] = empty($field->adminLbl) ? $field_details[$fieldCounter]['name'] : $field->adminLbl;
      $field_details[$fieldCounter]['key'] = $key;
      $field_details[$fieldCounter]['type'] = $field->typ;
      $fieldCounter += 1;
    }
    if ($this->isGCLIDEnabled()) {
      $field_details[$fieldCounter]['name'] = 'GCLID';
      $field_details[$fieldCounter]['adminLbl'] = 'GCLID';
      $field_details[$fieldCounter]['key'] = 'GCLID';
      $field_details[$fieldCounter]['type'] = 'hidden';
      $fieldCounter += 1;
    }
    if (!$forQuery) {
      $field_details = (array) $this->addEntryInfo($field_details, $fieldCounter);
    }
    $this->_field_label = $field_details;
    return $field_details;
  }

  public function getUploadFields()
  {
    if (!is_null($this->_has_upload)) {
      return $this->_has_upload;
    }
    $upload_fields = [];
    $form_field_details = $this->getFields();
    foreach ($form_field_details as $field_name => $__field_detail) {
      if (isset($__field_detail['type']) && ('file-up' === $__field_detail['type'] || 'advanced-file-up' === $__field_detail['type'])) {
        $upload_fields[] = $field_name;
      }
    }
    $this->_has_upload = $upload_fields;
    return $upload_fields;
  }

  public function getSignatureFilePath($blobLink, $form_id, $fieldKey, $entry_id, $imgType)
  {
    $imgTypes = [
      'image/png'     => 'png',
      'image/jpeg'    => 'jpg',
      'image/svg+xml' => 'svg',
    ];
    try {
      if (!isset($imgTypes[$imgType])) {
        throw new \InvalidArgumentException("Unsupported image type: $imgType");
      }
      $parts = explode(',', $blobLink, 2);
      if (2 !== count($parts) || false === ($decoded_image = base64_decode($parts[1]))) {
        throw new \RuntimeException('Invalid or corrupt signature data URI');
      }

      $_upload_dir = FileHandler::getEntriesFileUploadDir($form_id, $entry_id);
      FileHandler::createIndexFile($_upload_dir);
      $uniqueId = time() . '-' . bin2hex(\random_bytes(4));
      $filename = "{$entry_id}-{$fieldKey}-{$uniqueId}.{$imgTypes[$imgType]}";
      $fullPath = $_upload_dir . DIRECTORY_SEPARATOR . $filename;
      if (false === file_put_contents($fullPath, $decoded_image)) {
        throw new \RuntimeException("Failed to write image to $fullPath");
      }
      return $filename;
    } catch (\Throwable $e) {
      Log::debug_log("[Signature Error] Form: $form_id, Entry: $entry_id, Field: $fieldKey - " . $e->getMessage());
      return 'signature-failed.png'; // or a default filename if appropriate
    }
  }

  private function entryInsert($user_details)
  {
    $formEntryModel = new FormEntryModel();
    $entryId = $formEntryModel->insert(
      [
        'form_id'     => $this->form_id,
        'user_id'     => $user_details['id'],
        'user_ip'     => $user_details['ip'],
        'user_device' => $user_details['device'],
        'referer'     => $user_details['page'],
        'status'      => $this->_saveFormAsDraft ? 9 : 1,
        'created_at'  => $user_details['time'],
      ]
    );
    return $entryId;
  }

  public function submisionLog($user_details, $entry_id, $type)
  {
    $formEntryLogModel = new FormEntryLogModel();
    $submissionLogData = [
      'user_id'       => $user_details['id'],
      'action_type'   => $type, // create, update
      'log_type'      => 'entry',
      'ip'            => $user_details['ip'],
      'form_entry_id' => $entry_id,
      'content'       => ['user_device' => $user_details['device']],
      'form_id'       => $this->form_id,
      'created_at'    => $user_details['time'],
    ];
    $submissionLogData = apply_filters('bitform_filter_submission_log_data', $submissionLogData, $this->form_id, $type);
    $logId = $formEntryLogModel->form_log_insert(
      $submissionLogData
    );
    return $logId;
  }

  private function isArrayAllKeyInt($InputArray)
  {
    if (!is_array($InputArray)) {
      return false;
    }

    if (count($InputArray) <= 0) {
      return true;
    }

    return array_unique(array_map('is_int', array_keys($InputArray))) === [true];
  }

  public function formatSubmittedData($submitted_data)
  {
    $form_content = $this->getFormContent();
    $form_fields = $form_content->fields;

    foreach ($submitted_data as $key => $value) {
      if (!isset($form_fields->{$key})) {
        continue;
      }
      $field_data = $form_fields->{$key};
      $field_type = $field_data->typ;
      $normalizedParentValue = $this->normalizeSubmittedValue($value);
      $parentFieldName = isset($field_data->fieldName) ? $field_data->fieldName : '';
      if (!empty($field_data->childFields) && is_array($field_data->childFields)) {
        foreach ($field_data->childFields as $childFieldRef) {
          $childFieldKey = isset($childFieldRef->fldKey) ? $childFieldRef->fldKey : '';
          if (empty($childFieldKey) || !isset($form_fields->{$childFieldKey})) {
            continue;
          }

          $childFieldData = $form_fields->{$childFieldKey};
          $childFieldName = isset($childFieldData->fieldName) ? $childFieldData->fieldName : '';
          $childFieldName = FieldValueHandler::deriveChildName($childFieldName, $parentFieldName);
          if (empty($childFieldName)) {
            continue;
          }

          $childValue = FieldValueHandler::extractChildValueFromParentValue($normalizedParentValue, $childFieldName, $childFieldKey);
          if (null !== $childValue) {
            $submitted_data[$childFieldKey] = $childValue;
          }
        }
      }

      if ($this->isRepeatedField($key) && in_array($field_type, ['name', 'address'])) {
        $submitted_data[$key] = $this->normalizeRepeatedCompositeFieldInput($normalizedParentValue);
      }

      if ('select' === $field_type && !empty($field_data->config->multipleSelect)) {
        $valueArr = [];
        if ($this->isRepeatedField($key) && is_array($normalizedParentValue)) {
          foreach ($normalizedParentValue as $index => $v) {
            $valueArr[$index] = explode(BITFORMS_BF_SEPARATOR, $v);
          }
        } else {
          $valueArr = explode(BITFORMS_BF_SEPARATOR, (string) $value);
        }
        $submitted_data[$key] = $valueArr;
      }
    }
    $submitted_data = apply_filters('bitform_filter_format_submitted_data', $submitted_data, $this->form_id);
    return $submitted_data;
  }

  private function normalizeSubmittedValue($value)
  {
    if (!is_string($value)) {
      return $value;
    }

    $decoded = json_decode($value, true);
    return (JSON_ERROR_NONE === json_last_error()) ? $decoded : $value;
  }

  private function addNewFilePathToFiles($form_id, $entry_id, $file_fields = [])
  {
    $common_file_path = Helpers::getFullPathWithEncryptedEntryId($form_id, $entry_id);
    foreach ($_FILES as $field_key => $file_details) {
      if (!($file_fields && in_array($field_key, $file_fields))) {
        continue;
      }

      $isRepeaterFldKey = $this->isRepeatedField($field_key);
      if ($isRepeaterFldKey && isset($file_details['new_name'])) {
        // If 'new_name' is an array (i.e., for repeated fields)
        foreach ($file_details['new_name'] as $slNo => $newFileNamesArray) {
          if (is_array($newFileNamesArray)) {
            foreach ($newFileNamesArray as $newFileName) {
              $filePath = $common_file_path . DIRECTORY_SEPARATOR . $newFileName;
              $_FILES[$field_key]['file_path'][$slNo][] = $filePath;
            }
          } else {
            // Generate the file path for each file
            $filePath = $common_file_path . DIRECTORY_SEPARATOR . $newFileNamesArray;
            $_FILES[$field_key]['file_path'][$slNo] = $filePath;
          }
        }
      } elseif (isset($file_details['new_name'])) {
        // If 'new_name' is an array (i.e., for repeated fields)
        if (is_array($file_details['new_name'])) {
          foreach ($file_details['new_name'] as $slNo => $newFileName) {
            // Generate the file path for each file
            $filePath = $common_file_path . DIRECTORY_SEPARATOR . $newFileName;
            $_FILES[$field_key]['file_path'][$slNo] = $filePath;
          }
        } else {
          $filePath = $common_file_path . DIRECTORY_SEPARATOR . $file_details['new_name'];
          $_FILES[$field_key]['file_path'] = $filePath;
        }
      }
    }
  }

  private function formatRepeateFieldData($submitted_data, $form_fields)
  {
    $repeaterFields = $this->getRepeaterFields();
    foreach ($repeaterFields as $repeaterFldKey => $repeatedFields) {
      $repeatIndexes = $submitted_data["{$form_fields[$repeaterFldKey]['name']}-repeat-index"];
      $repeatIndexes = explode(',', $repeatIndexes);
      foreach ($repeatIndexes as $slNo => $repeatIndex) {
        foreach ($repeatedFields as $repeatedField) {
          if (!isset($submitted_data[$repeatedField][$repeatIndex])) {
            continue;
          }
          if (!isset($submitted_data[$repeaterFldKey][$slNo])) {
            $submitted_data[$repeaterFldKey][$slNo] = [];
          }
          if (!isset($submitted_data[$repeaterFldKey][$slNo][$repeatedField])) {
            $submitted_data[$repeaterFldKey][$slNo][$repeatedField] = [];
          }
          $submitted_data[$repeaterFldKey][$slNo][$repeatedField] = $submitted_data[$repeatedField][$repeatIndex];
        }
      }
      foreach ($repeatedFields as $repeatedField) {
        unset($submitted_data[$repeatedField]);
      }
      unset($submitted_data["{$form_fields[$repeaterFldKey]['name']}-repeat-index"]);
    }

    return $submitted_data;
  }

  private function saveEntryMeta($submitted_data, $entry_id)
  {
    $errorInEntryMetaInsert = false;
    $entryMeta = new FormEntryMetaModel();
    foreach ($submitted_data as $key => $value) {
      $value = $submitted_data[$key];
      if (is_string($value)) {
        $value = wp_unslash($value);
      } elseif ($this->isArrayAllKeyInt($value)) {
        $value = wp_json_encode(array_values($value));
      } else {
        $value = wp_json_encode($value);
      }
      // Form entry meta insert; meta_key/meta_value required to store dynamic field data per entry.
      $status = $entryMeta->insert(
        [
          'bitforms_form_entry_id' => $entry_id,
          'meta_key'               => $key,
          'meta_value'             => $value,
        ]
      );
      if (is_wp_error($status)) {
        $errorInEntryMetaInsert = true;
        break;
      }
    }
    return $errorInEntryMetaInsert;
  }

  public function setSaveFormAsDraft()
  {
    $this->_saveFormAsDraft = true;
  }

  public function saveFormEntry($submitted_data)
  {
    // CSRF verified upstream via FrontendFormManager::verifySubmissionNonce() before this method is invoked.
    $submitted_data = $this->formatSubmittedData($submitted_data);
    $submitted_data = apply_filters('bitform_filter_save_form_entry', $submitted_data, $this->form_id);
    $form_content = \json_decode(static::$form[0]->form_content);
    do_action('bitform_save_entry', $this, $submitted_data, $this->form_id);
    $key = null;
    $ipTool = new IpTool();
    $fileHandler = new FileHandler();
    $form_fields = $this->getFields();
    $file_fields = $this->getUploadFields();

    foreach ($_FILES as $file_name => $file_details) {
      if ($file_fields && in_array($file_name, $file_fields)) {
        $validation = $fileHandler->validation($file_name, $file_details, $this->form_id);
        if (!empty($validation['error_type']) && !empty($validation['message'])) {
          return new WP_Error($validation['error_type'], esc_html($validation['message']));
        }
      }
    }
    $user_details = $ipTool->getUserDetail();
    $user_details = apply_filters('bitform_filter_user_details', $user_details, $this->form_id);
    $user_details = apply_filters('bitform_filter_save_entry_user_details', $user_details, $this->form_id);

    $form_fields = $this->getFields();
    $submitted_data = $this->passwordEncrypted($submitted_data, $form_fields);
    $submitted_data = $this->formatRepeateFieldData($submitted_data, $form_fields);
    global $wpdb;
    // Direct transaction control; no user input involved.
    $wpdb->query('START TRANSACTION');
    $entry_id = $this->entryInsert($user_details);
    $log_id = null;

    $GLOBALS['bitform_entry_id'] = $entry_id;

    if (is_wp_error($entry_id)) {
      return new WP_Error('insert_error', __('Sorry, Error occurred in saving form entry', 'bit-form'));
    }
    if ($entry_id) {
      $log_id = $this->submisionLog($user_details, $entry_id, 'create', $key);
      if (is_wp_error($log_id)) {
        $wpdb->query('ROLLBACK');
        return new WP_Error('error_entry_log', __('Sorry, error occurred in logging form entry', 'bit-form'));
      }
    }
    if ($entry_id) {
      $submitted_fields = $this->getFormContentWithValue($submitted_data)->fields;
      $workFlowRunHelper = new WorkFlow($this->form_id);

      $workFlowreturnedOnSubmit = $workFlowRunHelper->executeOnSubmit(
        'create',
        $submitted_fields,
        $submitted_data,
        $entry_id,
        $log_id
      );

      if (!empty($workFlowreturnedOnSubmit['fields'])) {
        $submitted_data = $workFlowreturnedOnSubmit['fields'];
      }

      $file_fields = $this->getUploadFields();
      $formFields = $this->getFields();
      $submitted_data = FileHandler::tempDirToUploadDir($submitted_data, $formFields, $this->form_id, $entry_id);
      $fileHandler = new FileHandler();
      foreach ($_FILES as $field_key => $file_details) {
        if ($file_fields && in_array($field_key, $file_fields)) {
          $fileNames = [];
          $repeaterFldKey = $this->isRepeatedField($field_key);
          if ($repeaterFldKey) {
            foreach ($file_details['name'] as $slNo => $fileName) {
              $repeateFileDetails = [
                'name'     => $file_details['name'][$slNo],
                'type'     => $file_details['type'][$slNo],
                'tmp_name' => $file_details['tmp_name'][$slNo],
                'error'    => $file_details['error'][$slNo],
                'size'     => $file_details['size'][$slNo],
              ];
              $fileNames = $fileHandler->moveUploadedFiles($repeateFileDetails, $this->form_id, $entry_id);
              if (!empty($fileNames)) {
                $submitted_data[$repeaterFldKey][$slNo - 1][$field_key] = $fileNames;
                $_FILES[$field_key]['new_name'][$slNo - 1] = $fileNames;
              }
            }
          } else {
            $fileNames = $fileHandler->moveUploadedFiles($file_details, $this->form_id, $entry_id);
            if (!empty($fileNames)) {
              $submitted_data[$field_key] = $fileNames;
              $_FILES[$field_key]['new_name'] = $fileNames;
            }
          }
        }
      }

      // Get the common path for file storage
      $this->addNewFilePathToFiles($this->form_id, $entry_id, $file_fields);

      foreach ($form_content->fields as $key => $field) {
        /* ======== for Signature field ===========*/
        if ('signature' === $field->typ) {
          if (isset($submitted_data[$key])) {
            $fld_data = $submitted_data[$key];
            $img_type = $field->config->imgTyp;
            $submitted_data[$key] = $this->getSignatureFilePath($fld_data, $this->form_id, $key, $entry_id, $img_type);
          }
        }

        //  for Signature field inside reepater
        if ('repeater' === $field->typ) {
          $rptr_data = $submitted_data[$key];
          $formFields = $form_content->fields;
          $this->setSignatureFilePathInRepeater($rptr_data, $key, $formFields, $entry_id, $submitted_data);
        }
      }

      if (!isset($form_content->additional->enabled->submission)) {
        $errorInEntryMetaInsert = $this->saveEntryMeta($submitted_data, $entry_id);
        if ($errorInEntryMetaInsert) {
          do_action('bitform_save_entry_error', $this, $submitted_data, $this->form_id);
          $wpdb->query('ROLLBACK');
          return new WP_Error('insert_error', __('Sorry, Error occured in saving form entry data', 'bit-form'));
        }
        do_action('bitform_after_save_entry_success', $this, $submitted_data, $this->form_id, $entry_id);
      } else {
        $wpdb->query('ROLLBACK');
      }
      $wpdb->query('COMMIT');
      $this->setSubmissionCount();
      $workFlowreturnedOnSubmit['entry_id'] = $entry_id;
      $workFlowreturnedOnSubmit['fields'] = $submitted_data;
      $workFlowreturnedOnSubmit = apply_filters('bitform_filter_return_submit_success', $workFlowreturnedOnSubmit, $this->form_id);

      return $workFlowreturnedOnSubmit;
    }
  }

  private function setSignatureFilePathInRepeater($repeaterData, $repeaterFieldKey, $formFields, $entry_id, &$submitted_data)
  {
    foreach ($repeaterData as $rptr_entry_index => $rptr_entries) {
      foreach ($rptr_entries as $entry_key => $entry_value) {
        $rptr_entry_info = $formFields->{$entry_key};

        if ('signature' === $rptr_entry_info->typ) {
          $imgType = $rptr_entry_info->config->imgTyp;
          $signatureImage = $this->getSignatureFilePath($entry_value, $this->form_id, $repeaterFieldKey, $entry_id, $imgType);
          $submitted_data[$repeaterFieldKey][$rptr_entry_index][$entry_key] = $signatureImage;
        }
      }
    }
  }

  public function passwordEncrypted($updatedValue, $form_fields)
  {
    $integrationHandler = new IntegrationHandler($this->form_id);
    $formIntegrations = $integrationHandler->getAllIntegration('wp_user_auth', 'wp_auth', 1);
    if (!isset($formIntegrations->errors['result_empty'])) {
      foreach ($form_fields as $field) {
        if (array_key_exists($field['key'], $updatedValue) && 'password' === $field['type']) {
          $updatedValue[$field['key']] = '**** (encrypted)';
        }
      }
    }
    return $updatedValue;
  }

  public function updateFormEntry($updatedValue, $formID, $entryID)
  {
    // CSRF / entry-token verified upstream via FrontendFormManager::handleUpdateEntry() before this method is invoked.
    $updatedValue = $this->formatSubmittedData($updatedValue);
    $updatedValue = apply_filters('bitform_filter_update_form_entry', $updatedValue, $this->form_id);
    do_action('bitform_update_entry', $this, $updatedValue, $formID, $entryID);
    $form_content = $this->getFormContent();
    if (isset($form_content->additional->enabled->submission)) {
      // Run workflow but skip DB/meta update
      $workFlowRunHelper = new WorkFlow($formID);
      $fieldsWithValue = $this->getFormContentWithValue($updatedValue)->fields;
      $workFlowreturnedOnSubmit = $workFlowRunHelper->executeOnSubmit(
        'edit',
        $fieldsWithValue,
        $updatedValue,
        $entryID,
        0
      );
      if (empty($workFlowreturnedOnSubmit['message'])) {
        $workFlowreturnedOnSubmit['message'] = __('Entry update skipped due to submission restriction.', 'bit-form');
      }
      $workFlowreturnedOnSubmit['entry_id'] = $entryID;
      $workFlowreturnedOnSubmit = apply_filters('bitform_filter_return_edit_success', $workFlowreturnedOnSubmit, $this->form_id);
      return $workFlowreturnedOnSubmit;
    }

    $formEntryModel = new FormEntryModel();
    $formEntryLogModel = new FormEntryLogModel();
    $formOldData = $formEntryLogModel->get_form_value($entryID);
    $key = null;
    $entryMeta = new FormEntryMetaModel();
    $ipTool = new IpTool();
    $user_details = $ipTool->getUserDetail();
    $user_details = apply_filters('bitform_filter_user_details', $user_details, $this->form_id);
    $user_details = apply_filters('bitform_filter_update_entry_user_details', $user_details, $this->form_id);

    $form_fields = $this->getFields();

    $updatedValue = $this->passwordEncrypted($updatedValue, $form_fields);
    $updatedValue = $this->formatRepeateFieldData($updatedValue, $form_fields);
    $field_map = [];
    foreach ($formOldData as $index => $data) {
      foreach ($form_fields as $field_key => $field) {
        if ($data->meta_key === $field['key']) {
          $field_map[$field_key] = $field['key'];
        }
      }
    }
    $geResult = $formEntryModel->get('status', ['id' => $entryID]);
    if (is_wp_error($geResult) || empty($geResult)) {
      return new WP_Error('empty_form', __('provided form entries does not exists', 'bit-form'));
    }
    $oldEntry = $geResult[0];
    $formEntry = $formEntryModel->update(
      [
        'status'      => ('9' === $oldEntry->status && !$this->_saveFormAsDraft) ? 1 : $oldEntry->status,
        'updated_at'  => $user_details['time'],
      ],
      [
        'form_id' => $formID,
        'id'      => $entryID,
      ]
    );
    $log_id = null;
    if ($formEntry) {
      $log_id = $this->submisionLog($user_details, $entryID, 'update');
    }

    if (is_wp_error($formEntry) || !$formEntry) {
      return new WP_Error('empty_form', __('provided form entries does not exists', 'bit-form'));
    }
    $formFields = $this->getFields();
    $updatedValue = FileHandler::tempDirToUploadDir($updatedValue, $formFields, $this->form_id, $entryID);
    $file_fields = $this->getUploadFields();
    if (count($file_fields) > 0) {
      $fileHandler = new FileHandler();
      foreach ($_FILES as $file_name => $file_details) {
        if ($file_fields && in_array($file_name, $file_fields)) {
          $validation = $fileHandler->validation($file_name, $file_details, $this->form_id);
          if (!empty($validation['error_type']) && !empty($validation['message'])) {
            return new WP_Error($validation['error_type'], esc_html($validation['message']));
          }
        }
      }
      if (is_object($updatedValue)) {
        $updatedValue = (array) $updatedValue;
      }
      foreach ($file_fields as $field_key) {
        $repeaterFldKey = $this->isRepeatedField($field_key);
        if (isset($updatedValue[$field_key . '_old'])) {
          // Handle file deletion for repeater fields
          if ($repeaterFldKey) {
            // Form entry meta lookup; meta_key/meta_value query required to retrieve repeater field data by entry.
            $repeaterExistData = $entryMeta->get(
              'meta_value',
              [
                'bitforms_form_entry_id' => $entryID,
                'meta_key'               => $repeaterFldKey,
              ]
            );
            if (!is_wp_error($repeaterExistData)) {
              // restructor json
              $repeaterExistData = json_decode($repeaterExistData[0]->meta_value, true);
              $repeaterExistFiles = [];
              $repeaterDeleted_files = [];
              $repeaterFiles_old = [];
              foreach ($repeaterExistData as $index => $repeaterRow) {
                $repeaterExistFiles[$index] = [];
                if (isset($repeaterRow[$field_key]) && !empty($repeaterRow[$field_key])) {
                  $repeaterExistFiles[$index] = json_decode($repeaterRow[$field_key], true);
                }
                $repeaterFiles_old[$index] = empty($updatedValue[$field_key . '_old'][$index]) ? [] : explode(',', $updatedValue[$field_key . '_old'][$index]);
                $repeaterDeleted_files[$index] = array_diff($repeaterExistFiles[$index], $repeaterFiles_old[$index]);
                $fileHandler->deleteFiles($formID, $entryID, $repeaterDeleted_files[$index]);
              }
            }
          } else {
            // Handle file deletion for non-repeater fields; meta_key/meta_value lookup required to identify stored file paths per entry.
            $file_exists = $entryMeta->get(
              'meta_value',
              [
                'bitforms_form_entry_id' => $entryID,
                'meta_key'               => $field_key,
              ]
            );
            if (!is_wp_error($file_exists) && count($file_exists) > 0) {
              $files_in_db = json_decode($file_exists[0]->meta_value);
              $files_old = empty($updatedValue[$field_key . '_old']) ? [] : explode(',', $updatedValue[$field_key . '_old']);
              $deleted_file = array_diff($files_in_db, $files_old);
              if (count($deleted_file) > 0) {
                $fileHandler->deleteFiles($formID, $entryID, $deleted_file);
              }
              $updatedValue[$field_key] = wp_json_encode($files_old);
            }
          }
        }
        if (!empty($_FILES[$field_key]['name'])) {
          if ($repeaterFldKey) {
            // Handle repeater field files
            $file_details = $_FILES[$field_key];
            foreach ($file_details['name'] as $index => $file) {
              // Retrieve existing old files for this specific repeater index
              if (isset($repeaterFiles_old[$index - 1]) && count($repeaterFiles_old[$index - 1]) > 0) {
                $old_meta_value = empty($repeaterFiles_old[$index - 1]) ? [] : $repeaterFiles_old[$index - 1];
                // json format causing issue with repeater file in mail attachment as it's sending broken url(for multistep and abandonment form)
                // $updatedValue[$repeaterFldKey][$index - 1][$field_key] = wp_json_encode($old_meta_value);
                $updatedValue[$repeaterFldKey][$index - 1][$field_key] = $old_meta_value;
              }
              $repeateFileDetails = [
                'name'     => $file_details['name'][$index],
                'type'     => $file_details['type'][$index],
                'tmp_name' => $file_details['tmp_name'][$index],
                'error'    => $file_details['error'][$index],
                'size'     => $file_details['size'][$index],
              ];
              $meta_value = $fileHandler->moveUploadedFiles($repeateFileDetails, $formID, $entryID, $index);
              if (!empty($meta_value)) {
                $mergedMetaValueWithOld = isset($old_meta_value) ? array_merge($old_meta_value, (array) $meta_value) : (array) $meta_value;
                // json format causing issue with repeater file in mail attachment as it's sending broken url(for multistep and abandonment form)
                // $updatedValue[$repeaterFldKey][$index - 1][$field_key] = wp_json_encode($mergedMetaValueWithOld);
                $updatedValue[$repeaterFldKey][$index - 1][$field_key] = $mergedMetaValueWithOld;

                $_FILES[$field_key]['new_name'][$index - 1] = $mergedMetaValueWithOld;
                // $_FILES[$field_key]['file_path'][$index - 1] = $common_file_path . DIRECTORY_SEPARATOR . $meta_value;
              }
            }
          } else {
            // Handle non-repeater field files
            $meta_value = $fileHandler->moveUploadedFiles($_FILES[$field_key], $formID, $entryID);
            if (!empty($meta_value)) {
              $_FILES[$field_key]['new_name'] = $meta_value;
              if (isset($updatedValue[$field_key . '_old']) && !is_wp_error($file_exists) && count($file_exists) > 0) {
                $meta_value = empty($files_old) ? $meta_value : array_merge($meta_value, $files_old);
                $updatedValue[$field_key] = $meta_value;
              } else {
                $updatedValue[$field_key] = $meta_value;
              }
            }
          }
        }
      }

      // Get the common file path to avoid repetitive calculation
      $this->addNewFilePathToFiles($formID, $entryID, $file_fields);
    }

    if (is_object($updatedValue)) {
      $updatedValue = (array) $updatedValue;
    }
    if (isset($updatedValue['_ajax_nonce'], $_REQUEST['g-recaptcha-response']) && sanitize_text_field(wp_unslash($_REQUEST['g-recaptcha-response']))) {
      unset($updatedValue['_ajax_nonce'], $_REQUEST['g-recaptcha-response']);
    }

    $toUpdateValues = [];
    foreach ($form_fields as $field) {
      if (isset($updatedValue[$field['key']])) {
        $toUpdateValues[$field['key']] = $updatedValue[$field['key']];
      }
    }
    $form_content = \json_decode(static::$form[0]->form_content);

    foreach ($form_content->fields as $key => $field) {
      if ('signature' === $field->typ) {
        $fld_data = $updatedValue[$key];
        $img_type = $field->config->imgTyp;
        $toUpdateValues[$key] = $this->getSignatureFilePath($fld_data, $this->form_id, $key, $entryID, $img_type);
      }

      //  for Signature field inside reepater
      if ('repeater' === $field->typ) {
        $rptr_data = $updatedValue[$key];
        $formFields = $form_content->fields;
        $this->setSignatureFilePathInRepeater($rptr_data, $key, $formFields, $entryID, $toUpdateValues);
      }
    }

    $workFlowRunHelper = new WorkFlow($formID);
    $workFlowreturnedOnSubmit = $workFlowRunHelper->executeOnSubmit(
      'edit',
      $this->getFormContentWithValue($toUpdateValues)->fields,
      $toUpdateValues,
      $entryID,
      $log_id
    );

    if (!empty($workFlowreturnedOnSubmit['fields'])) {
      $updatedValue = $workFlowreturnedOnSubmit['fields'];
    }

    $formEntryMetaUpdateStatus = $entryMeta->update(
      $toUpdateValues,
      [
        'bitforms_form_entry_id' => $entryID,
      ]
    );
    if (is_wp_error($formEntryMetaUpdateStatus) || isset($newFileInsertStatus) && is_wp_error($newFileInsertStatus)) {
      do_action('bitform_update_entry_error', $this, $toUpdateValues, $formEntryMetaUpdateStatus, $this->form_id);
      return $formEntryMetaUpdateStatus;
    }
    $toUpdateValues = array_merge($formEntryMetaUpdateStatus, ['entry_id' => $entryID]);
    do_action('bitform_after_update_entry_success', $this, $toUpdateValues, $formID, $entryID);
    if (empty($workFlowreturnedOnSubmit['message'])) {
      $workFlowreturnedOnSubmit['message'] = __('Entry Updated Successfully', 'bit-form');
    }
    $customFieldHandler = new CustomFieldHandler();
    $toUpdateValues = $customFieldHandler->updatedData($form_fields, $toUpdateValues);

    $workFlowreturnedOnSubmit['updatedData'] = $toUpdateValues;
    $counter = 0;
    for ($i = 0; $i < count($formOldData); $i++) {
      if (array_key_exists($formOldData[$i]->meta_key . '_old', $toUpdateValues)) {
        unset($toUpdateValues[$formOldData[$i]->meta_key . '_old']);
      }
      if (in_array($formOldData[$i]->meta_key, $file_fields)) {
        if (
          empty($_FILES[$formOldData[$i]->meta_key]['name'])
          || (is_array($_FILES[$formOldData[$i]->meta_key]['name'])
              && 1 === count($_FILES[$formOldData[$i]->meta_key]['name'])
              && empty($_FILES[$formOldData[$i]->meta_key]['name'][0]))
        ) {
          unset($toUpdateValues[$formOldData[$i]->meta_key]);
          continue;
        }
        if (is_array($_FILES[$formOldData[$i]->meta_key]['name']) && !in_array($_FILES[$formOldData[$i]->meta_key]['name'], json_decode($formOldData[$i]->meta_value))) {
          $sanitized_names = array_map('sanitize_file_name', array_map('wp_unslash', (array) $_FILES[$formOldData[$i]->meta_key]['name']));
          $key[$i] = '${' . $formOldData[$i]->meta_key . '} file was Updated  To ' . wp_json_encode($sanitized_names);
        } elseif (!is_array($_FILES[$formOldData[$i]->meta_key]['name']) && !in_array($_FILES[$formOldData[$i]->meta_key]['name'], json_decode($formOldData[$i]->meta_value))) {
          $key[$i] = '${' . $formOldData[$i]->meta_key . '} file was Updated  To ' . sanitize_file_name(wp_unslash($_FILES[$formOldData[$i]->meta_key]['name']));
        }
        unset($toUpdateValues[$formOldData[$i]->meta_key]);
      } elseif (isset($toUpdateValues[$formOldData[$i]->meta_key])) {
        if (is_array($toUpdateValues[$formOldData[$i]->meta_key])) {
          if (json_decode($formOldData[$i]->meta_value) !== $toUpdateValues[$formOldData[$i]->meta_key]) {
            $key[$i] = '${' . $formOldData[$i]->meta_key . '} was Updated From ' . implode(',', json_decode($formOldData[$i]->meta_value)) . ' To ' . implode(',', $toUpdateValues[$formOldData[$i]->meta_key]);
          }
        } elseif (is_string($toUpdateValues[$formOldData[$i]->meta_key]) && !FieldValueHandler::isEmpty($toUpdateValues[$formOldData[$i]->meta_key])) {
          if ($formOldData[$i]->meta_value !== $toUpdateValues[$formOldData[$i]->meta_key]) {
            $key[$i] = '${' . $formOldData[$i]->meta_key . '} was Updated' . ($formOldData[$i]->meta_value ? ' From ' . $formOldData[$i]->meta_value : '') . ' To ' . $toUpdateValues[$formOldData[$i]->meta_key];
          }
        }
      }
      $counter++;
    }

    $newField = array_keys(array_diff_key($formEntryMetaUpdateStatus, $field_map));
    for ($i = 0; $i < count($newField); $i++) {
      if (is_array($toUpdateValues[$newField[$i]]) && !empty($toUpdateValues[$newField[$i]])) {
        $key[$counter + $i] = '${' . $newField[$i] . '} Updated To ' . implode(',', $toUpdateValues[$newField[$i]]);
      } elseif (is_string($newField[$i]) && !FieldValueHandler::isEmpty($toUpdateValues[$newField[$i]])) {
        $key[$counter + $i] = '${' . $newField[$i] . '} Updated To ' . $toUpdateValues[$newField[$i]];
      }
    }
    if (null !== $key) {
      $logUpdate = implode('b::f', (array) $key);
      $formEntryLogUpdate = $formEntryLogModel->logUpdate($logUpdate, $log_id);
    }
    $workFlowreturnedOnSubmit['entry_id'] = $entryID;
    $workFlowreturnedOnSubmit = apply_filters('bitform_filter_return_edit_success', $workFlowreturnedOnSubmit, $this->form_id);

    return $workFlowreturnedOnSubmit;
  }

  public function getRepeaterFields()
  {
    if (!is_null($this->_repeaterFields)) {
      return $this->_repeaterFields;
    }
    $repeaterFields = [];
    $form_content = \json_decode(static::$form[0]->form_content);
    $fields = $form_content->fields;
    $nestedLayouts = !empty($form_content->nestedLayout) ? $form_content->nestedLayout : [];
    foreach ($nestedLayouts as $fieldKey => $repeatLayout) {
      if ('repeater' !== $fields->{$fieldKey}->typ) {
        continue;
      }
      $repeaterFields[$fieldKey] = [];
      foreach ($repeatLayout->lg as $fieldLayoutData) {
        $repeaterFields[$fieldKey][] = $fieldLayoutData->i;
      }
    }
    $this->_repeaterFields = $repeaterFields;
    return $repeaterFields;
  }

  public function isRepeatedField($fieldKey)
  {
    $repeatedFields = $this->getRepeaterFields();
    foreach ($repeatedFields as $repeaterKey => $repeaterFields) {
      if (in_array($fieldKey, $repeaterFields)) {
        return $repeaterKey;
      }
    }
    return false;
  }

  public function getParentRepeaterField($fieldKey)
  {
    $repeatedFields = $this->getRepeaterFields();
    foreach ($repeatedFields as $repeaterKey => $repeaterFields) {
      if (in_array($fieldKey, $repeaterFields)) {
        return $repeaterKey;
      }
    }
    return null;
  }

  public function isRepeaterField($fieldKey)
  {
    $repeatedFields = $this->getRepeaterFields();
    if (array_key_exists($fieldKey, $repeatedFields)) {
      return true;
    }
    return false;
  }

  public function fieldNameReplaceOfPost()
  {
    // CSRF verified upstream before this method is called; $_POST/$_FILES are being normalized (field key remapping), not reading new user input.
    $fields = $this->getFields();
    foreach ($fields as $fieldKey => $fieldData) {
      if (array_key_exists('name', $fieldData)) {
        $fldName = $fieldData['name'];
        $catchChildFldNamePattern = '/\[(.*?)\]/';
        // catching the child field name for confirm field, name field's child
        // preg_match_all($catchChildFldNamePattern, $fldName, $matches);
        $fldName = preg_replace($catchChildFldNamePattern, '', $fldName);
        $fldName = str_replace(['.', ' '], '_', $fldName);
        if (!empty($fldName)) {
          if (array_key_exists($fldName, $_POST)) {
            $temp = $this->sanitize_text_recursive($_POST[$fldName]);
            unset($_POST[$fldName]);
            $_POST[$fieldKey] = $temp;
          } elseif (array_key_exists($fldName, $_FILES)) {
            $temp = $this->sanitize_text_recursive($_FILES[$fldName], false);
            unset($_FILES[$fldName]);
            $_FILES[$fieldKey] = $temp;
          }
          // Convert _session_id suffix (used by email-otp and similar fields)
          if (array_key_exists($fldName . '_session_id', $_POST)) {
            $temp = sanitize_text_field(wp_unslash($_POST[$fldName . '_session_id']));
            unset($_POST[$fldName . '_session_id']);
            $_POST[$fieldKey . '_session_id'] = $temp;
          }
        }
      }
    }
  }

  private function sanitize_text_recursive($input, $unslash = true)
  {
    if (is_array($input)) {
      return array_map(fn ($item) => $this->sanitize_text_recursive($item, $unslash), $input);
    }

    return sanitize_text_field($unslash ? wp_unslash($input) : $input);
  }

  private function normalizeRepeatedCompositeFieldInput($value)
  {
    if (!is_array($value) || empty($value)) {
      return $value;
    }

    $hasNestedArray = false;
    foreach ($value as $childValues) {
      if (!is_array($childValues)) {
        return $value;
      }
      $hasNestedArray = true;
    }

    if (!$hasNestedArray) {
      return $value;
    }

    $formattedValue = [];
    foreach ($value as $childKey => $childValues) {
      foreach ($childValues as $repeatIndex => $repeatValue) {
        if (!isset($formattedValue[$repeatIndex]) || !is_array($formattedValue[$repeatIndex])) {
          $formattedValue[$repeatIndex] = [];
        }
        $formattedValue[$repeatIndex][$childKey] = $repeatValue;
      }
    }

    return $formattedValue;
  }

  public function setSubmissionCount($countStep = 1)
  {
    $update_status = $this->formModel->update(
      [
        'entries' => intval(static::$form[0]->entries) + $countStep,
      ],
      [
        'id' => $this->form_id,
      ]
    );
  }

  public function resetSubmissionCount($countStep)
  {
    $update_status = $this->formModel->update(
      [
        'entries' => intval($countStep),
      ],
      [
        'id' => $this->form_id,
      ]
    );
  }

  public function getCaptchaSettings()
  {
    $formContents = $this->getFormContent();
    $fieldStr = wp_json_encode($formContents->fields);
    if (false !== strpos($fieldStr, '"typ":"recaptcha"')) {
      return true;
    }
  }

  public function getTurnstileSettings()
  {
    $formContents = $this->getFormContent();
    $fieldStr = wp_json_encode($formContents->fields);
    if (false !== strpos($fieldStr, '"typ":"turnstile"')) {
      return true;
    }
  }

  public function isFieldTypeExist($fieldType)
  {
    $formContents = $this->getFormContent();
    $fieldStr = wp_json_encode($formContents->fields);
    if (false !== strpos($fieldStr, '"typ":"' . $fieldType . '"')) {
      return true;
    }
  }

  public function getCaptchaV3Settings()
  {
    $formContents = $this->getFormContent();
    if (!empty($formContents->additional->enabled) && !empty($formContents->additional->enabled->recaptchav3)) {
      return $formContents->additional->settings->recaptchav3;
    }
    return false;
  }

  // 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)) {
  //             $msgDetailsId = $msg->details->id;
  //             $idObj = json_decode(stripslashes($msgDetailsId));
  //             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 $key => $msgItem) {
  //     $messageMarkups .= $this->messageMarkup($msgItem->id);
  //   }

  //   return $messageMarkups;
  // }

  //   private function messageMarkup($msgId) {
  //     return <<<SUCCESSMSG
  //             <div role="dialog" aria-hidden="true" data-modal-backdrop="true" class="{$this->getAtomicCls("msg-container-{$msgId}")} deactive2 test">
  //               <div role="button" class="{$this->getAtomicCls("msg-background-{$msgId}")} msg-backdrop">
  //                 <div class="bf-notification-message {$this->getAtomicCls("msg-content-{$msgId}")}">
  //                   <button 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>
  // SUCCESSMSG;
  //   }

  public function getAtomicCls($element)
  {
    $atomicClassMap = $this->getAtomicClsMap();
    if (is_object($atomicClassMap) && property_exists($atomicClassMap, ".$element")) {
      $getAtomicCls = $atomicClassMap->{".$element"};
      return implode(' ', $getAtomicCls) . " $element";
    }
    return $element;
  }

  public function isGCLIDEnabled()
  {
    $formContents = $this->getFormContent();
    if (isset($formContents->additional->enabled->captureGCLID) && $formContents->additional->enabled->captureGCLID) {
      return true;
    }
    return false;
  }

  protected function addEntryInfo($field_details, $counter)
  {
    $infos = [
      '__user_id'      => __('User', 'bit-form'),
      '__entry_status' => __('Status', 'bit-form'),
      //'__user_location' => __(''),
      '__referer'     => __('Refer URL', 'bit-form'),
      '__user_device' => __('Device', 'bit-form'),
      '__user_ip'     => __('IP address', 'bit-form'),
      '__created_at'  => __('Created Time', 'bit-form'),
      '__updated_at'  => __('Modified Time', 'bit-form'),
    ];
    foreach ($infos as $key => $value) {
      $field_details[$counter]['name'] = $value;
      $field_details[$counter]['key'] = $key;
      $field_details[$counter]['type'] = 'sys';
      $counter = $counter + 1;
    }

    return $field_details;
  }
}
