<?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\Libraries;

defined('_JEXEC') or die;

use Feseur\Library\FsrDate;
use Feseur\Library\FsrFieldQuery;
use Feseur\Library\FsrHelper;
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseDriver;
use Joomla\Registry\Registry;

class FormeaSubmissions
{
  /**
   * @var array
   * @since 1.0.0
   */
  public $submissions;

  /**
   * @var FormeaCore[]
   * @since 1.0.3
   */
  protected $coreClasses = [];

  protected $formObject;

  public function __construct($config = [])
  {
    if (isset($config['coreClasses'])) {
      $this->coreClasses = $config['coreClasses'];
    } else {
      $this->coreClasses = [new FormeaCore()];
    }

    if (isset($config['formObject'])) {
      $this->formObject = $config['formObject'];
    }
  }

  /**
   * @param   boolean          $isCount
   * @param   array            $db_limit
   * @param   array            $ordering
   * @param   FsrFieldQuery[]  $whereClause
   *
   *
   * @since 1.0.0
   */
  public function get($isCount = false, $fieldType = ['*'], $db_limit = [], $ordering = ['a.id' => 'DESC'], $whereClause = [])
  {
    if ($isCount) {
      $ret = $this->_totalItems($whereClause);
    } else {
      $ret = $this->_getSubmissions($fieldType, $db_limit, $ordering, $whereClause);
    }

    return $ret;
  }

  /**
   * @param   string[]         $fieldType
   * @param                    $db_limit
   * @param                    $ordering
   * @param   FsrFieldQuery[]  $whereClause
   *
   *
   * @since version
   */
  protected function _getSubmissions($fieldType = ['*'], $db_limit = [], $ordering = ['a.id' => 'DESC'], array $whereClause = [])
  {
    $ret    = [];
    $limit  = 5;
    $offset = 0;
    if (!empty($db_limit)) {
      if (isset($db_limit['limit'])) {
        $limit = (int) $db_limit['limit'];
      }
      if (isset($db_limit['offset'])) {
        $offset = (int) $db_limit['offset'];
      }
    }
    $container = Factory::getContainer();
    /** @var DatabaseDriver $db */
    $db    = $container->get('DatabaseDriver');
    $query = $db->getQuery(true);
    $query->select(['a.*', 'b.title AS form_title', 'c.name AS user_name']);
    $query->from($db->quoteName('#__formea_submissions', 'a'))
      ->innerJoin(
        $db->quoteName('#__formea_forms', 'b') . ' ON ' . $db->quoteName('a.form_id') . ' = ' .
          $db->quoteName('b.id')
      )->leftJoin(
        $db->quoteName('#__users', 'c') . ' ON ' . $db->quoteName('a.user_id') . ' = ' .
          $db->quoteName('c.id')
      );
    if (!empty($whereClause)) {
      foreach ($whereClause as $clause) {
        $v = $db->quote($clause->value);
        if ($clause->value[0] == '(' && $clause->value[strlen($clause->value) - 1] == ')') {
          $v = $clause->value;
        }
        $query->where(
          $db->quoteName($clause->field) . ' ' .
            $clause->operator . ' ' .
            $v
        );
      }
    } else {
      $query->where($db->quoteName('a.state') . ' = 1');
    }
    if ($limit > 0) {
      $query->setLimit($limit, $offset);
    }
    foreach ($ordering as $column => $direction) {
      $query->order($db->quoteName($column) . ' ' . $direction);
    }
    $db->setQuery($query);
    $this->submissions = $db->loadObjectList();

    return $this->submissions;
  }


  /**
   * Get the total row in DB
   *
   * @param   FsrFieldQuery[]  $whereClause
   *
   * @return int
   *
   * @since 1.0.0
   */
  protected function _totalItems($whereClause = [])
  {
    $container = Factory::getContainer();
    /** @var DatabaseDriver $db */
    $db    = $container->get('DatabaseDriver');
    $query = $db->getQuery(true);
    $query->select('COUNT(*)');
    $query->from($db->quoteName('#__formea_submissions'));
    if (!empty($whereClause)) {
      foreach ($whereClause as $clause) {
        $v = $db->quote($clause->value);
        if ($clause->value[0] == '(' && $clause->value[strlen($clause->value) - 1] == ')') {
          $v = $clause->value;
        }
        $query->where(
          $db->quoteName($clause->field) . ' ' .
            $clause->operator . ' ' .
            $v
        );
      }
    }
    $db->setQuery($query);

    return (int) $db->loadResult();
  }

