<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Domain;

use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobInterface;
use Drupal\languagewire_translation_provider\api\Models\Project\ProjectModel;
use Drupal\languagewire_translation_provider\Client\Project as PlatformProject;
use Drupal\languagewire_translation_provider\Content\Translation\Import\TranslationImportFallbackInterface;
use Drupal\languagewire_translation_provider\Platform\AvailableServices;
use Laminas\Diactoros\Uri;
use Psr\Http\Message\UriInterface;

/**
 * Project domain object.
 */
final class Project {

  /**
   * TMGMT job.
   *
   * @var \Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobInterface
   */
  private TmgmtJobInterface $tmgmtJob;

  /**
   * Correlation ID.
   *
   * @var string
   */
  private string $correlationId;

  /**
   * Status.
   *
   * @var ProjectStatus
   */
  private ProjectStatus $status;

  /**
   * Documents.
   *
   * @var Document[]
   */
  private array $documents;

  /**
   * ID.
   *
   * @var int|null
   */
  private ?int $id;

  /**
   * Platform ID.
   *
   * @var int
   */
  private int $platformId;

  /**
   * Platform URL.
   *
   * @var \Psr\Http\Message\UriInterface|null
   */
  private ?UriInterface $platformUrl;

  /**
   * Bundling ID.
   *
   * @var string|null
   */
  private ?string $bundlingId;

  /**
   * Constructs a new Project object.
   *
   * @param TmgmtJobInterface $tmgmtJob
   *   TMGMT job.
   * @param string $correlationId
   *   Correlation ID.
   * @param ProjectStatus $status
   *   Status.
   * @param Document[] $documents
   *   Documents.
   * @param int|null $id
   *   ID.
   * @param int $platformId
   *   Platform ID.
   * @param \Psr\Http\Message\UriInterface|null $platformUrl
   *   Platform URL.
   * @param string|null $bundlingId
   *   Bundling ID.
   */
  public function __construct(TmgmtJobInterface $tmgmtJob, string $correlationId, ProjectStatus $status, array $documents = [], ?int $id = NULL, int $platformId = 0, ?UriInterface $platformUrl = NULL, string $bundlingId = NULL) {
    $this->tmgmtJob = $tmgmtJob;
    $this->correlationId = $correlationId;
    $this->status = $status;
    $this->documents = $documents;
    $this->id = $id;
    $this->platformId = $platformId;
    $this->platformUrl = $platformUrl;
    $this->bundlingId = $bundlingId;
  }

  /**
   * Get TMGMT job.
   */
  public function getTmgmtJob(): TmgmtJobInterface {
    return $this->tmgmtJob;
  }

  /**
   * Get correlation ID.
   */
  public function getCorrelationId(): string {
    return $this->correlationId;
  }

  /**
   * Get title.
   */
  public function getTitle(): string {
    return $this->tmgmtJob->title();
  }

  /**
   * Get status.
   */
  public function getStatus(): ProjectStatus {
    return $this->status;
  }

  /**
   * Set status.
   */
  public function setStatus(ProjectStatus $newStatus): void {
    $this->status = $newStatus;
  }

  /**
   * Get documents.
   *
   * @return Document[]
   *   Documents.
   */
  public function getDocuments(): array {
    return $this->documents;
  }

  /**
   * Get documents by status.
   *
   * @param DocumentStatus $status
   *   Status.
   *
   * @return Document[]
   *   Documents.
   */
  public function getDocumentsByStatus(DocumentStatus $status): array {
    return array_values(array_filter($this->documents, function (Document $document) use ($status): bool {
      return $document->getStatus()->equals($status);
    }));
  }

  /**
   * Get document by platform ID.
   */
  public function getDocumentByPlatformId(string $documentPlatformId): ?Document {
    foreach ($this->documents as $document) {
      if ($document->getPlatformId() == $documentPlatformId) {
        return $document;
      }
    }

    return NULL;
  }

  /**
   * Get document by TMGMT item ID.
   */
  public function hasDocumentsInStatus(DocumentStatus $status): bool {
    return count($this->getDocumentsByStatus($status)) > 0;
  }

  /**
   * Set documents.
   *
   * @param Document[] $documents
   *   Documents.
   */
  public function setDocuments(array $documents): void {
    $this->documents = $documents;
  }

