<?php

namespace Drupal\languagewire_translation_provider\Test\Service;

use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobItemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface;
use Drupal\languagewire_translation_provider\Client\ClientInterface;
use Drupal\languagewire_translation_provider\Client\RecoverableException;
use Drupal\languagewire_translation_provider\Domain\Document;
use Drupal\languagewire_translation_provider\Domain\DocumentStatus;
use Drupal\languagewire_translation_provider\Domain\Project;
use Drupal\languagewire_translation_provider\Domain\ProjectStatus;
use Drupal\languagewire_translation_provider\Platform\Document\ValidDocument;
use Drupal\languagewire_translation_provider\Service\ProjectCreator;
use Drupal\languagewire_translation_provider\Test\Builder\Domain\DocumentBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\Domain\ProjectBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\JobItemBuilder;
use Drupal\languagewire_translation_provider\Test\Builder\ValidDocumentBuilder;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Log\LoggerInterface;

/**
 * Project Creator Test .
 */
class ProjectCreatorTest extends TestCase {
  use \Prophecy\PhpUnit\ProphecyTrait;

  /**
   * Client .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|ClientInterface
   */
  private ObjectProphecy $client;

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

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

  /**
   * Set Up .
   */
  protected function setUp(): void {
    $this->client = $this->prophesize(ClientInterface::class);
    $this->system = $this->prophesize(SystemInterface::class);
    $this->logger = $this->prophesize(LoggerInterface::class);
    $this->system->logger()->willReturn($this->logger->reveal());
  }

  /**
   * When Project Has Valid Documents Then Project Is Created In The Platform .
   *
   * @test
   */
  public function whenProjectHasValidDocumentsThenProjectIsCreatedInThePlatform(): void {
    $job = $this->getSuccessfulTmgmtJobMock();
    [$project, $validDocs] = $this->createProject($job);
    $this->languageWireClientSuccessfullyCreatesTheProject($validDocs);

    $spc = $this->projectCreator();
    $this->client->submit($project)->willReturn($project);
    $spc->createProject($project, $this->client->reveal(), FALSE, TRUE);

    $this->assertEquals(ProjectStatus::uploading(), $project->getStatus());
    $job->addMessage('Translation project \'\' was successfully created in the LanguageWire platform.')->shouldHaveBeenCalled();
  }

  /**
   * When Client Throws Recoverable Error Then Project Is Not Updated .
   *
   * @test
   */
  /*public function whenClientThrowsRecoverableErrorThenProjectIsNotUpdated(): void {
    $job = $this->getSuccessfulTmgmtJobMock();
    [$project, $validDocs] = $this->createProject($job);

    $expectedException = new \Exception();
    $this->languageWireClientThrowsException($project, $expectedException);

    $spc = $this->projectCreator();
    $this->client->submit($project)->willReturn($project);
    $spc->createProject($project, $this->client->reveal(), FALSE, TRUE);

    //$this->logger->warning(Argument::type('string'))->shouldHaveBeenCalled();
    $this->assertEquals(ProjectStatus::uploading(), $project->getStatus());
  }*/

  /**
   * When Client Throws An Exception Then Project Status Is Set To Error .
   *
   * @test
   */
  /*public function whenClientThrowsAnExceptionThenProjectStatusIsSetToError(): void {
    $job = $this->getSuccessfulTmgmtJobMock();
    [$project, $validDocs] = $this->createProject($job);

    $expectedException = new \Exception();
    $this->languageWireClientThrowsException($project, $expectedException);
    $this->client->submit($project)->willReturn($project);
    $job->title()->willReturn('title');
    $job->jobId()->willReturn(123);
    //$job->cancel(Argument::containingString("Project creation has failed. Failure reason:"))->shouldHaveBeenCalled();
    //$job->save()->shouldHaveBeenCalled();

    $spc = $this->projectCreator();
    $spc->createProject($project, $this->client->reveal(), FALSE, TRUE);

    $this->assertEquals(ProjectStatus::uploading(), $project->getStatus());
  }*/

  /**
   * New Job Item .
   */
  private function newJobItem(int $itemId, string $label): TmgmtJobItemInterface {
    return (new JobItemBuilder())->withItemId($itemId)->withLabel($label)->build();
  }

  /**
   * Valid Document .
   */
  private function validDocument(Document $document): ValidDocument {
    return (new ValidDocumentBuilder())->withDocument($document)->build();
  }

  /**
   * Get Successful Tmgmt Job Mock .
   */
  private function getSuccessfulTmgmtJobMock(): ObjectProphecy {
    $job = $this->prophesize(TmgmtJobInterface::class);
    $job->jobId()->willReturn(1);
    $job->title()->willReturn("Job title");

    return $job;
  }

  /**
   * Language Wire Client Successfully Creates The Project .
   */
  private function languageWireClientSuccessfullyCreatesTheProject(
    array $validDocs
  ): void {
    $this->client->submit(Argument::that(function (Project $project) use ($validDocs): bool {
        return count($validDocs) == count($project->getDocuments());
    }))->shouldBeCalledOnce();
  }

  /**
   * Language Wire Client Throws Exception .
   */
  private function languageWireClientThrowsException(
    Project $project,
    \Exception $exception
  ): void {
    $this->client->submit($project)->willThrow($exception);
  }

  /**
   * Project Creator .
   */
  private function projectCreator(): ProjectCreator {
    return new ProjectCreator(
          $this->logger->reveal()
      );
  }

  /**
   * Create Project .
   *
   * @param \Prophecy\Prophecy\ObjectProphecy $job
   *   Job .
   *
   * @return array
   *   Project .
   */
  private function createProject(ObjectProphecy $job): array {
    $item1 = $this->newJobItem(1, 'Item 1');
    $item2 = $this->newJobItem(2, 'Item 2');
    $document1 = (new DocumentBuilder())->withTmgmtJobItem($item1)->withStatus(DocumentStatus::uploadingContent())->build();
    $document2 = (new DocumentBuilder())->withTmgmtJobItem($item2)->withStatus(DocumentStatus::uploadingContent())->build();
    $project = (new ProjectBuilder())->withTmgmtJob($job->reveal())->withStatus(ProjectStatus::uploading())->withDocuments($document1, $document2)->build();

    // Mocks of validated documents in the platform.
    $validDocs = [
      $this->validDocument($document1),
      $this->validDocument($document2),
    ];
    return [$project, $validDocs];
  }

}
