<?php

/**
 * @package     Joomla.Site
 * @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\Site\Model;

defined('_JEXEC') or die;

use Exception;
use Joomla\CMS\Application\CMSApplicationInterface;
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\Component\Formea\Administrator\Helper\FormeaHelper;
use Joomla\Component\Formea\Site\Helper\FormeaGeneralHelper;
use Joomla\Component\Formea\Site\Libraries\FormeaElement;
use Joomla\Database\DatabaseDriver;
use Joomla\Filesystem\Path;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
use stdClass;

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

  /**
   * Element type name
   *
   * @var string
   * @since 1.0.0
   */
  public $eltype_name = '';

  /**
   * @var null|object
   * @since 1.0.0
   */
  public $elementType = null;

  /**
   * The directory to the element type end with slash
   * @var string
   * @since 1.0.0
   */
  public $elementDir = '';

  /**
   * Factory::getApplication()
   * @var CMSApplicationInterface|null
   * @since 1.0.0
   */
  public $app;

  public function __construct($config = array(), MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null)
  {
    parent::__construct($config, $factory, $formFactory);
    $this->app = Factory::getApplication();

    if (isset($config['eltype_name'])) {
      $this->eltype_name = $config['eltype_name'];
    } else {
      $this->eltype_name = $this->app->getInput()->getString('type', '');
    }

    if (!empty($this->eltype_name)) {
      $this->elementDir = JPATH_ROOT . '/components/com_formea/plugins/element/' . $this->eltype_name . '/';
    }
  }


  /**
   * 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)
  {
    if (empty($this->eltype_name)) {
      return false;
    }
    $elementType = $this->getElementType($this->eltype_name);
    $filePathXml = $this->elementDir . $this->eltype_name . '.xml';
    $root        = simplexml_load_file($filePathXml);
    $configField = '';
    if (isset($root->elementConfig)) {
      $configField = $root->elementConfig->children()->asXML();
    }


    $xml     = file_get_contents(JPATH_ADMINISTRATOR . '/components/com_formea/forms/element.xml');
    $langTag = $this->app->getLanguage()->getTag();
    $con     = '<fields name="details">' . "\r\n";
    $con     .= FormeaHelper::commonElementDetailXml($langTag);

    $valCon = '<fields name="elementvalues">' . "\r\n";
    //var_dump($elementType->config);die;
    $valCon .= FormeaHelper::commonElementValueXml($langTag, $elementType->config);

    $languages      = FormeaGeneralHelper::getLanguages();
    $totalLanguages = count($languages);
    for ($i = 0; $i < $totalLanguages; $i++) {
      if ($languages[$i]->lang_code !== $langTag) {
        $con    .= FormeaHelper::commonElementDetailXml($languages[$i]->lang_code);
        $valCon .= FormeaHelper::commonElementValueXml($languages[$i]->lang_code, $elementType->config);
      }
    }
    $con    .= '</fields>';
    $valCon .= '</fields>';
    //var_dump($valCon);die;

    $xml = str_replace('<fields name="details"/>', $con, $xml);
    $xml = str_replace('<fields name="elementvalues"/>', $valCon, $xml);

    if (!empty($configField)) {
      $xml = str_replace('<fields name="params"/>', $configField, $xml);
    }


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

    if (empty($form)) {
      return false;
    }

    return $form;
  }


  /**
   * 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.eltype', $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);
    if ((int) $properties['id'] < 1) {
      $properties['type'] = $this->eltype_name;
    } else {
      $fe                          = new FormeaElement(['element_id' => $properties['id']]);
      $attributes                  = ArrayHelper::fromObject($fe->getAttributes());
      $details                     = $fe->getDetails();
      $values                      = $fe->getValues();
      $validations                 = $fe->getValidations();
      $properties['attributes']    = $attributes;
      $properties['details']       = $details;
      $properties['elementvalues'] = $values;
      $properties['validations']   = $validations;
    }
    //var_dump($properties);die;


    $item = ArrayHelper::toObject($properties, CMSObject::class);

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

    return $item;
  }

  /**
   * 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;
    }
    $saved      = parent::save($data);
    $element_id = $this->getState($this->getName() . '.id');
    if (empty($element_id)) {
      $element_id = FormeaGeneralHelper::findElementId($data);
    }
    //var_dump($element_id);die;
    if (!empty($element_id) && $saved) {
      //save attribute
      if (isset($data['attributes'])) {
        $this->_storeAttributes($element_id, $data['attributes']);
      }

      //save details
      if (isset($data['details'])) {
        $this->_storeDetails($element_id, $data['details']);
      }

      //save default values
      if (isset($data['elementvalues'])) {
        $this->_storeValues($element_id, $data['elementvalues']);
      }

      //save validations
      if (isset($data['validations'])) {
        $this->_storeValidations($element_id, $data['validations']);
      }
    }

    return $saved;
  }

  /**
   * Store the element attributes
   *
   * @param   int    $element_id
   * @param   array  $attributes_array
   *
   * @return bool
   *
   * @since 1.0.0
   */
  protected function _storeAttributes(int $element_id, array $attributes_array): bool
  {
    $ret = false;
    if ($element_id > 0 && !empty($attributes_array)) {
      //delete all first
      $container = Factory::getContainer();
      /** @var DatabaseDriver $db */
      $db         = $container->get('DatabaseDriver');
      $query      = $db->getQuery(true);
      $conditions = array(
        $db->quoteName('element_id') . ' = ' . $element_id
      );
      $query->delete($db->quoteName('#__formea_element_attributes'));
      $query->where($conditions);
      $db->setQuery($query);
      $db->execute();
      $query->clear();
      $obj             = new stdClass();
      $obj->element_id = $element_id;
      if (isset($attributes_array['size'])) {
        $obj->size = $attributes_array['size'];
      }
      if (isset($attributes_array['class'])) {
        $obj->class = $attributes_array['class'];
      }
      if (isset($attributes_array['max_size'])) {
        $obj->max_size = $attributes_array['max_size'];
      }
      if (isset($attributes_array['additional_attr'])) {
        $obj->additional_attr = $attributes_array['additional_attr'];
      }
      $ret = $db->insertObject('#__formea_element_attributes', $obj);
    }

    return $ret;
  }

  /**
   * Store the element details that are translatable
   *
   * @param   int    $element_id
   * @param   array  $details_array  [lang_code => [caption => string, placeholder => string ,description => string]]
   *
   * @return bool
   *
   * @since 1.0.0
   */
  protected function _storeDetails(int $element_id, array $details_array): bool
  {
    $ret = false;
    if ($element_id > 0 && !empty($details_array)) {
      //delete all first
      $container = Factory::getContainer();
      /** @var DatabaseDriver $db */
      $db         = $container->get('DatabaseDriver');
      $query      = $db->getQuery(true);
      $conditions = array(
        $db->quoteName('element_id') . ' = ' . $element_id
      );
      $query->delete($db->quoteName('#__formea_element_details'));
      $query->where($conditions);
      $db->setQuery($query);
      $db->execute();
      $query->clear();
      $stat = [];
      foreach ($details_array as $lang_code_underscore => $data) {
        $lang_code       = str_replace('_', '-', $lang_code_underscore);
        $obj             = new stdClass();
        $obj->element_id = $element_id;
        if (isset($data['caption'])) {
          $obj->caption = $data['caption'];
        }
        if (isset($data['placeholder'])) {
          $obj->placeholder = $data['placeholder'];
        }
        if (isset($data['description'])) {
          $obj->description = $data['description'];
        }
        $obj->language = $lang_code;
        $stat[]        = $db->insertObject('#__formea_element_details', $obj);
      }
      if (!in_array(false, $stat)) {
        $ret = true;
      }
    }

    return $ret;
  }

  /**
   * Store the element values that are translatable
   *
   * @param   int    $element_id
   * @param   array  $values_array  [lang_code => [defval => string, option_lists => OptionTextVal[] ]]
   *
   * @return bool
   *
   * @since 1.0.0
   */
  protected function _storeValues(int $element_id, array $values_array): bool
  {
    $ret = false;
    if ($element_id > 0 && !empty($values_array)) {
      //delete all first
      $container = Factory::getContainer();
      /** @var DatabaseDriver $db */
      $db         = $container->get('DatabaseDriver');
      $query      = $db->getQuery(true);
      $conditions = array(
        $db->quoteName('element_id') . ' = ' . $element_id
      );
      $query->delete($db->quoteName('#__formea_element_values'));
      $query->where($conditions);
      $db->setQuery($query);
      $db->execute();
      $query->clear();
      $stat = [];
      foreach ($values_array as $lang_code_underscore => $data) {
        $lang_code       = str_replace('_', '-', $lang_code_underscore);
        $obj             = new stdClass();
        $obj->element_id = $element_id;
        if (isset($data['defval'])) {
          $obj->defval = $data['defval'];
        }
        if (isset($data['option_lists'])) {
          $obj->option_lists = json_encode($data['option_lists']);
        }
        $obj->language = $lang_code;
        $stat[]        = $db->insertObject('#__formea_element_values', $obj);
      }
      if (!in_array(false, $stat)) {
        $ret = true;
      }
    }

    return $ret;
  }

  /**
   * Store the element values that are translatable
   *
   * @param   int    $element_id
   * @param   array  $validation_array  [validation_type => string, invalid_msg => [lang_code => string], * => string]
   *
   * @return bool
   *
   * @since 1.0.0
   */
  protected function _storeValidations(int $element_id, array $validation_array): bool
  {
    $ret = false;
    if ($element_id > 0 && !empty($validation_array)) {
      //delete all first
      $container = Factory::getContainer();
      /** @var DatabaseDriver $db */
      $db         = $container->get('DatabaseDriver');
      $query      = $db->getQuery(true);
      $conditions = array(
        $db->quoteName('element_id') . ' = ' . $element_id
      );
      $query->delete($db->quoteName('#__formea_element_validations'));
      $query->where($conditions);
      $db->setQuery($query);
      $db->execute();
      $query->clear();
      $stat             = [];
      $totalValidations = count($validation_array);
      for ($i = 0; $i < $totalValidations; $i++) {
        $params          = [];
        $data            = $validation_array[$i];
        $obj             = new stdClass();
        $obj->element_id = $element_id;
        foreach ($data as $key => $data_val) {
          if ($key === 'validation_type') {
            $obj->validation_type = $data_val;
          } elseif ($key === 'invalid_msg') {
            $invalid_msg = [];
            foreach ($data_val as $lang_code_underscore => $msg) {
              $lang_code               = str_replace('_', '-', $lang_code_underscore);
              $invalid_msg[$lang_code] = $msg;
            }
            $obj->invalid_messages = json_encode($invalid_msg);
          } else {
            $params[$key] = $data_val;
          }
        }
        $obj->params = json_encode($params);
        $stat[]      = $db->insertObject('#__formea_element_validations', $obj);
      }
      if (!in_array(false, $stat)) {
        $ret = true;
      }
    }

    return $ret;
  }

  /**
   * Get the element type
   *
   * @param   string  $name  name of the element type
   *
   * @return mixed|null
   *
   * @since 1.0.0
   */
  public function getElementType($name)
  {
    if (empty($this->elementType)) {
      $container = Factory::getContainer();
      /** @var DatabaseDriver $db */
      $db    = $container->get('DatabaseDriver');
      $query = $db->getQuery(true);
      $query->select('*');
      $query->from($db->quoteName('#__formea_eltypes'));
      $query->where($db->quoteName('name') . ' = ' . $db->quote($name));
      $db->setQuery($query);
      $elementType = $db->loadObject();
      if (!empty($elementType)) {
        //get element config
        $conf     = (object) [];
        $filePath = $this->elementDir . 'element.json';
        if (is_file(Path::clean($filePath))) {
          $conf                = file_get_contents($filePath);
          $conf                = json_decode($conf);
          $elementType->config = $conf;
        }
      }
      $this->elementType = $elementType;
    }

    return $this->elementType;
  }
}