  /**
   * Update document.
   *
   * @param Document $document
   *   Document.
   */
  public function updateDocument(Document $document): void {
    $documents = $this->getDocuments();
    foreach ($documents as $key => $existingDocument) {
      if ($existingDocument->getId() === $document->getId()) {
        $documents[$key] = $document;
        break;
      }
    }

    $this->setDocuments($documents);
  }

  /**
   * Update all documents status.
   *
   * @param DocumentStatus $status
   *   Document status.
   */
  public function updateAllDocumentsStatus(DocumentStatus $status): void {
    $documents = $this->getDocuments();
    foreach ($documents as $document) {
      $document->setStatus($status);
    }

    $this->setDocuments($documents);
  }

  /**
   * Get ID.
   */
  public function getId(): ?int {
    return $this->id;
  }

  /**
   * Get platform ID.
   */
  public function getPlatformId(): int {
    return $this->platformId;
  }

  /**
   * Get platform URL.
   */
  public function getPlatformUrl(): ?UriInterface {
    return $this->platformUrl;
  }

  /**
   * Has platform URL.
   */
  public function hasPlatformUrl(): bool {
    return $this->platformUrl !== NULL && !empty($this->platformUrl->__toString());
  }

  /**
   * Get bundling ID.
   */
  public function getBundlingId(): ?string {
    return $this->bundlingId;
  }

  /**
   * Has bunglingId.
   */
  public function hasBundlingId(): bool {
    return !empty($this->bundlingId);
  }

  /**
   * Update with.
   */
  public function updateWith(PlatformProject $platformProject): void {
    if (!$this->platformId && $platformProject->id()) {
      $this->platformId = $platformProject->id();
    }

    if ($platformProject->platformUrl()) {
      $this->platformUrl = new Uri($platformProject->platformUrl());
    }
  }

  /**
   * Apply translations.
   *
   * @param \Drupal\languagewire_translation_provider\Platform\Document\TranslatedDocument[] $translatedDocuments
   *   Translated documents.
   * @param \Drupal\languagewire_translation_provider\Content\Translation\Import\TranslationImportFallbackInterface $translationImportFallback
   *   Translation import fallback.
   */
  public function applyTranslations(array $translatedDocuments, TranslationImportFallbackInterface $translationImportFallback): void {
    $data = [];
    foreach ($translatedDocuments as $document) {
      $data = array_merge($data, $document->toTmgmtImportData());
    }

    $documents = $this->getDocuments();
    foreach ($documents as $document) {
      $data = $translationImportFallback->applyTranslationFallback($data, $document, $this);
    }

    $this->tmgmtJob->applyTranslations($data);
  }

  /**
   * Is all translated.
   */
  public function isAllTranslated(): bool {
    $countFinished = count($this->getDocumentsByStatus(DocumentStatus::translated())) + count($this->getDocumentsByStatus(DocumentStatus::cancelled()));
    return $countFinished === count($this->getDocuments());
  }

  /**
   * Is all cancelled.
   */
  public function isAllCancelled(): bool {
    return count($this->getDocumentsByStatus(DocumentStatus::cancelled())) === count($this->getDocuments());
  }

  /**
   * Is all error.
   */
  public function isAllError(): bool {
    return count($this->getDocumentsByStatus(DocumentStatus::error())) === count($this->getDocuments());
  }

  /**
   * Get reference file IDs.
   */
  public function canBeReimported(): bool {
    return $this->getStatus()->equals(ProjectStatus::error())
            || $this->getStatus()->equals(ProjectStatus::finished());
  }

  /**
   * From platform project.
   *
   * @param \Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobInterface $job
   *   TMGMT job.
   * @param int $projectID
   *   Project ID.
   * @param \Drupal\languagewire_translation_provider\Domain\ProjectStatus $status
   *   Status.
   * @param \Drupal\languagewire_translation_provider\api\Models\Project\ProjectModel $platformProject
   *   Platform project.
   * @param string|null $bundleId
   *   Bundle ID.
   */
  public static function fromPlatformProject(TmgmtJobInterface $job, int $projectID, ProjectStatus $status, ProjectModel $platformProject, ?string $bundleId): self {
    return new self(
      $job,
      $platformProject->getExternalId(),
      $status,
      DocumentFactory::createDocumentsForTmgmtJobItems($job->items(), $projectID),
      $projectID,
      $platformProject->getId(),
      new Uri($platformProject->getPlatformLink()),
      $bundleId
    );
  }

}
