<?php

namespace Civi\Core {

  /**
   * Class ResolverTest
   * @package Civi\Core
   */
  class ResolverTest extends \CiviUnitTestCase {

    /**
     * @var Resolver
     */
    protected $resolver;

    /**
     * Test setup.
     */
    protected function setUp(): void {
      // TODO: Change the autogenerated stub
      parent::setUp();
      $this->resolver = new Resolver();
    }

    /**
     * Test callback with a constant value.
     */
    public function testConstant(): void {
      $cb = $this->resolver->get('0');
      $actual = call_user_func($cb, 'foo');
      $this->assertTrue(0 === $actual);

      $cb = $this->resolver->get('1');
      $actual = call_user_func($cb, 'foo');
      $this->assertTrue(1 === $actual);
    }

    /**
     * Test callback for a global function.
     */
    public function testGlobalFunc(): void {
      // Note: civi_core_callback_dummy is implemented at the bottom of this file.
      $cb = $this->resolver->get('civi_core_callback_dummy');
      $this->assertEquals('civi_core_callback_dummy', $cb);

      $expected = 'global dummy received foo';
      $actual = call_user_func($cb, 'foo');
      $this->assertEquals($expected, $actual);
    }

    /**
     * Test callback for a static function.
     */
    public function testStatic(): void {
      $cb = $this->resolver->get('Civi\Core\ResolverTest::dummy');
      $this->assertEquals(['Civi\Core\ResolverTest', 'dummy'], $cb);

      $expected = 'static dummy received foo';
      $actual = call_user_func($cb, 'foo');
      $this->assertEquals($expected, $actual);
    }

    /**
     * Test callback for an API.
     */
    public function testApi3(): void {
      // Note: The Resolvertest.Ping API is implemented at the bottom of this file.
      $cb = $this->resolver->get('api3://Resolvertest/ping?first=@1');
      $expected = 'api dummy received foo';
      $actual = call_user_func($cb, 'foo');
      $this->assertEquals($expected, $actual);
    }

    /**
     * Test callback for an object in the container.
     */
    public function testCall(): void {
      // Note: ResolverTestExampleService is implemented at the bottom of this file.
      \Civi::container()->set('callbackTestService', new ResolverTestExampleService());
      $cb = $this->resolver->get('call://callbackTestService/ping');
      $expected = 'service dummy received foo';
      $actual = call_user_func($cb, 'foo');
      $this->assertEquals($expected, $actual);
    }

    /**
     * Test callback for an invalid object in the container.
     */
    public function testCallWithInvalidService(): void {
      $this->expectException(\Symfony\Component\DependencyInjection\Exception\ExceptionInterface::class);
      $this->resolver->get('call://totallyNonexistentService/ping');
    }

    /**
     * Test callback which returns a global variable.
     */
    public function testGlobalGetter(): void {
      $_GET['resolverTest'] = 123;
      $cb = $this->resolver->get('global://_GET/resolverTest?getter');
      $_GET['resolverTest'] = 456;
      $this->assertEquals(456, call_user_func($cb, 'side-effect-free'));
      $this->assertEquals(456, $_GET['resolverTest']);
      unset($_GET['resolverTest']);
    }

    public function testGlobalSetter(): void {
      $GLOBALS['resolverTest2'] = 78;
      $cb = $this->resolver->get('global://resolverTest2?setter');
      call_user_func($cb, 90);
      $this->assertEquals(90, $GLOBALS['resolverTest2']);
    }

    /**
     * Test object-lookup in the container.
     */
    public function testObj(): void {
      // Note: ResolverTestExampleService is implemented at the bottom of this file.
      \Civi::container()->set('callbackTestService', new ResolverTestExampleService());
      $obj = $this->resolver->get('obj://callbackTestService');
      $this->assertTrue($obj instanceof ResolverTestExampleService);
    }

    /**
     * Test object-lookup in the container (invalid name).
     */
    public function testObjWithInvalidService(): void {
      $this->expectException(\Symfony\Component\DependencyInjection\Exception\ExceptionInterface::class);
      $this->resolver->get('obj://totallyNonexistentService');
    }

    /**
     * Test default object creation.
     */
    public function testClass(): void {
      // Note: ResolverTestExampleService is implemented at the bottom of this file.
      $obj = $this->resolver->get('Civi\Core\ResolverTestExampleService');
      $this->assertTrue($obj instanceof ResolverTestExampleService);
    }

    /**
     * @param string $arg1
     *   Dummy value to pass through.
     * @return array
     */
    public static function dummy($arg1) {
      return "static dummy received $arg1";
    }

  }

  /**
   * Class ResolverTestExampleService
   *
   * @package Civi\Core
   */
  class ResolverTestExampleService {

    /**
     * @param string $arg1
     *   Dummy value to pass through.
     * @return string
     */
    public function ping($arg1) {
      return "service dummy received $arg1";
    }

  }
}

namespace {

  /**
   * @param string $arg1
   *   Dummy value to pass through.
   * @return string
   */
  function civi_core_callback_dummy($arg1) {
    return "global dummy received $arg1";
  }

  /**
   * @param array $params
   *   API parameters.
   * @return array
   */
  function civicrm_api3_resolvertest_ping($params) {
    return civicrm_api3_create_success("api dummy received " . $params['first']);
  }

}
