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

namespace Civi\DataProcessor\Source\Contribution;

use Civi\DataProcessor\DataFlow\SqlTableDataFlow;
use Civi\DataProcessor\DataSpecification\AggregateFunctionFieldSpecification;
use Civi\DataProcessor\DataSpecification\DataSpecification;
use Civi\DataProcessor\DataSpecification\FieldExistsException;
use Civi\DataProcessor\DataSpecification\FieldSpecification;
use Civi\DataProcessor\Source\AbstractCivicrmEntitySource;

use CRM_Dataprocessor_ExtensionUtil as E;

class AggregatedContributionSource extends AbstractCivicrmEntitySource {


  /**
   * Returns the entity name
   *
   * @return String
   */
  protected function getEntity() {
    return 'Contribution';
  }

  /**
   * Returns the table name of this entity
   *
   * @return String
   */
  protected function getTable() {
    return 'civicrm_contribution';
  }

  /**
   * Returns the default configuration for this data source
   *
   * @return array
   */
  public function getDefaultConfiguration() {
    return array(
      'filter' => array(
        'is_test' => array (
          'op' => '=',
          'value' => '0',
        )
      )
    );
  }

  /**
   * Returns an array with possible aggregate functions.
   * Return false when aggregation is not possible.
   *
   * This function could be overridden in child classes.
   *
   * @return array|false
   */
  protected function getPossibleAggregateFunctions() {
    return [
      'sum_total_amount' => E::ts('Sum Total Amount'),
      'max_receive_date' => E::ts('Last one by receive date'),
      'min_receive_date' => E::ts('First one by receive date'),
      'max_id' => E::ts('Last one by contribution id'),
      'min_id' => E::ts('First one by contribution id'),
    ];
  }


  /**
   * @return \Civi\DataProcessor\DataSpecification\DataSpecification
   * @throws \Civi\DataProcessor\DataSpecification\FieldExistsException
   */
  public function getAvailableFields() {
    if (!$this->availableFields) {
      parent::getAvailableFields();
      $id = new FieldSpecification('id', 'Integer', E::ts('Contribution ID'), null, $this->getSourceName().'_id');
      $this->availableFields->addFieldSpecification('id', $id);
      $totalAmount = new FieldSpecification('total_amount', 'Float', E::ts('Total amount'), null, $this->getSourceName().'_total_amount');
      $this->availableFields->addFieldSpecification('total_amount', $totalAmount);
      $receiveDate = new FieldSpecification('receive_date', 'Date', E::ts('Receive Date'), null, $this->getSourceName().'_receive_date');
      $this->availableFields->addFieldSpecification('receive_date', $receiveDate);
      $contactId = new FieldSpecification('contact_id', 'Float', E::ts('Contact ID'), null, $this->getSourceName().'_contact_id');
      $this->availableFields->addFieldSpecification('contact_id', $contactId);
      $count = new FieldSpecification('count', 'Integer', E::ts('Count'), null, $this->getSourceName().'_count');
      $this->availableFields->addFieldSpecification('count', $count);
    }
    return $this->availableFields;
  }

  /**
   * Adds an inidvidual filter to the data source
   *
   * @param $filter_field_alias
   * @param $op
   * @param $values
   *
   * @throws \Exception
   */
  protected function addFilter($filter_field_alias, $op, $values) {
    $spec = NULL;
    if ($this->getAvailableFilterFields()->doesAliasExists($filter_field_alias)) {
      $spec = $this->getAvailableFilterFields()
        ->getFieldSpecificationByAlias($filter_field_alias);
    }
    elseif ($this->getAvailableFilterFields()->doesFieldExist($filter_field_alias)) {
      $spec = $this->getAvailableFilterFields()
        ->getFieldSpecificationByName($filter_field_alias);
    }
    if ($spec) {
      $this->addFilterToAggregationDataFlow($spec, $op, $values);
    }
  }

  /**
   * @param \Civi\DataProcessor\DataSpecification\FieldSpecification $fieldSpecification
   *
   * @return void
   * @throws \Exception
   */
  public function ensureFieldInSource(FieldSpecification $fieldSpecification) {
    parent::ensureFieldInSource($fieldSpecification);
    if ($this->isAggregationEnabled()) {
      try {
        $originalFieldSpecification = null;
        if ($this->getAvailableFields()->doesAliasExists($fieldSpecification->alias)) {
          $originalFieldSpecification = $this->getAvailableFields()->getFieldSpecificationByAlias($fieldSpecification->alias);
        } elseif ($this->getAvailableFields()->doesFieldExist($fieldSpecification->name)) {
          $originalFieldSpecification = $this->getAvailableFields()->getFieldSpecificationByName($fieldSpecification->name);
        }
        if ($originalFieldSpecification) {
          $originalFieldSpecification = clone $originalFieldSpecification;
          $originalFieldSpecification->alias = $originalFieldSpecification->name;
          $this->aggretated_table_dataflow->getDataSpecification()->addFieldSpecification($originalFieldSpecification->alias, $originalFieldSpecification);
        }
      } catch (FieldExistsException $e) {
        // Do nothing.
      }
    }
    if ($fieldSpecification->name == 'count' && $this->isAggregationEnabled()) {
      $this->ensureEntity();
      $countField = new FieldSpecification('id', 'Integer', E::ts('Count'), null, 'count');
      $countField->setMySqlFunction('COUNT');
      $this->aggretated_table_dataflow->getDataSpecification()->addFieldSpecification('count', $countField);
    }
    if ($fieldSpecification->name == 'id' && $this->isAggregationEnabled()) {
      $this->ensureEntity();
      $idField = new FieldSpecification('id', 'Integer', E::ts('Contribution Id'), null, 'id');
      $idField->setMySqlFunction('MAX');
      $this->aggretated_table_dataflow->getDataSpecification()->addFieldSpecification('id', $idField);
    }
  }


}
