<?php

use Drupal\languagewire_translation_provider\Adapter\Content\VariablePlaceholderPrefixingConvention;
use Drupal\languagewire_translation_provider\Adapter\Entity\EntityRepository;
use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslator;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface;
use Drupal\languagewire_translation_provider\api\Core\Exception\Error;
use Drupal\languagewire_translation_provider\api\Core\Exception\RecoverableError;
use Drupal\languagewire_translation_provider\Client\LanguageWireClientFactory;
use Drupal\languagewire_translation_provider\Client\LanguageWireClientSettingsFactory;
use Drupal\languagewire_translation_provider\Client\ProjectSpecificationFactory;
use Drupal\languagewire_translation_provider\Content\CompositeSanitizer;
use Drupal\languagewire_translation_provider\Content\EmojiSanitizer;
use Drupal\languagewire_translation_provider\Content\HtmlEntitiesEncoder;
use Drupal\languagewire_translation_provider\Content\NewLineSanitizer;
use Drupal\languagewire_translation_provider\Content\Translation\Import\CompositeFallback;
use Drupal\languagewire_translation_provider\Content\Translation\Import\FallbackToPreviousTranslation;
use Drupal\languagewire_translation_provider\Content\Translation\Import\FallbackToSourceValues;
use Drupal\languagewire_translation_provider\Cron\Job\ContentUploadJob;
use Drupal\languagewire_translation_provider\Cron\Job\ProjectCreationJob;
use Drupal\languagewire_translation_provider\Cron\Job\ProjectMonitoringJob;
use Drupal\languagewire_translation_provider\Cron\Job\ProjectImportJob;
use Drupal\languagewire_translation_provider\Domain\ProjectStatus;
use Drupal\languagewire_translation_provider\Platform\ConfigurationItemImporterFactory;
use Drupal\languagewire_translation_provider\Service\ProjectContentUploader;
use Drupal\languagewire_translation_provider\Service\ProjectContentUploader\DocumentSelectorFactory;
use Drupal\languagewire_translation_provider\Service\ProjectCreator;
use Drupal\languagewire_translation_provider\Service\ProjectMonitor;
use Drupal\languagewire_translation_provider\Service\ProjectImporter;
use Drupal\tmgmt\TranslatorInterface;

/**
 * Implements hook_ENTITY-TYPE_presave().
 *
 * Pre save hook to handle the configuration changes.
 */
function languagewire_translation_provider_tmgmt_translator_presave(TranslatorInterface $tmgmtTranslator): void {

  $previousClientID = isset($tmgmtTranslator->original) ? $tmgmtTranslator->original->getSetting('client_id') : NULL;
  $clientID = $tmgmtTranslator->getSetting('client_id');
  $isFirstTimeConfiguration = $clientID && !$previousClientID;
  $hasClientIDChanged = $clientID != $previousClientID;

  if (empty($clientID)) {
    return;
  }

  $tmgmtTranslator = new TmgmtTranslator($tmgmtTranslator);
  if ($isFirstTimeConfiguration || $hasClientIDChanged) {
    if ($hasClientIDChanged) {
      $cancelableStatus = [
        ProjectStatus::uploading(),
        ProjectStatus::underCreation(),
        ProjectStatus::inProgress(),
        ProjectStatus::importing(),
      ];
      $projectRepository = Drupal::service('languagewire.project_repository');

      foreach ($cancelableStatus as $status) {
        $projects = $projectRepository->getAllByStatus($status);

        foreach ($projects as $project) {
          $tmgmtProject = $project->getTmgmtJob();

          if ($tmgmtTranslator->id() !== $tmgmtProject->translator()->id()) {
            continue;
          }

          $tmgmtProject->cancel('The project has been cancelled because the Client ID has changed.');
          $project->setStatus(ProjectStatus::cancelled());
          $projectRepository->save($project);
        }
      }
    }

    importLanguageWireConfigurationItems($tmgmtTranslator);
  } else {
    importLanguageWireApiLimits($tmgmtTranslator);
  }
}

