<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Test\Cron\Job;

use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface;
use Drupal\languagewire_translation_provider\Client\ClientInterface;
use Drupal\languagewire_translation_provider\Client\LanguageWireClient;
use Drupal\languagewire_translation_provider\Client\LanguageWireClientFactory;
use Drupal\languagewire_translation_provider\Cron\Job\ProjectMonitoringJob;
use Drupal\languagewire_translation_provider\Database\ProjectRepositoryInterface;
use Drupal\languagewire_translation_provider\Domain\ProjectStatus;
use Drupal\languagewire_translation_provider\Service\ProjectMonitor;
use Drupal\languagewire_translation_provider\Test\Builder\Domain\ProjectBuilder;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Psr\Log\LoggerInterface;
use Throwable;

/**
 * Project Monitoring Job Test .
 */
class ProjectMonitoringJobTest extends TestCase {
  use \Prophecy\PhpUnit\ProphecyTrait;

  /**
   * System .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $system;

  /**
   * Project Repository .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $projectRepository;

  /**
   * Project Monitor .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $projectMonitor;

  /**
   * Logger .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $logger;

  /**
   * Client Factory .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $clientFactory;

  /**
   * Set Up .
   */
  protected function setUp(): void {
    $this->system = $this->prophesize(SystemInterface::class);
    $this->projectRepository = $this->prophesize(ProjectRepositoryInterface::class);
    $this->projectMonitor = $this->prophesize(ProjectMonitor::class);
    $this->logger = $this->prophesize(LoggerInterface::class);

    $this->system->logger()->willReturn($this->logger->reveal());

    $this->clientFactory = $this->prophesize(LanguageWireClientFactory::class);
    $this->clientFactory->createForTmgmtTranslator(Argument::type(TmgmtTranslatorInterface::class))->willReturn(
          $this->prophesize(LanguageWireClient::class)->reveal()
      );
  }

  /**
   * When Projects Are Finished In The Platform Then Project Is Saved .
   *
   * @test
   */
  public function whenProjectsAreFinishedInThePlatformThenProjectIsSaved(): void {
    $project = (new ProjectBuilder())->withStatus(ProjectStatus::inProgress())->build();
    $this->projectRepository->getAllByStatus(ProjectStatus::inProgress())->willReturn([$project]);
    $this->projectRepository->save($project)->willReturn($project);
    $this->projectMonitor
      ->monitorProject($project, Argument::type(ClientInterface::class))
      ->shouldBeCalled();
    $sut = $this->projectMonitoringJob();

    $sut->execute();

    $this->projectRepository->save($project)->shouldHaveBeenCalled();
    $this->logger->error(Argument::type('string'))->shouldNotHaveBeenCalled();
  }

  /**
   * When Project Monitor Throws Then Exception Is Logged .
   *
   * @test
   * @dataProvider throwableDataProvider
   */
  public function whenProjectMonitorThrowsThenExceptionIsLogged(Throwable $exception): void {
    $project = (new ProjectBuilder())->withStatus(ProjectStatus::inProgress())->build();
    $this->projectRepository->getAllByStatus(ProjectStatus::inProgress())->willReturn([$project]);
    $this->projectMonitor
      ->monitorProject($project, Argument::type(ClientInterface::class))
      ->willThrow($exception);
    $sut = $this->projectMonitoringJob();

    $sut->execute();

    $this->projectRepository->save($project)->shouldNotHaveBeenCalled();
    $this->logger->error(Argument::type('string'))->shouldHaveBeenCalled();
  }

  /**
   * Project Monitoring Job .
   */
  private function projectMonitoringJob(): ProjectMonitoringJob {
    return new ProjectMonitoringJob(
          $this->system->reveal(),
          $this->projectRepository->reveal(),
          $this->clientFactory->reveal(),
          $this->projectMonitor->reveal()
      );
  }

  /**
   * Throwable Data Provider .
   */
  public function throwableDataProvider(): array {
    return [
          [new \Exception()],
          [new \Error()],
    ];
  }

}
