<?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\ProjectImportJob;
use Drupal\languagewire_translation_provider\Database\ProjectRepositoryInterface;
use Drupal\languagewire_translation_provider\Domain\ProjectStatus;
use Drupal\languagewire_translation_provider\Service\ProjectImporter;
use Drupal\languagewire_translation_provider\Test\Builder\Domain\ProjectBuilder;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Psr\Log\LoggerInterface;
use Throwable;

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

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

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

  /**
   * Project Importer .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $projectImporter;

  /**
   * 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->projectImporter = $this->prophesize(ProjectImporter::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 Translations Are Applied Successfully Then Project Is Saved .
   *
   * @test
   */
  public function whenProjectsTranslationsAreAppliedSuccessfullyThenProjectIsSaved(): void {
    $project = (new ProjectBuilder())->withStatus(ProjectStatus::importing())->build();
    $this->projectRepository->getAllByStatus(ProjectStatus::importing())->willReturn([$project]);
    $this->projectImporter->importProject($project, Argument::type(ClientInterface::class))->shouldBeCalled();
    $sut = $this->projectImportJob();
    $sut->execute();
    $this->projectRepository->save($project)->shouldHaveBeenCalled();
  }

  /**
   * When Project Import Throws Then Exception Is Logged .
   *
   * @test
   * @dataProvider throwableDataProvider
   */
  public function whenProjectImportThrowsThenExceptionIsLogged(Throwable $exception): void {
    $project = (new ProjectBuilder())->withStatus(ProjectStatus::importing())->build();
    $this->projectRepository->getAllByStatus(ProjectStatus::importing())->willReturn([$project]);
    $this->projectImporter->importProject($project, Argument::type(ClientInterface::class))->willThrow($exception);
    $sut = $this->projectImportJob();
    $sut->execute();
    $this->projectRepository->save($project)->shouldNotHaveBeenCalled();
    $this->logger->error(Argument::type('string'))->shouldHaveBeenCalled();
  }

  /**
   * Project Import Job .
   */
  private function projectImportJob(): ProjectImportJob {
    return new ProjectImportJob($this->system->reveal(), $this->projectRepository->reveal(), $this->clientFactory->reveal(), $this->projectImporter->reveal());
  }

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

}
