<?php
/**
 * @author Jaap Jansma <jaap.jansma@civicoop.org>
 * @license AGPL-3.0
 */

namespace Civi\DataProcessor\FilterHandler;

use Civi\DataProcessor\DataFlow\SqlDataFlow;
use Civi\DataProcessor\Exception\InvalidConfigurationException;
use CRM_ACL_API;
use CRM_ACL_BAO_ACL;
use CRM_Core_BAO_UFMatch;
use CRM_Core_Permission;
use CRM_Core_Session;
use CRM_Dataprocessor_ExtensionUtil as E;

class PermissionToViewContactFilter extends AbstractFieldFilterHandler {

  /**
   * Initialize the filter
   *
   * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException
   * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException
   * @throws \Civi\DataProcessor\Exception\FieldNotFoundException
   */
  protected function doInitialization() {
    if (!isset($this->configuration['datasource']) || !isset($this->configuration['field'])) {
      throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$this->title)));
    }
    $this->initializeField($this->configuration['datasource'], $this->configuration['field']);
  }

  /**
   * @param array $filter
   *   The filter settings
   * @return mixed
   */
  public function setFilter($filter) {
    $this->resetFilter();
    $dataFlow  = $this->dataSource->ensureField($this->inputFieldSpecification);
    $where = $this->getAclWhereClause($filter['value']);
    $contactTableAlias = 'civicrm_contact_'.$this->inputFieldSpecification->alias;
    $where = str_replace(["`contact_a`.", "contact_a."], "`{$contactTableAlias}`.", $where);
    if ($where) {
      $contactFilters[] = new SqlDataFlow\PureSqlStatementClause($where);
      if ($dataFlow && $dataFlow instanceof SqlDataFlow && $this->filterCollection->useWhere()) {
        $tableAlias = $this->getTableAlias($dataFlow);
        $this->whereClause = new SqlDataFlow\InTableWhereClause(
          'id',
          'civicrm_contact',
          $contactTableAlias,
          $contactFilters,
          $tableAlias,
          $this->inputFieldSpecification->getName(),
          'IN'
        );

        $this->filterCollection->addWhere($this->whereClause);
      }
    }
  }

  protected function getAclWhereClause($contactID = null) {
    $tables = array();
    $whereTables = array();
    if (!$contactID) {
      $contactID = CRM_Core_Session::getLoggedInContactID();
    }
    $contactID = (int) $contactID;
    $userId = CRM_Core_BAO_UFMatch::getUFId($contactID);
    // first see if the contact has edit / view all permission
    if ($userId && (CRM_Core_Permission::check('edit all contacts', $contactID) || CRM_Core_Permission::check('view all contacts', $contactID))) {
      return ' ( 1 ) ';
    }

    $whereClause = CRM_ACL_BAO_ACL::whereClause(CRM_ACL_API::VIEW, $tables, $whereTables, $contactID);
    $where = implode(' AND ', [$whereClause]);

    // Add permission on self if we really hate our server or have hardly any contacts.
    if ($userId && (CRM_Core_Permission::check('edit my contact', $contactID) || CRM_Core_Permission::check('view my contact', $contactID))) {
      $where = "(contact_a.id = $contactID OR ($where))";
    }
    return $where;
  }

  /**
   * Returns true when this filter has additional configuration
   *
   * @return bool
   */
  public function hasConfiguration() {
    return true;
  }

  /**
   * When this filter type has additional configuration you can add
   * the fields on the form with this function.
   *
   * @param \CRM_Core_Form $form
   * @param array $filter
   */
  public function buildConfigurationForm(\CRM_Core_Form $form, $filter=array()) {
    $fieldSelect = \CRM_Dataprocessor_Utils_DataSourceFields::getAvailableFilterFieldsInDataSources($filter['data_processor_id']);
    $form->add('select', 'contact_id_field', E::ts('Contact ID Field'), $fieldSelect, true, array(
      'style' => 'min-width:250px',
      'class' => 'crm-select2 huge data-processor-field-for-name',
      'placeholder' => E::ts('- select -'),
    ));

    if (isset($filter['configuration'])) {
      $configuration = $filter['configuration'];
      $defaults = array();
      if (isset($configuration['field']) && isset($configuration['datasource'])) {
        $defaults['contact_id_field'] = \CRM_Dataprocessor_Utils_DataSourceFields::getSelectedFieldValue($filter['data_processor_id'], $configuration['datasource'], $configuration['field']);
      }
      $form->setDefaults($defaults);
    }
  }

  /**
   * When this filter type has configuration specify the template file name
   * for the configuration form.
   *
   * @return false|string
   */
  public function getConfigurationTemplateFileName() {
    return "CRM/Dataprocessor/Form/Filter/Configuration/PermissionToViewContactFilter.tpl";
  }


  /**
   * Process the submitted values and create a configuration array
   *
   * @param $submittedValues
   * @return array
   */
  public function processConfiguration($submittedValues): array {
    [$datasource, $field] = explode('::', $submittedValues['contact_id_field'], 2);
    $configuration['field'] = $field;
    $configuration['datasource'] = $datasource;
    return $configuration;
  }

  /**
   * Get a list of possible operator options.
   *
   * @param \Civi\DataProcessor\DataSpecification\FieldSpecification $fieldSpec
   *
   * @return array
   */
  protected function getOperatorOptions(\Civi\DataProcessor\DataSpecification\FieldSpecification $fieldSpec) {
    return [
      '=' => E::ts('Is equal'),
    ];
  }


}
