<?php

declare(strict_types=1);

namespace Drupal\Tests\dkan_harvest\Unit;

use Contracts\Mock\Storage\MemoryFactory;
use Drupal\Component\DependencyInjection\Container;
use Drupal\Tests\dkan_common\Traits\ServiceCheckTrait;
use Drupal\dkan_harvest\Entity\HarvestPlanRepository;
use Drupal\dkan_harvest\Entity\HarvestRunRepository;
use Drupal\dkan_harvest\HarvestService;
use Drupal\dkan_harvest\Storage\HarvestHashesDatabaseTableFactory;
use Drupal\dkan_harvest\WebServiceApi;
use Drupal\dkan_metastore\MetastoreService;
use MockChain\Chain;
use MockChain\Options;
use PHPUnit\Framework\TestCase;
use Procrastinator\Result;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * @covers \Drupal\dkan_harvest\WebServiceApi
 * @coversDefaultClass \Drupal\dkan_harvest\WebServiceApi
 *
 * @group dkan
 * @group harvest
 * @group unit
 */
class WebServiceApiTest extends TestCase {
  use ServiceCheckTrait;

  private $request;

  /**
   * Getter.
   */
  public function getContainer() {
    // @todo Change the autogenerated stub.
    parent::setUp();

    $container = $this->getMockBuilder(ContainerInterface::class)
      ->onlyMethods(['get'])
      ->disableOriginalConstructor()
      ->getMockForAbstractClass();

    $container->method('get')
      ->with(
        $this->logicalOr(
          $this->equalTo('dkan.harvest.service'),
          $this->equalTo('dkan.harvest.logger_channel'),
          $this->equalTo('request_stack')
        )
      )
      ->will($this->returnCallback($this->containerGet(...)));

    return $container;
  }

  /**
   *
   */
  public function containerGet($input) {
    switch ($input) {
      case 'dkan.harvest.service':
        return new HarvestService(
          new MemoryFactory(),
          $this->createStub(HarvestHashesDatabaseTableFactory::class),
          $this->getMetastoreMockChain(),
          $this->getHarvestEntityRepositoryMock(),
          $this->createStub(HarvestRunRepository::class),
          $this->createStub(LoggerInterface::class)
        );

      case 'request_stack':
        $stack = $this->getMockBuilder(RequestStack::class)
          ->disableOriginalConstructor()
          ->onlyMethods(['getCurrentRequest'])
          ->getMock();

        $stack->method("getCurrentRequest")->willReturn($this->request);

        return $stack;
    }
  }

  /**
   *
   */
  public function testEmptyIndex() {
    $controller = WebServiceApi::create($this->getContainer());
    $response = $controller->index();
    $this->assertInstanceOf(JsonResponse::class, $response);
    $this->assertEquals($response->getContent(), json_encode([]));
  }

  /**
   *
   */
  public function testBadPlan() {
    $this->request = new Request();
    $controller = WebServiceApi::create($this->getContainer());
    $response = $controller->register($this->request);
    $this->assertInstanceOf(JsonResponse::class, $response);
    $this->assertEquals($response->getContent(), json_encode(["message" => "Harvest plan must be a php object."]));
  }

  /**
   *
   */
  public function testRegisterAndIndex() {
    $request = $this->getMockBuilder(Request::class)
      ->onlyMethods(['getContent'])
      ->disableOriginalConstructor()
      ->getMock();

    $plan = [
      'identifier' => 'test',
      'extract' => ['type' => "blah", "uri" => "http://blah"],
      'load' => ['type' => 'blah'],
    ];

    $request->method('getContent')->willReturn(json_encode($plan));

    $this->request = $request;

    $controller = WebServiceApi::create($this->getContainer());
    $response = $controller->register($request);
    $this->assertInstanceOf(JsonResponse::class, $response);
    $this->assertEquals($response->getContent(), json_encode(["identifier" => "test"]));

    $response = $controller->index();
    $this->assertInstanceOf(JsonResponse::class, $response);
  }

  /**
   *
   */
  public function testRun() {
    $options = (new Options())
      ->add("dkan.harvest.service", HarvestService::class)
      ->index(0);

    $this->checkService('dkan.harvest.service', 'harvest');

    // We are not using the logger but it is good to check.
    $this->checkService('dkan.harvest.logger_channel', 'harvest');

    $container = (new Chain($this))
      ->add(Container::class, "get", $options)
      ->add(HarvestService::class, "runHarvest", Result::class)
      ->getMock();

    $request = new Request([], [], [], [], [], [],
      json_encode((object) ['plan_id' => 'test'])
    );

    $controller = WebServiceApi::create($container);
    $response = $controller->run($request);
    $this->assertInstanceOf(JsonResponse::class, $response);
  }

  /**
   * Private.
   */
  private function getMetastoreMockChain() {
    return (new Chain($this))
      ->add(MetastoreService::class, 'publish', '1')
      ->getMock();
  }

  /**
   * Private.
   */
  private function getHarvestEntityRepositoryMock() {
    return (new Chain($this))
      ->add(HarvestPlanRepository::class, 'getAllHarvestPlanIds', [])
      ->add(HarvestPlanRepository::class, 'storePlanJson', 'test')
      ->getMock();
  }

}
