<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Test\Database\Mapper;

use Drupal\languagewire_translation_provider\Adapter\TMGMT\JobItemRepositoryInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobItemInterface;
use Drupal\languagewire_translation_provider\Database\Mapper\DocumentMapper;
use Drupal\languagewire_translation_provider\Domain\Document;
use Drupal\languagewire_translation_provider\Domain\DocumentStatus;
use Drupal\languagewire_translation_provider\Test\Builder\JobItemBuilder;
use PHPUnit\Framework\TestCase;

/**
 * Document Mapper Test .
 */
class DocumentMapperTest extends TestCase {
  use \Prophecy\PhpUnit\ProphecyTrait;

  /**
   * Tmgmt Job Repository .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy
   */
  private $tmgmtJobRepository;

  /**
   * Set Up .
   */
  protected function setUp(): void {
    $this->tmgmtJobRepository = $this->prophesize(JobItemRepositoryInterface::class);
  }

  /**
   * When Database Entity Has Properties Not Null Then Maps It To Entity .
   *
   * @test
   */
  public function whenRawDatabaseEntityHasAllPropertiesNotNullThenMapsItToDocumentDomainEntity(): void {
    $tmgmtJobItemId = 999;
    $tmgmtJobItem = $this->jobRepositoryReturnsItemById($tmgmtJobItemId);
    $documentStatus = 'a status';
    $errors = ['error1', 'error2'];
    $dbEntity = [
      'did' => 1,
      'project_id' => '345',
      'tmgmt_item_id' => $tmgmtJobItemId,
      'status' => $documentStatus,
      'platform_id' => '9999',
      'errors' => \json_encode($errors),
    ];
    $sut = $this->documentMapper();
    $result = $sut->toDocument($dbEntity);
    $this->assertSame($tmgmtJobItem, $result->getTmgmtItem());
    $this->assertEquals(new DocumentStatus($documentStatus), $result->getStatus());
    $this->assertSame(1, $result->getId());
    $this->assertSame(345, $result->getProjectId());
    $this->assertSame('9999', $result->getPlatformId());
    $this->assertSame($errors, $result->getErrors());
  }

  /**
   * When Database Entity Has Optional Values As Null Then Maps To Entity .
   *
   * @test
   */
  public function whenRawDatabaseEntityHasOptionalValuesAsNullThenMapsItToDocumentDomainEntity(): void {
    $tmgmtJobItemId = 999;
    $tmgmtJobItem = $this->jobRepositoryReturnsItemById($tmgmtJobItemId);
    $documentStatus = 'a status';
    $dbEntity = [
      'did' => 1,
      'project_id' => '345',
      'tmgmt_item_id' => $tmgmtJobItemId,
      'status' => $documentStatus,
    ];
    $sut = $this->documentMapper();
    $result = $sut->toDocument($dbEntity);
    $this->assertSame($tmgmtJobItem, $result->getTmgmtItem());
    $this->assertEquals(new DocumentStatus($documentStatus), $result->getStatus());
    $this->assertSame(1, $result->getId());
    $this->assertSame(345, $result->getProjectId());
    $this->assertNull($result->getPlatformId());
    $this->assertEmpty($result->getErrors());
  }

  /**
   * When Job Repository Returns Null Then Maps To Null .
   *
   * @test
   */
  public function whenJobRepositoryReturnsNullThenMapsToNull(): void {
    $tmgmtJobItemId = 999;
    $this->jobRepositoryReturnsNullItem($tmgmtJobItemId);
    $documentStatus = 'a status';
    $dbEntity = [
      'did' => 1,
      'project_id' => '345',
      'tmgmt_item_id' => $tmgmtJobItemId,
      'status' => $documentStatus,
    ];
    $sut = $this->documentMapper();
    $result = $sut->toDocument($dbEntity);
    $this->assertNull($result);
  }

  /**
   * When Entity Has All Properties Not Null Then Maps To Database .
   *
   * @test
   */
  public function whenDocumentDomainEntityHasAllPropertiesNotNullThenMapsItToRawDbData(): void {
    $tmgmtJobItemId = 999;
    $tmgmtJobItem = $this->jobRepositoryReturnsItemById($tmgmtJobItemId);
    $document = new Document(
          $projectId = 1,
          $tmgmtJobItem, DocumentStatus::uploadingContent(),
          $id = 10,
          $platformId = '100',
          $errors = ['error1', 'error2']
      );
    $sut = $this->documentMapper();
    $result = $sut->toDatabaseEntity($document);
    $this->assertSame($projectId, $result['project_id']);
    $this->assertSame($tmgmtJobItemId, $result['tmgmt_item_id']);
    $this->assertSame(DocumentStatus::UPLOADING_CONTENT, $result['status']);
    $this->assertSame($platformId, $result['platform_id']);
    $this->assertSame('["error1","error2"]', $result['errors']);
  }

  /**
   * When Entity Has Optional Properties As Null Then Maps To Database .
   *
   * @test
   */
  public function whenDocumentDomainEntityHasOptionalPropertiesAsNullThenMapsItToRawDbData(): void {
    $tmgmtJobItemId = 999;
    $tmgmtJobItem = $this->jobRepositoryReturnsItemById($tmgmtJobItemId);
    $document = new Document($projectId = 1, $tmgmtJobItem, DocumentStatus::uploadingContent());
    $sut = $this->documentMapper();
    $result = $sut->toDatabaseEntity($document);
    $this->assertSame($projectId, $result['project_id']);
    $this->assertSame($tmgmtJobItemId, $result['tmgmt_item_id']);
    $this->assertSame(DocumentStatus::UPLOADING_CONTENT, $result['status']);
    $this->assertNull($result['platform_id']);
    $this->assertSame('[]', $result['errors']);
  }

  /**
   * Document Mapper .
   */
  private function documentMapper(): DocumentMapper {
    return new DocumentMapper($this->tmgmtJobRepository->reveal());
  }

  /**
   * Job Repository Returns Item By Id .
   */
  private function jobRepositoryReturnsItemById(int $tmgmtJobItemId): TmgmtJobItemInterface {
    $tmgmtJobItem = (new JobItemBuilder())->withItemId($tmgmtJobItemId)->build();
    $this->tmgmtJobRepository->getById($tmgmtJobItemId)->willReturn($tmgmtJobItem);
    return $tmgmtJobItem;
  }

  /**
   * Job Repository Returns Null Item .
   */
  private function jobRepositoryReturnsNullItem(int $tmgmtJobItemId): void {
    $this->tmgmtJobRepository->getById($tmgmtJobItemId)->willReturn(NULL);
  }

}
