<?php

namespace Drupal\languagewire_translation_provider\Adapter\TMGMT;

use Drupal\languagewire_translation_provider\Settings\JobSettings;
use Drupal\languagewire_translation_provider\Settings\OrderType;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\JobItemInterface;

/**
 * TMGMT Job adapter.
* */
final class TmgmtJob implements TmgmtJobInterface {

  const TITLE_MAX_LENGTH = 99;

  /**
   * Job.
   *
   * @var \Drupal\tmgmt\JobInterface
   */
  private JobInterface $job;

  /**
   * Constructs a new TmgmtJob object.
   *
   * @param \Drupal\tmgmt\JobInterface $job
   *   Job.
   */
  public function __construct(JobInterface $job) {
    $this->job = $job;
  }

  /**
   * Settings.
   */
  public function settings(): JobSettings {
    $settings = $this->job->toArray()['settings'];
    $settings = empty($settings) ? [] : current($settings);

    return new JobSettings($settings);
  }

  /**
   * Translator.
   *
   * @throws \Drupal\tmgmt\TMGMTException
   */
  public function translator(): TmgmtTranslatorInterface {
    return new TmgmtTranslator($this->job->getTranslator());
  }

  /**
   * Word count.
   */
  public function wordCount(): int {
    return $this->job->getWordCount();
  }

  /**
   * Job ID.
   */
  public function jobId(): int {
    return $this->job->id();
  }

  /**
   * Job label.
   */
  public function title(): string {
    return $this->reduceTitleText($this->job->label());
  }

  /**
   * Source language.
   *
   * @throws \Drupal\tmgmt\TMGMTException
   */
  public function sourceLanguage(): LanguageMapping {
    return $this->languageMapping($this->job->getSourceLangcode());
  }

  /**
   * Target language.
   *
   * @throws \Drupal\tmgmt\TMGMTException
   */
  public function targetLanguage(): LanguageMapping {
    return $this->languageMapping($this->job->getTargetLangcode());
  }

  /**
   * Submit.
   *
   * @throws \Exception
   */
  public function submit(string $message): void {
    if (!$this->job->isUnprocessed()) {
      throw new \Exception('Only unprocessed jobs can be submitted.');
    }

    $this->job->submitted($message);
  }

  /**
   * Cancel.
   */
  public function cancel(string $message, array $context = []): void {
    $this->job->aborted($message, $context);
  }

  /**
   * Add message.
   */
  public function addMessage(string $message, array $context = []): void {
    $this->job->addMessage($message, $context);
  }

  /**
   * Has message.
   */
  public function hasMessage(string $message, string $searchOperator = '='): bool {
    return $this->countMessage($message, $searchOperator) > 0;
  }

  /**
   * Count message.
   */
  public function countMessage(string $message, string $searchOperator = '='): int {
    $conditions = ['message' => ['operator' => $searchOperator, 'value' => $message]];
    $foundMessages = $this->job->getMessages($conditions);

    return count($foundMessages);
  }

  /**
   * Make unprocessed.
   */
  public function makeUnprocessed(): void {
    $this->job->setState(JobInterface::STATE_UNPROCESSED);
  }

  /**
   * Make active.
   */
  public function makeActive(): void {
    $this->job->setState(JobInterface::STATE_ACTIVE);
  }

  /**
   * Make items active.
   */
  public function makeItemsActive(): void {
    foreach ($this->items() as $item) {
      $item->setAsActive();
    }
  }

  /**
   * Save.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function save(): void {
    $this->job->save();
  }

  /**
   * Item by ID.
   */
  public function itemById(int $itemId): ?TmgmtJobItemInterface {
    foreach ($this->items() as $item) {
      if ($item->itemId() == $itemId) {
        return $item;
      }
    }

    return NULL;
  }

  /**
   * Items.
   *
   * @return TmgmtJobItemInterface[]
   *   The job items.
   */
  public function items(): array {
    return array_map(
          function (JobItemInterface $tmgmtJobItem): TmgmtJobItemInterface {
              return new TmgmtJobItem($tmgmtJobItem);
          },
          $this->job->getItems()
      );
  }

  /**
   * Add translated data.
   *
   * @throws \Drupal\tmgmt\TMGMTException
   */
  public function applyTranslations(array $data): void {
    $dataFlatter = \Drupal::service('tmgmt.data');
    $structuredData = $dataFlatter->unflatten($data);
    $this->job->addTranslatedData($structuredData);
  }

  /**
   * Finish.
   */
  public function finish(string $message): void {
    $this->job->setState(JobInterface::STATE_FINISHED, $message);
  }

  /**
   * Get order type.
   */
  public function getOrderType(): string {
    return $this->settings()->getOrderType() ?? OrderType::ORDER;
  }

  /**
   * Get briefing.
   */
  public function getBriefing(): string {
    return $this->settings()->getBriefing() ?? '';
  }

  /**
   * Get service ID.
   */
  public function getServiceId(): ?int {
    return $this->settings()->getService();
  }

  /**
   * Get user ID.
   */
  public function getUserId(): ?int {
    return $this->settings()->getUser();
  }

  /**
   * Get project template platform ID.
   */
  public function getProjectTemplatePlatformId(): ?int {
    return $this->settings()->getProjectTemplate();
  }

  /**
   * Get Deadline.
   */
  public function getDeadline(): \DateTimeImmutable {
    return $this->settings()->getDeadlineAsDate()->toUtc();
  }

  /**
   * Get reduced title.
   */
  private function reduceTitleText(string $title): string {
    $maxLength = self::TITLE_MAX_LENGTH;

    if (strlen($title) > $maxLength) {
      $stringCut = substr($title, 0, $maxLength);
      $endPoint = strrpos($stringCut, ' ');

      $title = $endPoint ? substr($stringCut, 0, $endPoint) : substr($stringCut, 0);
    }

    return $title;
  }

  /**
   * Get language mapping.
   *
   * @throws \Drupal\tmgmt\TMGMTException
   */
  private function languageMapping(string $localLanguage): LanguageMapping {
    return new LanguageMapping(
          $localLanguage,
          $this->translator()->mapToRemoteLanguage($localLanguage)
      );
  }

}
