<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\api\Managers;

use Drupal\languagewire_translation_provider\api\Core\Settings;
use Drupal\languagewire_translation_provider\api\Endpoints\Project\CancelProject;
use Drupal\languagewire_translation_provider\api\Endpoints\Project\CreateProject;
use Drupal\languagewire_translation_provider\api\Endpoints\Project\Project as ProjectEndpoint;
use Drupal\languagewire_translation_provider\api\Endpoints\Project\StartProject;
use Drupal\languagewire_translation_provider\api\Endpoints\Project\StartQuoteProject;
use Drupal\languagewire_translation_provider\api\Models\Files\FileModel;
use Drupal\languagewire_translation_provider\api\Models\Project\ProjectModel;
use Drupal\languagewire_translation_provider\api\Models\Project\ProjectStatusModel;
use Drupal\languagewire_translation_provider\api\Models\TranslationModel;

/**
 * Project manager.
 */
class ProjectManager extends AbstractManager {

  /**
   * Project model.
   *
   * @var \Drupal\languagewire_translation_provider\api\Models\Project\ProjectModel
   */
  private ProjectModel $projectModel;

  /**
   * File manager.
   *
   * @var \Drupal\languagewire_translation_provider\api\Managers\FileManager
   */
  private FileManager $fileManager;

  public function __construct(Settings $settings) {
    parent::__construct($settings);

    $this->fileManager = new FileManager($settings, $this);
  }

  /**
   * Create project.
   */
  public function createProject(ProjectModel $projectModel): bool {
    $this->projectModel = $projectModel;

    $createProject = new CreateProject($this->settings);

    $createProject->createProject(
          $projectModel->getTitle(),
          $projectModel->getBriefing(),
          $projectModel->getBriefingForExperts(),
          $projectModel->getPurchaseOrderNumber(),
          $projectModel->getDeadline(),
          $projectModel->getTemplateId(),
          $projectModel->getServiceId(),
          $projectModel->getTranslationMemoryId(),
          $projectModel->getTermBaseId(),
          $projectModel->getInvoicingAccountId(),
          $projectModel->getUserId(),
          $projectModel->getWorkAreaId(),
          $projectModel->getExternalId()
      );

    $creationResult = $createProject->execute();

    if ($creationResult === NULL) {
      return FALSE;
    }

    $this->updateProjectModel($creationResult);

    return TRUE;
  }

  /**
   * Get file manager.
   */
  public function getFileManager(): FileManager {
    return $this->fileManager;
  }

  /**
   * Get project.
   */
  public function getProject(): ?ProjectModel {
    return $this->projectModel ?? NULL;
  }

  /**
   * Get project by ID.
   */
  public function getProjectById(int $projectID, bool $updateProjectModel = TRUE): ?ProjectModel {
    $project = new ProjectEndpoint($this->settings);
    $project->setProjectId($projectID);
    $projectData = $project->execute();

    if ($projectData && $updateProjectModel) {
      $this->updateProjectModel($projectData);
    }

    return $projectData ? $this->getProject() : NULL;
  }

  /**
   * Get platform project.
   */
  public function getPlatformProject(): bool {
    if (!$this->isProjectCreated()) {
      return FALSE;
    }

    $updateProject = new ProjectEndpoint($this->settings);
    $updateProject->setProjectId($this->getProject()->getId());
    $projectData = $updateProject->execute();

    if ($projectData === NULL) {
      return FALSE;
    }

    $this->updateProjectModel($projectData);

    return TRUE;
  }

  /**
   * Start project.
   */
  public function startProject(): bool {
    if (!$this->isProjectCreated()) {
      return FALSE;
    }

    $startProject = new StartProject($this->settings);
    $startProject->setProjectId($this->getProject()->getId());

    return $startProject->execute() !== NULL;
  }

  /**
   * Start quote project.
   */
  public function startQuoteProject(): bool {
    if (!$this->isProjectCreated()) {
      return FALSE;
    }

    $startQuoteProject = new StartQuoteProject($this->settings);
    $startQuoteProject->setProjectId($this->getProject()->getId());

    return $startQuoteProject->execute() !== NULL;
  }

  /**
   * Cancel project.
   */
  public function cancelProject(): bool {
    if (!$this->isProjectCreated()) {
      return FALSE;
    }

    $cancelProject = new CancelProject($this->settings);
    $cancelProject->setProjectId($this->getProject()->getId());

    return $cancelProject->execute() !== NULL;
  }

  /**
   * Get project model.
   */
  public static function toProjectModel(array $projectData): ProjectModel {
    $projectModel = new ProjectModel();
    $projectModel->setFields($projectData);

    return $projectModel;
  }

  /**
   * Update project model.
   */
  private function isProjectCreated(): bool {
    $project = $this->getProject();

    return $project !== NULL && $project->isCreated();
  }

  /**
   * Update project model.
   */
  private function updateProjectModel(array $projectData): void {
    if (is_string($projectData['status'])) {
      $projectData['status'] = ProjectStatusModel::initialize($projectData['status']);
    }

    $projectModel = $this->getProject() ?? new ProjectModel();

    $projectModel->setFields($projectData);

    $this->projectModel = $projectModel;

    $this->updateProjectFiles($projectData);
    $this->updateProjectReferenceFiles($projectData);
  }

  /**
   * Update project files.
   */
  private function updateProjectFiles(array $projectData): void {
    $this->updateProjectFilesFor('files', $projectData);
  }

  /**
   * Update project reference files.
   */
  private function updateProjectReferenceFiles(array $projectData): void {
    $this->updateProjectFilesFor('referenceFiles', $projectData);
  }

  /**
   * Update project files for.
   */
  private function updateProjectFilesFor(string $fileType, array $projectData): void {
    if (!isset($projectData[$fileType]) && count($projectData[$fileType]) === 0) {
      return;
    }

    $files = [];
    foreach ($projectData[$fileType] as $file) {
      if (isset($file['translations'])) {
        $file['translations'] = array_map(function (array $translation) {
            $translation['fileID'] = $translation['fileId'] ?? '';

            $translationModel = new TranslationModel();
            $translationModel->setFields($translation);
            return $translationModel;
        }, $file['translations']);
      }

      $fileModel = new FileModel();
      $fileModel->setFields(is_array($file) ? $file : ['sourceFileID' => $file]);

      $files[] = $fileModel;
    }

    $this->projectModel->setFields([$fileType => $files]);
  }

}