  public function getStats($fromDate, $toDate, $formIds = [])
  {
    $container = Factory::getContainer();
    /** @var DatabaseDriver $db */
    $db    = $container->get('DatabaseDriver');
    $query = $db->getQuery(true);
    $query->select(['a.*']);
    $query->from($db->quoteName('#__formea_submissions', 'a'));
    $query->where($db->quoteName('a.created_date') . ' <= ' . $db->quote($toDate));
    $query->where($db->quoteName('a.created_date') . ' >= ' . $db->quote($fromDate));
    if (!empty($formIds)) {
      $query->where($db->quoteName('a.form_id') . ' IN (' . implode(',', $formIds) . ')');
    }
    $query->order('a.created_date ASC');
    $db->setQuery($query);

    return $db->loadObjectList();
  }

  public function getStatsByMonths($fromDate, $toDate, $formIds = [])
  {
    $allMonthsArr = [];
    $container    = Factory::getContainer();
    /** @var DatabaseDriver $db */
    $db    = $container->get('DatabaseDriver');
    $query = $db->getQuery(true);
    //Don't work on MySQL 5.7
    //$query->select(['a.*', 'COUNT(*) OVER(PARTITION BY a.form_id, YEAR(a.created_date), MONTH(a.created_date)) AS counts', 'b.title AS form_title']);
    $query->select(['a.*', '(SELECT COUNT(*) FROM #__formea_submissions AS sub WHERE sub.form_id = a.form_id AND YEAR(sub.created_date) = YEAR(a.created_date) AND MONTH(sub.created_date) = MONTH(a.created_date)) AS counts', 'b.title AS form_title']);
    $query->from($db->quoteName('#__formea_submissions', 'a'))->innerJoin(
      $db->quoteName('#__formea_forms', 'b') . ' ON ' .
        $db->quoteName('a.form_id') . ' = ' . $db->quoteName('b.id')
    );
    $query->where($db->quoteName('a.created_date') . ' <= ' . $db->quote($toDate));
    $query->where($db->quoteName('a.created_date') . ' >= ' . $db->quote($fromDate));
    if (!empty($formIds)) {
      $query->where($db->quoteName('a.form_id') . ' IN (' . implode(',', $formIds) . ')');
    }
    $query->order('a.created_date');
    $db->setQuery($query);
    $results = $db->loadObjectList();
    $ret     = [];
    if (!empty($results)) {
      $formatVal = 'Y-m';
      $allMonths = FsrHelper::getMonthsInRange($fromDate, $toDate, $formatVal);

      foreach ($allMonths as $months) {
        $allMonthsArr[$months] = 0;
      }
      $totalResults = count($results);
      for ($i = 0; $i < $totalResults; $i++) {
        $form_id          = $results[$i]->form_id;
        $createdDateMonth = (new FsrDate($results[$i]->created_date))->format($formatVal);
        if (!isset($ret[$form_id])) {
          $ret[$form_id]['form_id']    = $form_id;
          $ret[$form_id]['form_title'] = $results[$i]->form_title;
          $ret[$form_id]['data']       = $allMonthsArr;
        }
        if (isset($ret[$form_id]['data'][$createdDateMonth])) {
          $ret[$form_id]['data'][$createdDateMonth] = $results[$i]->counts;
        }
      }
    }
    if (!empty($ret)) {
      $ret = array_values($ret);
    }

    return [
      'datum'  => $ret,
      'months' => $allMonthsArr
    ];
  }

