<?php
/**
 * Copyright (C) 2023  Jaap Jansma (jaap.jansma@civicoop.org)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

namespace Civi\DataProcessor\FilterHandler;

use Civi\DataProcessor\DataSpecification\FieldSpecification;
use CRM_Core_Exception;
use CRM_Core_Form;
use CRM_Dataprocessor_ExtensionUtil as E;
use CRM_Utils_Type;

class ContactHasNumberOfActivitiesInPeriodFilter extends ContactHasActivityInPeriodFilter {

  protected function getOperatorOptions(FieldSpecification $fieldSpec): array {
    return array(
      'IN' => E::ts('Has number of activities in period'),
      'NOT IN' => E::ts('Does not have this number of activities in period'),
    );
  }

  /**
   * Add addition filter fields to the API specs.
   *
   * @param array $specs
   *
   * @return array
   */
  public function enhanceApi3FieldSpec(array $specs): array {
    $specs = parent::enhanceApi3FieldSpec($specs);
    $fieldSpec = $this->getFieldSpecification();
    $alias = $fieldSpec->alias;
    $specs["{$alias}_min"] = [
      'name' => "{$alias}_min",
      'title' => $fieldSpec->title .': '.E::ts('Min number of activities'),
      'api.filter' => TRUE,
      'api.return' => FALSE,
      'type' => CRM_Utils_Type::T_INT,
    ];
    $specs["{$alias}_max"] = [
      'name' => "{$alias}_max",
      'title' => $fieldSpec->title .': '.E::ts('Max number of activities'),
      'api.filter' => TRUE,
      'api.return' => FALSE,
      'type' => CRM_Utils_Type::T_INT,
    ];
    return $specs;
  }

  /**
   * Add the elements to the filter form.
   *
   * @param \CRM_Core_Form $form
   * @param array $defaultFilterValue
   * @param string $size
   *   Possible values: full or compact
   * @return array
   *   Return variables belonging to this filter.
   */
  public function addToFilterForm(CRM_Core_Form $form, $defaultFilterValue, $size='full'): array {
    $filter = parent::addToFilterForm($form, $defaultFilterValue, $size);
    $fieldSpec = $this->getFieldSpecification();
    $alias = $fieldSpec->alias;

    try {
      $form->add('text', "{$alias}_min", E::ts('Min'), [
        'class' => 'four',
      ]);
      $form->add('text', "{$alias}_max", E::ts('Max'), [
        'class' => 'four',
      ]);
    } catch (CRM_Core_Exception $e) {
    }

    $defaults = [];
    if (isset($defaultFilterValue['min'])) {
      $defaults[$alias.'_min'] = $defaultFilterValue['min'];
    }
    if (isset($defaultFilterValue['max'])) {
      $defaults[$alias.'_max'] = $defaultFilterValue['max'];
    }
    if (count($defaults)) {
      $form->setDefaults($defaults);
    }
    return $filter;
  }

  /**
   * File name of the template to add this filter to the criteria form.
   *
   * @return string
   */
  public function getTemplateFileName(): string {
    return "CRM/Dataprocessor/Form/Filter/ContactHasNumberOfActivitiesInPeriodFilter.tpl";
  }

  /**
   * Process the submitted values to a filter value
   * Which could then be processed by applyFilter function
   *
   * @param $submittedValues
   * @return array
   */
  public function processSubmittedValues($submittedValues): array {
    $return = parent::processSubmittedValues($submittedValues);
    $filterSpec = $this->getFieldSpecification();
    $alias = $filterSpec->alias;
    if (isset($submittedValues[$alias.'_min'])) {
      $return['min'] = $submittedValues[$alias . '_min'];
    }
    if (isset($submittedValues[$alias.'_max'])) {
      $return['max'] = $submittedValues[$alias . '_max'];
    }
    return $return;
  }

  /**
   * Extend the filter params from the submitted values.
   *
   * @param array $submittedValues
   * @param array $filterParams
   *
   * @return array
   */
  public function extendFilterParamsFromSubmittedValues(array $submittedValues, array $filterParams): array {
    $filterParams = parent::extendFilterParamsFromSubmittedValues($submittedValues, $filterParams);
    if (isset($submittedValues['min']) && is_numeric($submittedValues['min']) && strlen($submittedValues['min'])) {
      $filterParams['min'] = $submittedValues['min'];
    }
    if (isset($submittedValues['max']) && is_numeric($submittedValues['max']) && strlen($submittedValues['max'])) {
      $filterParams['max'] = $submittedValues['max'];
    }
    return $filterParams;
  }

  /**
   * Returns the subquery statement.
   *
   * This function can be overridden in child classes to alter the subquery.
   *
   * @param string $select
   * @param string $from
   * @param string $where
   * @param array $filterParams
   *
   * @return string
   */
  protected function getSubQueryStatement(string $select, string $from, string $where, array $filterParams): string {
    $groupBy = "GROUP BY `contact_id`";
    $havingClauses = [];
    $having = "";

    $fieldAlias = $this->inputFieldSpecification->alias;
    if (isset($filterParams['min']) && strlen($filterParams['min']) && is_numeric($filterParams['min'])) {
      try {
        $havingClauses[] = "COUNT(`activity_$fieldAlias`.`id`) >= " . CRM_Utils_Type::escape($filterParams['min'], "Positive");
      } catch (CRM_Core_Exception $e) {
      }
    }
    if (isset($filterParams['max']) && strlen($filterParams['max']) && is_numeric($filterParams['max'])) {
      try {
        $havingClauses[] = "COUNT(`activity_$fieldAlias`.`id`) <= " . CRM_Utils_Type::escape($filterParams['max'], "Positive");
      } catch (CRM_Core_Exception $e) {
      }
    }
    if (count($havingClauses)) {
      $having = "HAVING " . implode(" AND ", $havingClauses);
    }

    return trim(trim($select) . " " . trim($from) . " " . trim($where) . " ".$groupBy . " " . $having);
  }

}
