<?php

namespace Drupal\straker_translate\Controller;

use Drupal\Core\Language\LanguageInterface;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\straker_translate\LanguageLocaleMapperInterface;
use Drupal\straker_translate\StrakerTranslate;
use Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface;
use Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface;
use Drupal\straker_translate\StrakerTranslateContentTranslationServiceInterface;
use Drupal\straker_translate\StrakerTranslateInterfaceTranslationServiceInterface;
use Drupal\straker_translate\StrakerTranslateInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Returns responses for straker_translate module setup routes.
 */
class StrakerTranslateNotificationController extends StrakerTranslateControllerBase {

  /**
   * The Straker Translate configuration service.
   *
   * @var \Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface
   */
  protected $straker_translateConfiguration;

  /**
   * The Straker Translate content translation service.
   *
   * @var \Drupal\straker_translate\StrakerTranslateContentTranslationServiceInterface
   */
  protected $straker_translateContentTranslation;

  /**
   * The Straker Translate configuration translation service.
   *
   * @var \Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface
   */
  protected $straker_translateConfigTranslation;

  /**
   * The Straker Translate interface translation service.
   *
   * @var \Drupal\straker_translate\StrakerTranslateInterfaceTranslationServiceInterface
   */
  protected $straker_translateInterfaceTranslation;