/**
 * Implements hook_ENTITY-TYPE_delete() for deleting entity type data.
 */
function languagewire_translation_provider_tmgmt_translator_delete(TranslatorInterface $tmgmtTranslator): void {
  $system                      = getSystem();
  $projectSpecificationFactory = Drupal::service('languagewire.project_specification_factory');

  $configurationItemImporterFactory = new ConfigurationItemImporterFactory($system, $projectSpecificationFactory);

  $importer = $configurationItemImporterFactory->createDummy();
  $importer->clearAllItems(new TmgmtTranslator($tmgmtTranslator));
}

/**
 * Imports the configuration items from the LanguageWire platform.
 *
 * @param \Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface $translator
 *   The translator.
 */
function importLanguageWireConfigurationItems(TmgmtTranslatorInterface $translator): void {
  $clientID = $translator->getClientId();

  $system = getSystem();
  $logger = $system->logger();

  $projectSpecificationFactory      = Drupal::service('languagewire.project_specification_factory');
  $configurationItemImporterFactory = new ConfigurationItemImporterFactory($system, $projectSpecificationFactory);

  try {
      $importer = $configurationItemImporterFactory->createFor($translator);
      $importer->importItems($translator);
  } catch (Error $exception) {
      $importer = $configurationItemImporterFactory->createDummy();
      $importer->clearAllItems($translator);

      $message = "The Client ID $clientID or Client Secret are invalid. Please enter valid credentials.";
      $logger->error($message . PHP_EOL . $exception);
      Drupal::messenger()->addError($message);
  } catch (RecoverableError $exception) {
      $limitRenowned = gmdate('H:i:s', strtotime('tomorrow') - time());
      $message = "You have reached the request limit. Please try in $limitRenowned.";
      $logger->error($message . PHP_EOL . $exception);
      Drupal::messenger()->addError($message);
  } catch (Exception $exception) {
      $message = "Could not connect to LanguageWire platform. Please try again later.";
      $logger->error($message . PHP_EOL . $exception);
      Drupal::messenger()->addError($message);
  }
}

/**
 * Imports the API limits from the LanguageWire platform.
 *
 * @param \Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslator $translator
 *   The translator.
 */
function importLanguageWireApiLimits(TmgmtTranslator $translator): void {
  $system = getSystem();
  $logger = $system->logger();

  try {
    $projectSpecificationFactory = Drupal::service('languagewire.project_specification_factory');

    $clientSettingsFactory = new LanguageWireClientSettingsFactory($system);
    $clientFactory = new LanguageWireClientFactory($clientSettingsFactory, $projectSpecificationFactory);

    $apiLimits = $clientFactory->createForTmgmtTranslator($translator)->getLimits();
    $translator->setMaxDocumentsPerProject($apiLimits->getMaxDocumentsPerProject());
  }
  catch (Error $exception) {
    $clientID = $translator->getClientId();

    $message = "The Client ID $clientID or Client Secret are invalid. Please enter valid credentials.";
    $logger->error($message . PHP_EOL . $exception);
    Drupal::messenger()->addError($message);
  }
  catch (RecoverableError $exception) {
    $limitRenowned = gmdate('H:i:s', strtotime('tomorrow') - time());
    $message = "You have reached the request limit. Please try in $limitRenowned.";
    $logger->error($message . PHP_EOL . $exception);
    Drupal::messenger()->addError($message);
  }
  catch (Exception $exception) {
    $message = "Could not connect to LanguageWire platform. Please try again later.";
    $logger->error($message . PHP_EOL . $exception);
    Drupal::messenger()->addError($message);
  }
}

/**
 * Gets the LanguageWire client factory.
 *
 * @param \Drupal\languagewire_translation_provider\Adapter\SystemInterface $system
 *   The system.
 */
function getClientFactory(SystemInterface $system): LanguageWireClientFactory {
  $clientSettingsFactory = new LanguageWireClientSettingsFactory($system);
  $projectTemplateRepository = Drupal::service('languagewire.project_template_repository');
  $projectSpecificationFactory = new ProjectSpecificationFactory($projectTemplateRepository);
  return new LanguageWireClientFactory($clientSettingsFactory, $projectSpecificationFactory);
}


