<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Test\Service;

use Drupal\languagewire_translation_provider\Adapter\Entity\EntityInterface;
use Drupal\languagewire_translation_provider\Adapter\Entity\EntityRepositoryInterface;
use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobItemInterface;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewGeneratorInterface;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewPackageCreator;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewSettingsChecker;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewTemporaryArtifactsCleaner;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\Result\HtmlPreviewDocumentResult;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\Result\HtmlPreviewResult;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\Result\HtmlPreviewZipPackage;
use Drupal\languagewire_translation_provider\Domain\Document;
use Drupal\languagewire_translation_provider\Service\HtmlPreviewService;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Log\LoggerInterface;

/**
 * Html Preview Service Test .
 */
class HtmlPreviewServiceTest extends TestCase {
  use \Prophecy\PhpUnit\ProphecyTrait;

  /**
   * Html Preview Generator .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|HtmlPreviewGeneratorInterface
   */
  private ObjectProphecy $generator;

  /**
   * Html Preview Package Creator .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|HtmlPreviewPackageCreator
   */
  private ObjectProphecy $packageCreator;

  /**
   * Html Preview Settings Checker .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|HtmlPreviewSettingsChecker
   */
  private ObjectProphecy $settingsChecker;

  /**
   * Html Preview Temporary Artifacts Cleaner .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|HtmlPreviewTemporaryArtifactsCleaner
   */
  private ObjectProphecy $temporaryArtifactsCleaner;

  /**
   * Entity Repository .
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|EntityRepositoryInterface
   */
  private ObjectProphecy $entityRepository;

  /**
   * 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->generator = $this->prophesize(HtmlPreviewGeneratorInterface::class);
    $this->packageCreator = $this->prophesize(HtmlPreviewPackageCreator::class);
    $this->settingsChecker = $this->prophesize(HtmlPreviewSettingsChecker::class);
    $this->temporaryArtifactsCleaner = $this->prophesize(HtmlPreviewTemporaryArtifactsCleaner::class);
    $this->entityRepository = $this->prophesize(EntityRepositoryInterface::class);
    $this->system = $this->prophesize(SystemInterface::class);
    $this->logger = $this->prophesize(LoggerInterface::class);
    $this->system->logger()->willReturn($this->logger->reveal());
  }

  /**
   * When Creating Html Preview Then An Html Preview Zip Package Is Returned .
   *
   * @test
   */
  public function whenCreatingHtmlPreviewThenAnHtmlPreviewZipPackageIsReturned(): void {
    [$doc, $entity] = $this->createValidEntityMock();
    $this->validPackageIsReturned($entity);
    $this->settingsAreValid();
    $sut = $this->htmlPreviewService();
    $results = $sut->createHtmlPreviews([$doc->reveal()], 1);
    $this->assertIsArray($results);
    $this->assertContainsOnly(HtmlPreviewDocumentResult::class, $results);
  }

  /**
   * When Settings Are Not Valid Then An Empty Result Is Returned .
   *
   * @test
   */
  public function whenSettingsAreNotValidThenAnEmptyResultIsReturned(): void {
    [$doc, $entity] = $this->createValidEntityMock();
    $this->settingsAreInValid();
    $sut = $this->htmlPreviewService();
    $results = $sut->createHtmlPreviews([$doc->reveal()], 1);
    $this->assertIsArray($results);
    $this->assertNotEmpty($entity);
    $this->assertCount(0, $results);
    $this->logger->warning(Argument::type("string"))->shouldHaveBeenCalled();
  }

  /**
   * When Generator Throws Exception Then Results Is Empty .
   *
   * @test
   */
  public function whenGeneratorThrowsExceptionThenResultsIsEmpty(): void {
    [$doc, $entity] = $this->createValidEntityMock();
    $this->exceptionIsThrownDuringGeneration($entity);
    $this->settingsAreValid();
    $sut = $this->htmlPreviewService();
    $results = $sut->createHtmlPreviews([$doc->reveal()], 1);
    $this->logger->error(Argument::type('string'))->shouldHaveBeenCalled();
    $this->assertIsArray($results);
    $this->assertCount(0, $results);
  }

  /**
   * Html Preview Service .
   */
  private function htmlPreviewService(): HtmlPreviewService {
    return new HtmlPreviewService($this->generator->reveal(), $this->packageCreator->reveal(), $this->settingsChecker->reveal(), $this->temporaryArtifactsCleaner->reveal(), $this->entityRepository->reveal(), $this->system->reveal());
  }

  /**
   * Html Preview Result .
   */
  private function htmlPreviewResult(): HtmlPreviewResult {
    return new HtmlPreviewResult('/tmp/folderPath', '/tmp/tempFolderPath');
  }

  /**
   * Valid Package Is Returned .
   */
  private function validPackageIsReturned($entity): void {
    $htmlPreviewResult = $this->htmlPreviewResult();
    $this->generator->generatePreviewForEntity(
      $entity,
      Argument::type(Document::class),
      Argument::type('int')
    )->willReturn($htmlPreviewResult);
    $this->packageCreator->createPreviewPackage($htmlPreviewResult)->willReturn(new HtmlPreviewZipPackage("path"));
    $this->temporaryArtifactsCleaner->removePreviewArtifacts($htmlPreviewResult)->shouldBeCalled();
  }

  /**
   * Settings Are Valid .
   */
  private function settingsAreValid(): void {
    $this->settingsChecker->canGenerateHtmlPreview()->willReturn(TRUE);
    $this->settingsChecker->getFailedRequirements()->willReturn([]);
  }

  /**
   * Settings Are In Valid .
   */
  private function settingsAreInValid(): void {
    $this->settingsChecker->canGenerateHtmlPreview()->willReturn(FALSE);
    $this->settingsChecker->getFailedRequirements()->willReturn(["error1", "error2"]);
  }

  /**
   * Exception Is Thrown During Generation .
   */
  private function exceptionIsThrownDuringGeneration($entity): void {
    $this->generator->generatePreviewForEntity(
      $entity,
      Argument::type(Document::class),
      Argument::type('int')
    )->willThrow(new \Exception());
  }

  /**
   * Create Valid Entity Mock .
   */
  private function createValidEntityMock(): array {
    $jobItem = $this->prophesize(TmgmtJobItemInterface::class);
    $jobItem->getWrappedObjectsEntityType()->willReturn("node");
    $jobItem->getWrappedObjectsEntityId()->willReturn("1");
    $doc = $this->prophesize(Document::class);
    $doc->getTmgmtItem()->willReturn($jobItem);
    $entityMock = $this->prophesize(EntityInterface::class);
    $entityMock->getEntityId()->willReturn("1");
    $entity = $entityMock->reveal();
    $this->entityRepository->getEntity("node", "1")->willReturn($entity);
    return [$doc, $entity];
  }

}