  /**
   * Constructs a StrakerTranslateControllerBase object.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The Request instance.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The factory for configuration objects.
   * @param \Drupal\straker_translate\StrakerTranslateInterface $straker_translate
   *   The straker_translate service.
   * @param \Drupal\straker_translate\LanguageLocaleMapperInterface $language_locale_mapper
   *   The language-locale mapper.
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The form builder.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   * @param \Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface $straker_translate_configuration
   *   The Straker Translate configuration service.
   * @param \Drupal\straker_translate\StrakerTranslateContentTranslationServiceInterface $content_translation_service
   *   The Straker Translate content translation service.
   * @param \Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface $config_translation_service
   *   The Straker Translate config translation service.
   * @param \Drupal\straker_translate\StrakerTranslateInterfaceTranslationServiceInterface $interface_translation_service
   *   The Straker Translate interface translation service.
   */
  public function __construct(Request $request, ConfigFactoryInterface $config_factory, StrakerTranslateInterface $straker_translate, LanguageLocaleMapperInterface $language_locale_mapper, FormBuilderInterface $form_builder, LoggerInterface $logger, StrakerTranslateConfigurationServiceInterface $straker_translate_configuration, StrakerTranslateContentTranslationServiceInterface $content_translation_service, StrakerTranslateConfigTranslationServiceInterface $config_translation_service, StrakerTranslateInterfaceTranslationServiceInterface $interface_translation_service) {
    parent::__construct($request, $config_factory, $straker_translate, $language_locale_mapper, $form_builder, $logger);
    $this->straker_translateConfiguration = $straker_translate_configuration;
    $this->straker_translateContentTranslation = $content_translation_service;
    $this->straker_translateConfigTranslation = $config_translation_service;
    $this->straker_translateInterfaceTranslation = $interface_translation_service;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('request_stack')->getCurrentRequest(),
      $container->get('config.factory'),
      $container->get('straker_translate'),
      $container->get('straker_translate.language_locale_mapper'),
      $container->get('form_builder'),
      $container->get('logger.channel.straker_translate'),
      $container->get('straker_translate.configuration'),
      $container->get('straker_translate.content_translation'),
      $container->get('straker_translate.config_translation'),
      $container->get('straker_translate.interface_translation')
    );
  }

  /**
   *
   */
  public function endpoint(Request $request) {
    $request_method = $request->getMethod();
    // Only allow POST requests.
    if ($request->getMethod() !== 'POST') {
      $this->logger('straker_translate')->warning('Webhook called with invalid method: @method', [
        '@method' => $request->getMethod(),
      ]);
      return new Response('Invalid method', 405);
    }
    // Get the raw payload and decode JSON.
    $payload_raw = $request->getContent();
    $payload = json_decode($payload_raw, TRUE);

    // If JSON is invalid, log and return error.
    if (json_last_error() !== JSON_ERROR_NONE || !is_array($payload)) {
      $this->logger->error('Webhook payload is not valid JSON: @payload', [
        '@payload' => $payload_raw,
      ]);
      return new Response('Invalid JSON', 400);
    }

    // Extract job_uuid from payload.
    $document_id = $payload['job_uuid'] ?? NULL;
    if (!$document_id) {
      $this->logger->error('Webhook received without job_uuid. Payload: @payload', [
        '@payload' => $payload_raw,
      ]);
      return new Response('Missing job_uuid', 400);
    }

    // Log for debugging.
    $this->logger->notice('Webhook received for job_uuid: @job_uuid and payload: @payload', [
      '@job_uuid' => $document_id,
      '@payload' => $payload_raw,
    ]);

    $http_status_code = Response::HTTP_ACCEPTED;
    $result = [];
    $messages = [];

    $lock = \Drupal::lock();
    $lock_name = __FUNCTION__ . ':' . $document_id;

    do {
      if ($lock->lockMayBeAvailable($lock_name)) {
        if ($held = $lock->acquire($lock_name)) {
          break;
        }
      }
      $lock->wait($lock_name, rand(1, 3));
    } while (TRUE);

    try {
      $entity = $this->getEntity($document_id);
      /** @var \Drupal\straker_translate\Entity\StrakerTranslateProfile $profile */
      $profile = $this->getProfile($entity);

      if ($entity) {
        if ($entity instanceof ConfigEntityInterface) {
          $translation_service = $this->straker_translateConfigTranslation;
        }
        elseif ($entity instanceof ContentEntityInterface) {
          $translation_service = $this->straker_translateContentTranslation;
        }
        else {
          $translation_service = $this->straker_translateInterfaceTranslation;
        }
        $project_status = $translation_service->checkSourceStatus($entity);
        // Check the project status and download the translation fileids.
        $source_status = $translation_service->getSourceStatus($entity);
        if ($project_status || $source_status === StrakerTranslate::STATUS_CURRENT) {
          $result['status_checked'] = 'Project status Checked succesfully';
          $result = $this->downloadDocuments($entity, $profile, $translation_service);
          $messages[] = "Document synced sucessfully.";
          $http_status_code = Response::HTTP_OK;
        }
        else {
          $result['status_checked'] = 'Something went wrong';
        }
      }
      else {
        $http_status_code = Response::HTTP_NO_CONTENT;
        $messages[] = "Document not found.";
      }
    }
    catch (\Exception $exception) {
      $http_status_code = Response::HTTP_SERVICE_UNAVAILABLE;
      $messages[] = new FormattableMarkup(
        'Something went wrong for document @document failed',
        ['@document' => $document_id]
      );
    }
    finally {
      $lock->release($lock_name);
    }
    // After the if/else block.
    $response = [
      'service' => 'notify',
      'method' => $request_method,
      'params' => $request->query->all(),
      'result' => $result,
      'messages' => $messages,
    ];

    $response = new CacheableJsonResponse($response, $http_status_code);
    $response->setMaxAge(0);
    $response->setSharedMaxAge(0);
    $response->getCacheableMetadata()->addCacheContexts(['url.query_args']);
    return $response;
  }

  // Download documents for the given entity and profile.

  /**
   * Returns an array with the result of the download operation.
   */
  protected function downloadDocuments($entity, $profile, $translation_service) {
    if (!$profile->hasAutomaticDownload()) {
      $result['download'] = 'Automatic download is not enabled for this profile.';
      return $result;
    }
    // Get all available languages using Drupal's language manager service.
    $target_languages = \Drupal::service('language_manager')->getLanguages();

    // Filter out only the enabled languages using straker_translate.configuration service.
    $target_languages = array_filter($target_languages, function (LanguageInterface $language) {
      $configLanguage = \Drupal::entityTypeManager()
        ->getStorage('configurable_language')
        ->load($language->getId());

      return \Drupal::service('straker_translate.configuration')->isLanguageEnabled($configLanguage);
    });
    if ($entity instanceof ConfigEntityInterface) {
      // Safe way to get language from config entity.
      $entity_langcode = $entity->language()->getId();
    }
    else {
      $entity_langcode = $entity->getUntranslated()->language()->getId();
    }

    foreach ($target_languages as $langcode => $language) {
      if ($langcode == $entity_langcode) {
        continue;
      }
      // Skip the download if the automatic download is not enabled for the target language.
      $hasAutomaticDownloadForTarget = $profile->hasAutomaticDownloadForTarget($langcode);
      if (!$hasAutomaticDownloadForTarget) {
        continue;
      }
      $document_id = $translation_service->getDocumentId($entity);
      $target_file_id = $translation_service->getTargetFileId($entity, $langcode);
      // Get queue service and add an item.
      $queue = \Drupal::queue('straker_translate_downloader_queue_worker');
      $item = [
        'entity_type_id' => $entity->getEntityTypeId(),
        'entity_id' => $entity->id(),
        'document_id' => $document_id,
        'langcode' => $langcode,
        'file_id' => $target_file_id,
      ];
      $item_id = $queue->createItem($item);
      // Store both item data and item_id for processing.
      $queued_items[] = $item;
    }
    foreach ($queued_items as $queued) {
      try {
        // If you want to download directly instead of queuing:
        $data = $translation_service->downloadDocument($entity, $queued['file_id'], $queued['langcode']);
        if ($data) {
          // Successful download — remove from queue.
          $result['download'][] = [
            'langcode' => $queued['langcode'],
            'status' => 'success',
          ];
        }
        else {
          // Keep in queue for cron to retry later.
          $result['download_queued'][] = [
            'langcode' => $queued['langcode'],
            'status' => 'queued (retry later)',
          ];
        }
      }
      catch (\Exception $e) {
        // Don't delete from queue — let cron retry.
        $this->logger->warning('Immediate download failed for @langcode: @message', [
          '@langcode' => $queued['langcode'],
          '@message' => $e->getMessage() ? $e->getMessage() : 'unknown error',
        ]);
      }
    }
    return $result;
  }

  /**
   * Get the Straker Translate profile associated with the given entity.
   */
  protected function getProfile($entity) {
    /** @var \Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface $configuration_service */
    $configuration_service = \Drupal::service('straker_translate.configuration');
    $profile = NULL;
    if ($entity instanceof ContentEntityInterface) {
      $profile = $configuration_service->getEntityProfile($entity);
    }
    elseif ($entity instanceof ConfigEntityInterface) {
      $profile = $configuration_service->getConfigEntityProfile($entity, FALSE);
    }
    return $profile;
  }

  /**
   * Get the entity associated with the given document ID.
   */
  protected function getEntity($document_id) {
    /** @var \Drupal\straker_translate\StrakerTranslateContentTranslationServiceInterface $translation_service */
    $translation_service = \Drupal::service('straker_translate.content_translation');
    $entity = $translation_service->loadByDocumentId($document_id);
    if ($entity === NULL) {
      /** @var \Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface $translation_service */
      $translation_service = \Drupal::service('straker_translate.config_translation');
      $entity = $translation_service->loadByDocumentId($document_id);
    }
    if ($entity === NULL) {
      /** @var \Drupal\straker_translate\StrakerTranslateInterfaceTranslationService $translation_service */
      $translation_service = \Drupal::service('straker_translate.interface_translation');
      $entity = $translation_service->loadByDocumentId($document_id);
    }
    return $entity;
  }

}