/**
 * Performs routine tasks for content upload during cron runs.
 *
 * This function is executed by the cron system to handle content upload
 * processes at scheduled intervals.
 */

function languagewire_content_upload_cron(): void {
  $system = getSystem();

  $logger = $system->logger();
  $sanitizer = new CompositeSanitizer(new NewLineSanitizer(), new HtmlEntitiesEncoder(), new EmojiSanitizer());
  $documentSelectorFactory = new DocumentSelectorFactory();
  $htmlPreviewService = Drupal::service('languagewire.html_preview_service');
  $temporaryArtifactsCleaner = Drupal::service('languagewire.html_preview_temporary_artifacts_cleaner');
  $clientFactory = getClientFactory($system);
  $cronJob = new ContentUploadJob(
    $system,
    new ProjectContentUploader(
      $system,
      $sanitizer,
      $documentSelectorFactory,
      $htmlPreviewService,
      $temporaryArtifactsCleaner
    ),
    $clientFactory,
    Drupal::service('languagewire.project_repository')
  );

  try {
      $cronJob->execute();
  }
  catch (Throwable $exception) {
      $logger->error("$exception");
  }
}

/**
 * Handles project creation tasks during cron runs.
 *
 * This function is executed by the cron system to manage project creation
 * tasks at scheduled intervals.
 */
function languagewire_project_creation_cron(): void {
  $system = getSystem();
  $logger = $system->logger();
  $clientFactory = getClientFactory($system);

  $cronJob = new ProjectCreationJob(
    $system,
    Drupal::service('languagewire.project_repository'),
    $clientFactory,
    new ProjectCreator($logger)
  );

  try {
    $cronJob->execute();
  }
  catch (Throwable $exception) {
    $logger->error("$exception");
  }
}

/**
 * Executes project monitoring tasks during cron runs.
 *
 * This function is called by the cron system to perform monitoring
 * tasks related to projects at scheduled intervals.
 */
function languagewire_project_monitoring_cron(): void {
  $system = getSystem();

  $logger = $system->logger();
  $variablePlaceholderPrefixingConvention = new VariablePlaceholderPrefixingConvention();

  $clientFactory = getClientFactory($system);

  $projectRepository = Drupal::service('languagewire.project_repository');

  $cronJob = new ProjectMonitoringJob(
    $system,
    $projectRepository,
    $clientFactory,
    new ProjectMonitor($projectRepository, $logger, $variablePlaceholderPrefixingConvention)
  );

  try {
      $cronJob->execute();
  }
  catch (Throwable $exception) {
      $logger->error("$exception");
  }
}

/**
 * Handles project import tasks during cron runs.
 *
 * This function is executed by the cron system to perform project import
 * operations at scheduled intervals.
 */
function languagewire_project_import_cron(): void {
  $system = getSystem();
  $logger = $system->logger();
  $sanitizer = new CompositeSanitizer(new NewLineSanitizer(), new HtmlEntitiesEncoder(), new EmojiSanitizer());
  $translationImportFallback = new CompositeFallback(
    new FallbackToPreviousTranslation(new EntityRepository($system, Drupal::entityTypeManager())),
    new FallbackToSourceValues()
  );
  $clientFactory = getClientFactory($system);

  $cronJob = new ProjectImportJob(
    $system,
    Drupal::service('languagewire.project_repository'),
    $clientFactory,
    new ProjectImporter($sanitizer, $logger, $translationImportFallback)
  );

  try {
    $cronJob->execute();
  }
  catch (Throwable $exception) {
    $logger->error("$exception");
  }
}

/**
 * Retrieves the system entity.
 *
 * This function returns the system entity used for various operations
 * within the module.
 *
 * @return SystemInterface
 *   The system entity.
 */
function getSystem(): SystemInterface {
  return Drupal::service('languagewire.drupal.system');
}
