<?php

namespace Drupal\languagewire_translation_provider\Test\Client;

use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslator;
use Drupal\languagewire_translation_provider\Client\InvalidSourceLanguageException;
use Drupal\languagewire_translation_provider\Client\ProjectSpecificationFactory;
use Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface;
use Drupal\languagewire_translation_provider\Domain\Project;
use Drupal\languagewire_translation_provider\Domain\ProjectTemplate;
use Drupal\languagewire_translation_provider\Platform\AvailableServices;
use Drupal\languagewire_translation_provider\Platform\Services\ServiceInterface;
use Drupal\languagewire_translation_provider\Settings\JobSettings;
use Drupal\languagewire_translation_provider\Test\Builder\Domain\ProjectBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\Domain\ProjectTemplateBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\LanguageMappingBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\ServiceBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\TmgmtJobBuilder;
use Exception;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;

/**
 * Project Specification Factory Test .
 */
class ProjectSpecificationFactoryTest extends TestCase {
  use \Prophecy\PhpUnit\ProphecyTrait;

  /**
   * Available Services .
   *
   * @var ObjectProphecy
   */
  protected $availableServices;

  /**
   * Project Template Repository .
   *
   * @var ObjectProphecy
   */
  protected $projectTemplateRepository;

  /**
   * Set Up .
   */
  protected function setUp(): void {
    $this->availableServices = $this->prophesize(AvailableServices::class);
    $this->projectTemplateRepository = $this->prophesize(ProjectTemplateRepositoryInterface::class);
  }

  /**
   * When Supplied With Project And Available Services .
   *
   * @test
   */
  public function whenSuppliedWithDomainProjectObjectAndAvailableServicesThenItCreatesProjectSpecification(): void {
    $translator = $this->prophesize(TmgmtTranslator::class)->reveal();
    $project = (new ProjectBuilder())->build();
    $service = (new ServiceBuilder())->build();

    $this->availableServices->getById($project->getTmgmtJob()->getServiceId())->willReturn($service);
    $this->projectTemplateRepository->getByPlatformId($project->getTmgmtJob()->getProjectTemplatePlatformId(), $translator)->willReturn(NULL);
    $sut = $this->projectSpecificationFactory();

    $result = $sut->createSpecification($project, $this->availableServices->reveal());

    $this->assertSame($project->getTitle(), $result->title());
    $this->assertSame($project->getCorrelationId(), $result->projectUuid());
  }

  /**
   * When Supplied With Project And Available Services .
   *
   * @test
   */
  public function whenSuppliedWithDomainProjectObjectAndAvailableServicesThenServiceIsConfigured(): void {
    $project = (new ProjectBuilder())->build();

    $service = $this->prophesize(ServiceInterface::class);

    $this->availableServices->getById($project->getTmgmtJob()->getServiceId())->willReturn($service->reveal());
    $this->projectTemplateRepository->getByPlatformId($project->getTmgmtJob()->getProjectTemplatePlatformId())->willReturn(NULL);
    $sut = $this->projectSpecificationFactory();

    $sut->createSpecification($project, $this->availableServices->reveal());

    $service->configure(Argument::type(JobSettings::class))->shouldHaveBeenCalled();
  }

  /**
   * When Supplied With Project With Template .
   *
   * @test
   */
  public function whenSuppliedWithDomainProjectObjectWithTemplateAndAvailableTemplatesThenItCreatesProjectWithTemplateSpecification(): void {
    $projectTemplateId = 22;
    $tmgmtJob = (new TmgmtJobBuilder())->withProjectTemplateId($projectTemplateId)->build();
    $project = (new ProjectBuilder())->withTmgmtJob($tmgmtJob)->build();

    $projectTemplate = (new ProjectTemplateBuilder())->build();

    $this->availableServices->getById($project->getTmgmtJob()->getServiceId())->willReturn($this->prophesize(ServiceInterface::class)->reveal());
    $this->projectTemplateRepositoryReturnsTemplate($project, $projectTemplate);
    $sut = $this->projectSpecificationFactory();

    $result = $sut->createSpecification($project, $this->availableServices->reveal());

    $this->assertSame($project->getTitle(), $result->title());
    $this->assertSame($project->getCorrelationId(), $result->projectUuid());
  }

  /**
   * When Supplied With Project With Template .
   *
   * @test
   */
  public function whenSuppliedWithDomainProjectObjectWithTemplateAndDifferentSourceLanguageThenExceptionIsThrown(): void {
    $projectTemplateId = 22;
    $jobSourceLanguage = (new LanguageMappingBuilder())->withLocalLanguage('es')->withRemoteLanguage('es-ES')->build();
    $templateSourceLanguage = (new LanguageMappingBuilder())->withLocalLanguage('en')->withRemoteLanguage('en-US')->build();
    $tmgmtJob = (new TmgmtJobBuilder())->withProjectTemplateId($projectTemplateId)->withSourceLanguage($jobSourceLanguage)->build();
    $project = (new ProjectBuilder())->withTmgmtJob($tmgmtJob)->build();

    $projectTemplate = (new ProjectTemplateBuilder())->withSourceLanguage($templateSourceLanguage->remoteLanguage())->build();

    $this->availableServices->getById($project->getTmgmtJob()->getServiceId())->willReturn(NULL);
    $this->projectTemplateRepositoryReturnsTemplate($project, $projectTemplate);
    $sut = $this->projectSpecificationFactory();

    $this->expectException(Exception::class);

    $sut->createSpecification($project, $this->availableServices->reveal());
  }

  /**
   * When Supplied Project With Null Template .
   *
   * @test
   */
  public function whenSuppliedWithDomainProjectObjectWithNullTemplateThenExceptionIsThrown(): void {
    $projectTemplateId = 22;
    $tmgmtJob = (new TmgmtJobBuilder())->withProjectTemplateId($projectTemplateId)->build();
    $project = (new ProjectBuilder())->withTmgmtJob($tmgmtJob)->build();

    $this->availableServices->getById($project->getTmgmtJob()->getServiceId())->willReturn(NULL);
    $this->projectTemplateRepositoryReturnsTemplate($project, NULL);
    $sut = $this->projectSpecificationFactory();

    $this->expectException(Exception::class);

    $sut->createSpecification($project, $this->availableServices->reveal());
  }

  /**
   * Project Specification Factory .
   */
  public function projectSpecificationFactory(): ProjectSpecificationFactory {
    return new ProjectSpecificationFactory($this->projectTemplateRepository->reveal());
  }

  /**
   * Project Template Repository Returns Template .
   */
  public function projectTemplateRepositoryReturnsTemplate(Project $project, ?ProjectTemplate $projectTemplate): void {
    $this->projectTemplateRepository->getByPlatformId(
          $project->getTmgmtJob()->getProjectTemplatePlatformId(),
          $project->getTmgmtJob()->translator()
      )->willReturn($projectTemplate);
  }

}
