<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Adapter\PreviewSite;

use Drupal\languagewire_translation_provider\Adapter\Entity\EntityInterface;
use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewGeneratorInterface;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\Result\HtmlPreviewResult;
use Drupal\languagewire_translation_provider\Domain\Document;
use Drupal\preview_site\Generate\GenerationInProgressException;
use Psr\Log\LoggerInterface;

/**
 * Preview site HTML preview generator.
 * */
final class PreviewSiteHtmlPreviewGenerator implements HtmlPreviewGeneratorInterface {

  /**
   * Preview site build factory.
   *
   * @var PreviewSiteBuildFactory
   */
  private PreviewSiteBuildFactory $previewSiteBuildFactory;

  /**
   * Preview site builder.
   *
   * @var PreviewSiteBuilder
   */
  private PreviewSiteBuilder $previewSiteBuilder;

  /**
   * Preview site build repository.
   *
   * @var PreviewSiteBuildRepositoryInterface
   */
  private PreviewSiteBuildRepositoryInterface $buildRepository;

  /**
   * Preview strategy repository.
   *
   * @var PreviewStrategyRepositoryInterface
   */
  private PreviewStrategyRepositoryInterface $strategyRepository;

  /**
   * Preview site cleanup action.
   *
   * @var PreviewSiteCleanupActionInterface
   */
  private PreviewSiteCleanupActionInterface $previewSiteCleanupAction;

  /**
   * System.
   *
   * @var \Drupal\languagewire_translation_provider\Adapter\SystemInterface
   */
  private SystemInterface $system;

  /**
   * Logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  private LoggerInterface $logger;

  /**
   * Constructs a new PreviewSiteHtmlPreviewGenerator object.
   *
   * @param PreviewSiteBuildFactory $previewSiteBuildFactory
   *   Preview site build factory.
   * @param PreviewSiteBuilder $previewSiteBuilder
   *   Preview site builder.
   * @param PreviewSiteBuildRepositoryInterface $buildRepository
   *   Preview site build repository.
   * @param PreviewStrategyRepositoryInterface $strategyRepository
   *   Preview strategy repository.
   * @param PreviewSiteCleanupActionInterface $previewSiteCleanupAction
   *   Preview site cleanup action.
   * @param \Drupal\languagewire_translation_provider\Adapter\SystemInterface $system
   *   System.
   */
  public function __construct(PreviewSiteBuildFactory $previewSiteBuildFactory, PreviewSiteBuilder $previewSiteBuilder, PreviewSiteBuildRepositoryInterface $buildRepository, PreviewStrategyRepositoryInterface $strategyRepository, PreviewSiteCleanupActionInterface $previewSiteCleanupAction, SystemInterface $system) {
    $this->previewSiteBuildFactory = $previewSiteBuildFactory;
    $this->previewSiteBuilder = $previewSiteBuilder;
    $this->buildRepository = $buildRepository;
    $this->strategyRepository = $strategyRepository;
    $this->previewSiteCleanupAction = $previewSiteCleanupAction;
    $this->system = $system;
    $this->logger = $system->logger();
  }

  /**
   * Generate preview for entity.
   */
  public function generatePreviewForEntity(EntityInterface $entity, Document $document, int $jobID): HtmlPreviewResult {
    $build = $this->previewSiteBuildFactory->createSiteBuild($entity, $document, $jobID);
    $result = $this->generatePreviewResult($build);
    return $this->previewSiteCleanupAction->cleanUpResult($result);
  }

  /**
   * Generate preview result.
   */
  private function generatePreviewResult(PreviewSiteBuild $build): HtmlPreviewResult {
    $this->logger->info(sprintf('Generating HTML preview for build %s', $build->getName()));

    try {
      $this->previewSiteBuilder->generate($build);
    } catch (GenerationInProgressException $e) {
      $this->logger->info(sprintf('HTML preview generation for build %s is already in progress', $build->getName()));
      // If the generation is already in progress, we can return the result
      // without further processing.
      $build = $this->buildRepository->getById($build->getId());

      if (!$build) {
        $this->logger->error(sprintf('Build %s not found after generation in progress exception', $build->getName()));
        throw new \RuntimeException(sprintf('Build %s not found', $build->getName()));
      }

      $finalPreviewFolderPath = $this->system->realPath($build->getFolderPath());
      $temporaryPreviewFolderPath = $this->system->realPath($build->getTemporaryArtifactsBasePath());

      if (!is_dir($finalPreviewFolderPath)) {
        $this->logger->error(sprintf('Final preview folder %s does not exist yet', $finalPreviewFolderPath));
        $this->strategyRepository->deleteById($build->getStrategy()->getId());
        $this->buildRepository->deleteById($build->getId());
        return $this->generatePreviewResult($build);
      }

      return new HtmlPreviewResult($finalPreviewFolderPath, $temporaryPreviewFolderPath);
    }

    $this->logger->info(sprintf('Deploying HTML preview for build %s', $build->getName()));
    $this->previewSiteBuilder->deploy($build);
    $build = $this->buildRepository->getById($build->getId());
    $finalPreviewFolderPath = $this->system->realPath($build->getFolderPath());
    $temporaryPreviewFolderPath = $this->system->realPath($build->getTemporaryArtifactsBasePath());
    $result = new HtmlPreviewResult($finalPreviewFolderPath, $temporaryPreviewFolderPath);
    $this->strategyRepository->deleteById($build->getStrategy()->getId());
    $this->buildRepository->deleteById($build->getId());
    return $result;
  }

}