  /**
   * @param $_elements
   * @param $data
   * @param $uniqueString
   *
   * @return FormeaSubmissionElement[]
   *
   * @since 1.0.5
   */
  public function prepareForValidations($_elements, $data, $uniqueString)
  {
    $elements      = [];
    $totalElements = count($_elements);

    for ($j = 0; $j < $totalElements; $j++) {
      $submissionElementConfig = [
        'form_id'           => $this->formObject->id,
        'form_alias'        => $this->formObject->alias,
        'submitted_value'   => '',
        'setIndex'          => -1,
        'found'             => false,
        'link_path'         => [],
        'dir_path'          => [],
        'element_error'     => false,
        'element_error_msg' => '',
        'byPassValidation'  => false,
      ];
      if ($_elements[$j]->group_id > 0) {
        //group
        if ($_elements[$j]->group_state != 1) {
          $submissionElementConfig['byPassValidation'] = true;
        }
      } else {
        if ($_elements[$j]->state != 1) {
          $submissionElementConfig['byPassValidation'] = true;
        }
      }

      $_key                    = $_elements[$j]->element_id . '_' . $_elements[$j]->group_id . '_';

      $submissionElementConfig = array_merge($submissionElementConfig, (array) $_elements[$j]);
      $formeaSubmissionElement = new FormeaSubmissionElement($submissionElementConfig);
      $elements[$_key]         = $formeaSubmissionElement;
    }

    //assign data
    $assignedData = $this->assignElementToGroupData($data, $elements, $uniqueString);
    $assignedElements    = $assignedData['elements'];
    $assignedElementsIdx = $assignedData['idx'];
    $totalIdx            = count($assignedElementsIdx);

    $filteredArray = [];
    foreach ($elements as $index => $item) {
      if (!in_array($index, $assignedElementsIdx)) {
        // Add the item to the filtered array if the index is not in $item_index
        $filteredArray[$index] = $item;
      }
    }

    for ($i = 0; $i < $totalIdx; $i++) {
      unset($elements[$assignedElementsIdx[$i]]);
    }
    $elements         = array_values($elements);
    $assignedElements = array_merge($assignedElements, $elements);

    $coreClasses      = $this->coreClasses;
    $coreClasses      = array_values($coreClasses);
    $totalCoreClasses = count($coreClasses);
    for ($k = 0; $k < $totalCoreClasses; $k++) {
      $assignedElements = $coreClasses[$k]->checkElementBeforeSubmissions($assignedElements);
    }

    return $assignedElements;
  }

  private function assignElementToGroupData($datas, $elements, $uniquename)
  {

    $ret    = [];
    $retIdx = [];
    foreach ($datas as $key => $data) {
      if ($key === 'group_' . $uniquename) {
        foreach ($data as $group_id => $sets) {
          $totalSets = count($sets);
          for ($i = 0; $i < $totalSets; $i++) {
            $__elements = null;
            foreach ($sets[$i] as $alias => $value) {

              $_elements = $this->getElementByGroupIdAndAlias($elements, $group_id, $alias);

              foreach ($_elements as $idx => $_data) {
                $retIdx[$idx] = $idx;
                $__elements = array_merge([], (array)$_data);
                $__elements = new FormeaSubmissionElement($__elements);
              }
              if (!empty($__elements)) {
                $__elements->setIndex        = $i;
                $__elements->submitted_value = $value;
                $__elements->found           = true;
                $ret[]                         = $__elements;
              }
            }
          }
        }
      } else {
        //single field
        //$_elements = $this->getElementByAlias($elements, $key);
        $_elements = $this->getElementByGroupIdAndAlias($elements, 0, $key);
        foreach ($_elements as $idx => $_data) {
          $retIdx[$idx] = $idx;
          break;
        }
        $_elements = array_values($_elements);
        if (!empty($_elements)) {
          $_elements[0]->setIndex        = -1;
          $_elements[0]->submitted_value = $data;
          $_elements[0]->found           = true;
          $ret[]                         = $_elements[0];
        }
      }
    }

    return [
      'elements' => $ret,
      'idx'      => array_values($retIdx)
    ];
  }

  /**
   * @param $elements
   * @param $targetGroupId
   * @param $targetName
   *
   * @return array
   *
   * @since version
   */
  private function getElementByGroupIdAndAlias($elements, $targetGroupId, $targetName)
  {
    $filteredObjects = array_filter($elements, function ($object) use ($targetGroupId, $targetName) {
      return $object->group_id === $targetGroupId && $object->alias === $targetName;
    });

    return $filteredObjects;
  }

  private function getElementByAlias($elements, $targetName)
  {
    $filteredObjects = array_filter($elements, function ($object) use ($targetName) {
      return $object->alias === $targetName;
    });

    return $filteredObjects;
  }
}
