<?php

require_once(__DIR__.'./../../UnitTestActionProvider.php');

use CRM_FormProcessor_ExtensionUtil as E;
use Civi\Test\HeadlessInterface;
use Civi\Test\HookInterface;
use Civi\Test\TransactionalInterface;

/**
 * This test tests whether the api is not accessible for people who dont have the right
 * to access the api. All FormProcessor api's are only accessible with the permission 'administer CiviCRM'.
 *
 * As this test does api calls it somehow also show when an api breaks.
 *
 * Tips:
 *  - With HookInterface, you may implement CiviCRM hooks directly in the test class.
 *    Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar).
 *  - With TransactionalInterface, any data changes made by setUp() or test****() functions will
 *    rollback automatically -- as long as you don't manipulate schema or truncate tables.
 *    If this test needs to manipulate schema or truncate tables, then either:
 *       a. Do all that using setupHeadless() and Civi\Test.
 *       b. Disable TransactionalInterface, and handle all setup/teardown yourself.
 *
 * @group headless
 */
class Api_v3_ApiPermissionTest extends \PHPUnit_Framework_TestCase implements HeadlessInterface, TransactionalInterface {

  protected $formProcessorId;

  protected $formProcessorInputId;

  protected $formProcessorInputValidationId;

  protected $formProcessorActionId;

  public function setUpHeadless() {
    // Civi\Test has many helpers, like install(), uninstall(), sql(), and sqlFile().
    // See: https://github.com/civicrm/org.civicrm.testapalooza/blob/master/civi-test.md
    return \Civi\Test::headless()
      ->installMe(__DIR__)
      ->apply();
  }

  public function setUp() {
    parent::setUp();

    $config = \CRM_Core_Config::singleton();
    $config->userPermissionClass = new \CRM_Core_Permission_UnitTests();
    $config->userPermissionClass->permissions = array();

    $formProcessor = new \CRM_FormProcessor_BAO_FormProcessorInstance();
    $formProcessor->name = 'name';
    $formProcessor->title = 'Title';
    $formProcessor->save();
    $this->formProcessorId = $formProcessor->id;

    $formProcessorInput = new \CRM_FormProcessor_BAO_FormProcessorInput();
    $formProcessorInput->name = 'contact_id';
    $formProcessorInput->type = 'Integer';
    $formProcessorInput->form_processor_instance_id = $this->formProcessorId;
    $formProcessorInput->save();
    $this->formProcessorInputId = $formProcessorInput->id;

    $formProcessorInputValidation = new \CRM_FormProcessor_BAO_FormProcessorInputValidation();
    $formProcessorInputValidation->validator = 'email';
    $formProcessorInputValidation->form_processor_input_id = $this->formProcessorInputId;
    $formProcessorInputValidation->save();
    $this->formProcessorInputValidationId = $formProcessorInputValidation->id;

    $formProcessorAction = new \CRM_FormProcessor_BAO_FormProcessorAction();
    $formProcessorAction->title = 'contact_id';
    $formProcessorAction->type = 'AddToGroup';
    $formProcessorAction->form_processor_instance_id = $this->formProcessorId;
    $formProcessorAction->weight = 0;
    $formProcessorAction->save();
    $this->formProcessorActionId = $formProcessorAction->id;
  }

  public function tearDown() {
    parent::tearDown();
  }

  /**
   * First check whether the permission fails when the api is accessed by
   * a user with access CiviCRM permission. After that test whether the api
   * call succeeds with an administer CiviCRM permission.
   *
   * @param string
   * @param string
   * @param array
   * @param array
   * @param array
   * @dataProvider apiCalls
   */
  public function testApiPermissions($entity, $action, $params, $id_params, $expected) {
    $civi_container = \Civi::container();
    $civi_container->set('action_provider', new \UnitTestActionProviderContainer());

    foreach($id_params as $key => $prop) {
      $params[$key] = $this->$prop;
    }
    $params['version'] = 3;
    $params['check_permissions'] = 1;
    \CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access CiviCRM');
    $result = civicrm_api($entity, $action, $params);
    $this->assertArrayHasKey('error_code', $result);
    $this->assertEquals('unauthorized', $result['error_code']);

    \CRM_Core_Config::singleton()->userPermissionClass->permissions = array('administer CiviCRM');
    $result = civicrm_api($entity, $action, $params);
    if (isset($result['is_error']) && !empty($result['is_error'])) {
      var_dump($result);
    }
    foreach($expected as $key => $expected_value) {
      $this->assertArrayHasKey($key, $result);
      $this->assertEquals($expected_value, $result[$key]);
    }

  }

  public function apiCalls() {
    return array(
      'FormProcessorInstance.Get' => array(
        'entity' => 'FormProcessorInstance',
        'action' => 'get',
        'params' => array(),
        'id_params' => array('id' => 'formProcessorId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorInstance.Create' => array(
        'entity' => 'FormProcessorInstance',
        'action' => 'create',
        'params' => array(
          'title' => 'Test',
          'name' => 'test',
        ),
        'id_params' => array('id' => 'formProcessorId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorInstance.Delete' => array(
        'entity' => 'FormProcessorInstance',
        'action' => 'delete',
        'params' => array(
        ),
        'id_params' => array('id' => 'formProcessorId'),
        'expected' => array('count' => 0, 'is_error' => 0),
      ),

      // Test for the input api
      'FormProcessorInput.Get' => array(
        'entity' => 'FormProcessorInput',
        'action' => 'get',
        'params' => array(),
        'id_params' => array('id' => 'formProcessorInputId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorInput.Create' => array(
        'entity' => 'FormProcessorInput',
        'action' => 'create',
        'params' => array(
          'name' => 'test',
          'configuration' => array(),
        ),
        'id_params' => array('id' => 'formProcessorInputId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorInput.Delete' => array(
        'entity' => 'FormProcessorInput',
        'action' => 'delete',
        'params' => array(
        ),
        'id_params' => array('id' => 'formProcessorInputId'),
        'expected' => array('count' => 0, 'is_error' => 0),
      ),

      // Test for the input validation api
      'FormProcessorInputValidation.Get' => array(
        'entity' => 'FormProcessorInputValidation',
        'action' => 'get',
        'params' => array(),
        'id_params' => array('id' => 'formProcessorInputValidationId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorInputValidation.Create' => array(
        'entity' => 'FormProcessorInputValidation',
        'action' => 'create',
        'params' => array(
          'validator' => 'email',
          'configuration' => array(),
        ),
        'id_params' => array('id' => 'formProcessorInputValidationId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorInputValidation.Delete' => array(
        'entity' => 'FormProcessorInputValidation',
        'action' => 'delete',
        'params' => array(
        ),
        'id_params' => array('id' => 'formProcessorInputValidationId'),
        'expected' => array('count' => 0, 'is_error' => 0),
      ),

      // Test for the action api.
      'FormProcessorAction.Get' => array(
        'entity' => 'FormProcessorAction',
        'action' => 'get',
        'params' => array(),
        'id_params' => array('id' => 'formProcessorActionId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorAction.Create' => array(
        'entity' => 'FormProcessorAction',
        'action' => 'create',
        'params' => array(
          'name' => 'test',
          'configuration' => array(),
          'mapping' => array(),
        ),
        'id_params' => array('id' => 'formProcessorActionId'),
        'expected' => array('count' => 1),
      ),
      'FormProcessorAction.Delete' => array(
        'entity' => 'FormProcessorAction',
        'action' => 'delete',
        'params' => array(
        ),
        'id_params' => array('id' => 'formProcessorActionId'),
        'expected' => array('count' => 0, 'is_error' => 0),
      ),
    );
  }

}