<?php

/**
 * @package     Joomla.Administrator
 * @subpackage  com_formea
 *
 * @copyright   Copyright (C) 2010-2025 Feseur Sdn Bhd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 * @version     1.3.0
 */

namespace Joomla\Component\Formea\Administrator\Model;

defined('_JEXEC') or die;

use Exception;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Form\FormFactoryInterface;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Component\Formea\Site\Helper\FormeaGeneralHelper;
use Joomla\Component\Formea\Site\Libraries\FormeaGridItem;
use Joomla\Component\Formea\Site\Libraries\FormeaGroupedElement;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
use stdClass;

/**
 * Item Model for a Formea.
 *
 * @since  1.0.0
 */
class GroupedelementModel extends AdminModel
{
  /**
   * The type alias for this content type.
   *
   * @var    string
   * @since  1.0.0
   */
  public $typeAlias = 'com_formea.groupedelement';

  public $hasCoreExtensions;
  public $coreExtensions;

  protected $app;

  public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null)
  {
    parent::__construct($config, $factory, $formFactory);
    $this->app               = Factory::getApplication();
    $this->langTag           = $this->app->getLanguage()->getTag();
    $this->langTagUnderscore = str_replace('-', '_', $this->langTag);
    $this->coreExtensions    = FormeaGeneralHelper::getCoreExtensions(1, [3]);
    if (!empty($this->coreExtensions)) {
      $this->hasCoreExtensions = true;
    } else {
      $this->hasCoreExtensions = false;
    }
  }

  /**
   * Method to get the row form.
   *
   * @param   array    $data      Data for the form.
   * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
   *
   * @return  Form|boolean  A \Form object on success, false on failure
   *
   * @throws  Exception
   * @since   1.0.0
   */
  public function getForm($data = array(), $loadData = true)
  {
    $xml = file_get_contents(JPATH_ADMINISTRATOR . '/components/com_formea/forms/groupedelement.xml');

    //labelSettings params
    $labelSettingXML = $this->_addLabelSettingsXml();
    $xml             = str_replace('<fields name="labelSettings"/>', $labelSettingXML, $xml);


    //check for core extensions
    $extensionXml    = ' <fields name="cores">' . "\r\n";
    $hasExtensionXML = false;
    $pk              = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id');
    $replaceables    = [
      'GROUPID="0"' => 'data-groupid="' . $pk . '"'
    ];
    if ($this->hasCoreExtensions) {

      $totalExtensions = count($this->coreExtensions);
      for ($j = 0; $j < $totalExtensions; $j++) {
        $filePathXml = FORMEA_CORE_PATH . '/' . $this->coreExtensions[$j]->name . '/' . $this->coreExtensions[$j]->name . '.xml';
        $root        = simplexml_load_file($filePathXml);
        if (isset($root->formConfig)) {
          $configField = $root->formConfig->children()->asXML();
          if (!empty($configField)) {
            foreach ($replaceables as $rk => $rv) {
              $configField = str_replace($rk, $rv, $configField);
            }
            $extensionXml    .= $configField;
            $hasExtensionXML = true;
          }
        }
      }
    }
    $extensionXml .= '</fields>';

    if ($hasExtensionXML) {
      $xml = str_replace('<fields name="cores"/>', $extensionXml, $xml);
    }

    // Get the form.
    $form = $this->loadForm(
      'com_formea.groupedelement',
      $xml,
      ['control' => 'jform', 'load_data' => $loadData]
    );

    return $form;
  }

  protected function _addLabelSettingsXml()
  {
    $languages      = FormeaGeneralHelper::getLanguages(false);
    $totalLanguages = count($languages);
    //  $_xml = file_get_contents();
    $xml             = simplexml_load_file(FORMEA_ADMIN . '/forms/_labelSettings.xml');
    $xmlString       = $xml->formConfig->asXML();
    $xmlString       = str_replace(['<formConfig>', '</formConfig>'], '', $xmlString);
    $labelSettingXML = '<fields name="labelSettings">';
    for ($i = 0; $i < $totalLanguages; $i++) {
      $xmlStringCopy   = $xmlString;
      $labelSettingXML .= '<fields name="' . $languages[$i]->lang_code_underscore . '">';
      $langCodeField   = '<field name="lang_code" type="hidden" value="' . $languages[$i]->lang_code_underscore . '" default="' . $languages[$i]->lang_code_underscore . '"/>';
      $langTitleField  = '<field name="lang_title" type="hidden" value="' . $languages[$i]->title . '" default="' . $languages[$i]->title . '"/>';
      $xmlStringCopy   = str_replace('<field name="lang_code" type="hidden"/>', $langCodeField, $xmlStringCopy);
      $xmlStringCopy   = str_replace('<field name="lang_title" type="hidden"/>', $langTitleField, $xmlStringCopy);
      $labelSettingXML .= $xmlStringCopy;
      $labelSettingXML .= '</fields>';
    }
    $labelSettingXML .= '</fields>';

    return $labelSettingXML;
  }

  /**
   * Method to get the data that should be injected in the form.
   *
   * @return  mixed  The data for the form.
   *
   * @throws  Exception
   * @since   1.0.0
   */
  protected function loadFormData()
  {

    $data = $this->getItem();

    $this->preprocessData('com_formea.coreextension', $data);

    return $data;
  }

  /**
   * Method to get a single record.
   *
   * @param   integer  $pk  The id of the primary key.
   *
   * @return  mixed  Object on success, false on failure.
   *
   * @throws  Exception
   * @since   1.0.0
   */
  public function getItem($pk = null)

  {
    $pk    = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id');
    $table = $this->getTable();

    if ($pk > 0) {
      // Attempt to load the row.
      $return = $table->load($pk);

      // Check for a table object error.
      if ($return === false) {
        // If there was no underlying error, then the false means there simply was not a row in the db for this $pk.
        if (!$table->getError()) {
          $this->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST'));
        } else {
          $this->setError($table->getError());
        }

        return false;
      }
    }

    // Convert to the CMSObject before adding other data.
    $properties = $table->getProperties(1);
    $cores       = [];
    $elementLayout = 0;
    if (isset($properties['id'])) {
      $elementLayout = $properties['id'];
    }
    $properties['elementlayout'] = $elementLayout;

    if (isset($properties['id'])) {
      if ($properties['id'] > 0) {
        $db = $this->getDatabase();
        $query = $db->getQuery(true);
        $query->select('*');
        $query->from($db->quoteName('#__formea_group_cores'));
        $query->where($db->quoteName('group_id') . ' = ' . $properties['id']);
        $db->setQuery($query);
        $coreLists      = $db->loadObjectList();
        $totalCoreLists = count($coreLists);
        for ($k = 0; $k < $totalCoreLists; $k++) {
          if (!empty($coreLists[$k]->params)) {
            $coreParams = json_decode($coreLists[$k]->params);
          } else {
            $coreParams = [];
          }

          foreach ($coreParams as $cpKey => $cpVal) {
            $cores[$coreLists[$k]->corename][$cpKey] = $cpVal;
          }
        }
      }
    }
    $properties['cores']         = $cores;
    $item = ArrayHelper::toObject($properties, CMSObject::class);
    if ($item->id > 0) {
      $labelSettings     = json_decode($item->labelSettings, true);
      $totalLabelSetting = count($labelSettings);

      $lb = [];
      for ($i = 0; $i < $totalLabelSetting; $i++) {
        $labelSettings[$i]['setting']['show_label'] = ($labelSettings[$i]['setting']['show_label']) ? 1 : 0;
        if (!isset($labelSettings[$i]['show_helper'])) {
          $labelSettings[$i]['show_helper'] = true;
        }
        $labelSettings[$i]['show_helper']    = ($labelSettings[$i]['show_helper']) ? 1 : 0;
        $lb[$labelSettings[$i]['lang_code']] = $labelSettings[$i];
      }
      $item->labelSettings = $lb;
    }

    if (property_exists($item, 'params')) {
      $registry     = new Registry($item->params);
      $item->params = $registry->toArray();
    }

    return $item;
  }

  public function setCoreDir($dir)
  {
    $this->coreDir = $this->corePath . $dir;
  }


  /**
   * Preprocess the form.
   *
   * @param   Form    $form   Form object.
   * @param   object  $data   Data object.
   * @param   string  $group  Group name.
   *
   * @return  void
   *
   * @throws  Exception
   * @since   1.0.0
   */
  protected function preprocessForm(Form $form, $data, $group = 'content')
  {
    parent::preprocessForm($form, $data, $group);
  }

  /**
   * Method to save the form data.
   *
   * @param   array  $data  The form data.
   *
   * @return boolean  True on success, False on error.
   *
   * @throws Exception
   * @since 1.0.0
   */
  public function save($data)
  {


    $app         = Factory::getApplication();
    $user        = $app->getIdentity();
    $isNew       = false;
    $currentDate = new Date();
    if ((int) $data['id'] === 0) {
      $data['created_date']  = $currentDate->toSql();
      $data['created_by']    = $user->id;
      $data['modified_date'] = $currentDate->toSql();
      $data['modified_by']   = $user->id;
      $isNew                 = true;
    } else {
      $data['modified_date'] = $currentDate->toSql();
      $data['modified_by']   = $user->id;
    }
    if (isset($data['labelSettings'])) {
      $data['labelSettings'] = array_values($data['labelSettings']);

      $totalLabelSetting = count($data['labelSettings']);
      for ($i = 0; $i < $totalLabelSetting; $i++) {
        $data['labelSettings'][$i]['setting']['show_label'] = filter_var((int) $data['labelSettings'][$i]['setting']['show_label'], FILTER_VALIDATE_BOOLEAN);
        $data['labelSettings'][$i]['show_helper']           = filter_var((int) $data['labelSettings'][$i]['show_helper'], FILTER_VALIDATE_BOOLEAN);
      }
      $data['labelSettings'] = json_encode($data['labelSettings']);
    }
    $data['settings'] = null;
    $data['class']    = null;
    $elementLayout    = null;
    if (isset($data['elementlayout'])) {
      $elementLayout = json_decode($data['elementlayout']);
      if (isset($elementLayout->inputGroup)) {
        $inputGroup = $elementLayout->inputGroup;
        // $data['labelSettings'] = json_encode($inputGroup->labelSettings);
        $data['settings'] = json_encode($inputGroup->settings);
        $data['class']    = $inputGroup->class;
      }
      unset($data['elementlayout']);
    }

    $saved = parent::save($data);
    if ($saved) {
      $group_id = $this->getState($this->getName() . '.id');
      if (!empty($elementLayout)) {
        if (isset($elementLayout->rows)) {
          $this->_storeElementLayout($group_id, $elementLayout->rows, $isNew);
        }
      }
      //store cores
      if (isset($data['cores'])) {
        $this->_storeCoreExtensions((int) $group_id, $data['cores'], $isNew);
      }
    }

    return $saved;
  }

  protected function _storeElementLayout($group_id, array $layoutData, $isNew = false)
  {
    if ($group_id > 0) {
      $db = $this->getDatabase();
      if (!$isNew) {
        //form id already exist, clear past details
        $query             = $db->getQuery(true);
        $targetTables      = [
          ['table' => 'formea_form_rows', 'key' => 'row_type'],
          ['table' => 'formea_form_columns', 'key' => 'column_type'],
          ['table' => 'formea_form_elements', 'key' => 'target_type'],
          ['table' => 'formea_form_groups', 'key' => 'group_type'],
        ];
        $totalTargetTables = count($targetTables);
        for ($j = 0; $j < $totalTargetTables; $j++) {
          $conditions = array(
            $db->quoteName('group_id') . ' = ' . $group_id,
            $db->quoteName($targetTables[$j]['key']) . ' = 1',
          );

          $query->clear();
          $query->delete($db->quoteName('#__' . $targetTables[$j]['table']));
          $query->where($conditions);
          $db->setQuery($query);
          $db->execute();
        }
      }

      $styleSheetParams = FormeaGeneralHelper::getStyleSheetParams();
      $structure        = $this->_storeFormeaGridItem($group_id, $layoutData, $styleSheetParams);
      $upd              = new stdClass();
      $upd->id          = $group_id;
      $upd->layout      = json_encode($structure);
      $db->updateObject('#__formea_groups', $upd, 'id');
    }

    return true;
  }

  protected function _storeFormeaGridItem($group_id, $items, $styleSheetParams)
  {
    $structure  = [];
    $totalItems = count($items);
    $db         = $this->getDatabase();
    for ($i = 0; $i < $totalItems; $i++) {
      $targetId  = 0;
      $item_type = $items[$i]->item_type;
      $settings  = json_encode($items[$i]->settings);
      if ($items[$i]->item_type == 0) {
        $table = '#__formea_form_rows';
        //its a row
        $rowObject = [
          'form_id'  => 0,
          'page_id'  => 0,
          'group_id' => $group_id,
          'row_type' => 1,
          'settings' => $settings,
          'class'    => $items[$i]->class,
          'gutterX'  => $items[$i]->gutterX,
          'gutterY'  => $items[$i]->gutterY,
        ];
        $rowObject = (object) $rowObject;
        $db->insertObject($table, $rowObject);
        $rowObject->id = $db->insertid();
        $items[$i]->id = $rowObject->id;
        $styleSheet    = $this->_processStyleSheet($group_id, $settings, $items[$i], $styleSheetParams);
        $targetId      = $rowObject->id;
      } elseif ($items[$i]->item_type == 1) {
        $table = '#__formea_form_columns';
        //its a column
        $columnObject = [
          'form_id'     => 0,
          'page_id'     => 0,
          'column_type' => 1,
          'group_id'    => $group_id,
          'settings'    => $settings,
          'class'       => $items[$i]->class,
          'column'      => $items[$i]->column,
        ];
        $columnObject = (object) $columnObject;
        $db->insertObject($table, $columnObject);
        $columnObject->id = $db->insertid();
        $items[$i]->id    = $columnObject->id;
        $styleSheet       = $this->_processStyleSheet($group_id, $settings, $items[$i], $styleSheetParams);
        $targetId         = $columnObject->id;
      } elseif ($items[$i]->item_type == 2) {
        $table = '#__formea_form_elements';
        //its an element
        $elementObject = [
          'form_id'          => 0,
          'page_id'          => 0,
          'group_id'         => $group_id,
          'settings'         => $settings,
          'target_type'      => 1,
          'element_id'       => $items[$i]->content->element_id,
          'title'            => $items[$i]->content->title,
          'elementTypeTitle' => $items[$i]->content->elementTypeTitle,
          'type'             => $items[$i]->content->type,
          'labelSettings'    => json_encode($items[$i]->content->labelSettings),
        ];
        $elementObject = (object) $elementObject;
        $db->insertObject($table, $elementObject);
        $elementObject->id = $db->insertid();
        $items[$i]->id     = $elementObject->id;
        $styleSheet        = $this->_processStyleSheet($group_id, $settings, $items[$i], $styleSheetParams);
        $targetId          = $elementObject->id;
      } else {
        //group
        $table = '#__formea_form_groups';
        //its an element
        $groupObject = [
          'form_id'    => 0,
          'page_id'    => 0,
          'group_id'   => $group_id,
          'group_type' => 1,
        ];
        $groupObject = (object) $groupObject;
        $db->insertObject($table, $groupObject);
        $groupObject->id = $db->insertid();
        $items[$i]->id   = $groupObject->id;
        // $styleSheet        = $this->_processStyleSheet($group_id, $settings, $items[$i], $styleSheetParams);
        $targetId = $groupObject->id;
      }
      $structure[$i]['item_type'] = $item_type;
      $structure[$i]['item_id']   = $targetId;
      $structure[$i]['items']     = [];
      if (!empty($styleSheet) && $item_type !== 3) {
        $upd             = new stdClass();
        $upd->id         = $targetId;
        $upd->styleSheet = $styleSheet;
        $db->updateObject($table, $upd, 'id');
      }

      if (!empty($items[$i]->items)) {
        $structure[$i]['items'] = $this->_storeFormeaGridItem($group_id, $items[$i]->items, $styleSheetParams);
      }
    }

    return $structure;
  }

  /**
   * @param   stdClass        $settings
   * @param   FormeaGridItem  $item
   * @param   array           $styleSheetParams  ;
   *
   * @return string
   *
   * @since 1.0.0
   */
  protected function _processStyleSheet($group_id, $settings, $item, $styleSheetParams)
  {
    $settings = new Registry($settings);
    $elemType = 'row_' . $group_id . '_' . $item->id;
    if ($item->item_type == 1) {
      $elemType = 'column_' . $group_id . '_' . $item->id;
    } elseif ($item->item_type == 2) {
      $elemType = 'element_' . $group_id . '_' . $item->content->element_id;
    }

    $elemSelector = '.gp_fm_' . $elemType;
    $st           = $elemSelector . '{';
    $hasRowStyle  = false;

    foreach ($styleSheetParams as $target => $props) {
      $val = $settings->get($target);
      if (!empty($val)) {
        $hasRowStyle = true;
        foreach ($props as $param) {
          $st .= $param . ':' . $val . ';';
        }
      }
    }
    $st .= '}';
    if ($hasRowStyle) {
      return $st;
    } else {
      return '';
    }
  }

  public function _storeCoreExtensions($group_id, $details, $isNew = false)
  {
    if ($group_id > 0) {
      $db = Factory::getDbo();
      if (!$isNew) {
        //form id already exist, clear past details
        $query      = $db->getQuery(true);
        $conditions = array(
          $db->quoteName('group_id') . ' = ' . $group_id,
        );
        $query->delete($db->quoteName('#__formea_group_cores'));
        $query->where($conditions);
        $db->setQuery($query);
        $db->execute();
      }
      foreach ($details as $corename => $coreParam) {
        $enabled = 0;
        if (isset($coreParam['enabled'])) {
          $enabled = (int) $coreParam['enabled'];
        }
        $obj = (object) [
          'group_id' => $group_id,
          'enabled'  => $enabled,
          'corename' => $corename,
          'params'   => json_encode($coreParam)
        ];
        $db->insertObject('#__formea_group_cores', $obj);
      }
    }

    return true;
  }

  public function delete(&$pks)
  {

    if (is_array($pks)) {
      $pks = ArrayHelper::toInteger((array) $pks);
      // Include the plugins for the delete events.
      PluginHelper::importPlugin($this->events_map['delete']);
      $table = $this->getTable();

      $totalIds = count($pks);
      for ($i = 0; $i < $totalIds; $i++) {
        if ($table->load($pks[$i])) {
          if ($this->canDelete($table)) {
            $context = $this->option . '.' . $this->name;

            // Trigger the before delete event.
            $result = Factory::getApplication()->triggerEvent($this->event_before_delete, [$context, $table]);

            if (in_array(false, $result, true)) {
              $this->setError($table->getError());

              return false;
            }
            $formeaGroupedElement = new FormeaGroupedElement($pks[$i]);
            $delete               = $formeaGroupedElement->delete();
            if (!$delete->success) {
              if (isset($delete->msg[0])) {
                $this->setError($delete->msg[0]->text);
              } else {
                $this->setError(Text::_('COM_FORMEA_FAILED_TO_DELETE_VALIDATION'));
              }

              return false;
            }
            Factory::getApplication()->triggerEvent($this->event_after_delete, [$context, $table]);
          }
        }
      }

      // Clear the component's cache
      $this->cleanCache();
    }

    return true;
  }

  public function uploadGroupedElementLayout($group_id, $layoutRows)
  {
    $this->_storeElementLayout($group_id, $layoutRows, true);
  }
}
