<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Adapter;

use Drupal;
use Drupal\Component\Utility\Html;
use Drupal\Core\Extension\Exception\UnknownExtensionException;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\File\FileSystem;
use Drupal\languagewire_translation_provider\Adapter\Database\LanguageWireConfigurationItemRepositoryInterface;
use Drupal\languagewire_translation_provider\Adapter\Logger\LoggerFactory;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\JobRepositoryInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobRepository;
use Drupal\languagewire_translation_provider\Database\InvoicingAccountRepositoryInterface;
use Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface;
use Drupal\languagewire_translation_provider\Database\ServiceRepositoryInterface;
use Drupal\languagewire_translation_provider\Database\TermBaseRepositoryInterface;
use Drupal\languagewire_translation_provider\Database\TranslationMemoryRepositoryInterface;
use Drupal\languagewire_translation_provider\Database\TypedInvoicingAccountRepository;
use Drupal\languagewire_translation_provider\Database\TypedServiceRepository;
use Drupal\languagewire_translation_provider\Database\TypedTermBaseRepository;
use Drupal\languagewire_translation_provider\Database\TypedTranslationMemoryRepository;
use Drupal\languagewire_translation_provider\Database\TypedUserRepository;
use Drupal\languagewire_translation_provider\Database\TypedWorkAreaRepository;
use Drupal\languagewire_translation_provider\Database\UserRepositoryInterface;
use Drupal\languagewire_translation_provider\Database\WorkAreaRepositoryInterface;
use Drupal\languagewire_translation_provider\Settings\CmsSettings;
use Drupal\languagewire_translation_provider\Settings\SettingsInterface;
use Psr\Log\LoggerInterface;

/**
 * System adapter for Drupal.
 */
final class System implements SystemInterface {

  /**
   * Configuration item repository.
   *
   * @var \Drupal\languagewire_translation_provider\Adapter\Database\LanguageWireConfigurationItemRepositoryInterface
   */
  private LanguageWireConfigurationItemRepositoryInterface $configItemRepository;

  /**
   * Project template repository.
   *
   * @var \Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface
   */
  private ProjectTemplateRepositoryInterface $projectTemplateRepository;

  /**
   * Extension list module.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  private ModuleExtensionList $extensionListModule;

  /**
   * File system.
   *
   * @var \Drupal\Core\File\FileSystem
   */
  private FileSystem $fileSystem;

  /**
   * Constructs a new System object.
   *
   * @param \Drupal\languagewire_translation_provider\Adapter\Database\LanguageWireConfigurationItemRepositoryInterface $configItemRepository
   *   Configuration item repository.
   * @param \Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface $projectTemplateRepository
   *   Project template repository.
   * @param \Drupal\Core\Extension\ModuleExtensionList $extensionListModule
   *   Extension list module.
   * @param \Drupal\Core\File\FileSystem $fileSystem
   *   File system.
   */
  public function __construct(
    LanguageWireConfigurationItemRepositoryInterface $configItemRepository,
    ProjectTemplateRepositoryInterface $projectTemplateRepository,
    ModuleExtensionList $extensionListModule,
    FileSystem $fileSystem
  ) {
    $this->configItemRepository = $configItemRepository;
    $this->projectTemplateRepository = $projectTemplateRepository;
    $this->extensionListModule = $extensionListModule;
    $this->fileSystem = $fileSystem;
  }

  /**
   * Version.
   */
  public function version(): string {
    return Drupal::VERSION;
  }

  /**
   * Module version.
   */
  public function moduleVersion(string $moduleName): ?string {
    try {
      $moduleInfo = $this->extensionListModule->getExtensionInfo($moduleName);

      return empty($moduleInfo) || !isset($moduleInfo['version'])
        ? 'unknown'
        : $moduleInfo['version'];
    }
    catch (UnknownExtensionException) {
      return NULL;
    }
  }

  /**
   * Encode string.
   */
  public function encodeString(string $string): string {
    return Html::escape($string);
  }

  /**
   * Substring.
   */
  public function substring(string $string, int $startIndex, int $length): string {
    return mb_substr($string, $startIndex, $length);
  }

  /**
   * Real path.
   */
  public function realPath(string $path): string {
    return $this->fileSystem->realpath($path) ?: '';
  }

  /**
   * Logger.
   */
  public function logger(): LoggerInterface {
    return (new LoggerFactory($this))->createLogger();
  }

  /**
   * Service repository.
   */
  public function serviceRepository(): ServiceRepositoryInterface {
    return new TypedServiceRepository($this->configItemRepository);
  }

  /**
   * User repository.
   */
  public function userRepository(): UserRepositoryInterface {
    return new TypedUserRepository($this->configItemRepository);
  }

  /**
   * Invoice account repository.
   */
  public function invoicingAccountRepository(): InvoicingAccountRepositoryInterface {
    return new TypedInvoicingAccountRepository($this->configItemRepository);
  }

  /**
   * Translation memory repository.
   */
  public function translationMemoryRepository(): TranslationMemoryRepositoryInterface {
    return new TypedTranslationMemoryRepository($this->configItemRepository);
  }

  /**
   * Term base repository.
   */
  public function termBaseRepository(): TermBaseRepositoryInterface {
    return new TypedTermBaseRepository($this->configItemRepository);
  }

  /**
   * Work area repository.
   */
  public function workAreaRepository(): WorkAreaRepositoryInterface {
    return new TypedWorkAreaRepository($this->configItemRepository);
  }

  /**
   * Project template repository.
   */
  public function projectTemplateRepository(): ProjectTemplateRepositoryInterface {
    return $this->projectTemplateRepository;
  }

  /**
   * Settings.
   */
  public function settings(): SettingsInterface {
    return new CmsSettings();
  }

  /**
   * TMGMT job repository.
   */
  public function tmgmtJobRepository(): JobRepositoryInterface {
    return new TmgmtJobRepository();
  }

}
