<?php

namespace Drupal\straker_translate\Form;

use Drupal\config_translation\ConfigEntityMapper;
use Drupal\config_translation\ConfigFieldMapper;
use Drupal\config_translation\ConfigMapperInterface;
use Drupal\config_translation\ConfigNamesMapper;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\file\Entity\File;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\straker_translate\Exception\StrakerTranslateApiException;
use Drupal\straker_translate\Exception\StrakerTranslateDocumentArchivedException;
use Drupal\straker_translate\Exception\StrakerTranslateDocumentLockedException;
use Drupal\straker_translate\Exception\StrakerTranslateDocumentNotFoundException;
use Drupal\straker_translate\Exception\StrakerTranslatePaymentRequiredException;
use Drupal\straker_translate\Exception\StrakerTranslateProcessedWordsLimitException;
use Drupal\straker_translate\LanguageLocaleMapperInterface;
use Drupal\straker_translate\StrakerTranslate;
use Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface;
use Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface;
use Drupal\straker_translate\StrakerTranslateProfileInterface;
use Drupal\straker_translate\StrakerTranslateSetupTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\straker_translate\Exception\StrakerTranslateDocumentAlreadyUploaded;

/**
 * Form for bulk management of content.
 */
class StrakerTranslateConfigManagementForm extends FormBase {

  use StrakerTranslateSetupTrait;

  /**
   * The language-locale mapper.
   *
   * @var \Drupal\straker_translate\LanguageLocaleMapperInterface
   */
  protected $languageLocaleMapper;

  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

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

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

  /**
   * The tempstore factory.
   *
   * @var \Drupal\Core\TempStore\PrivateTempStoreFactory
   */
  protected $tempStoreFactory;

  /**
   * A array of configuration mapper instances.
   *
   * @var \Drupal\config_translation\ConfigMapperInterface[]
   */
  protected $mappers;

  /**
   * The type of config to display.
   *
   * @var string
   */
  protected $filter;

  /**
   * Constructs a new StrakerTranslateManagementForm object.
   *
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface $straker_translate_configuration
   *   The Straker Translate configuration service.
   * @param \Drupal\straker_translate\LanguageLocaleMapperInterface $language_locale_mapper
   *   The language-locale mapper.
   * @param \Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface $translation_service
   *   The Straker Translate config translation service.
   * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
   *   The factory for the temp store object.
   * @param \Drupal\config_translation\ConfigMapperInterface[] $mappers
   *   The configuration mappers.
   */
  public function __construct(LanguageManagerInterface $language_manager, StrakerTranslateConfigurationServiceInterface $straker_translate_configuration, LanguageLocaleMapperInterface $language_locale_mapper, StrakerTranslateConfigTranslationServiceInterface $translation_service, PrivateTempStoreFactory $temp_store_factory, array $mappers) {
    $this->languageManager = $language_manager;
    $this->translationService = $translation_service;
    $this->tempStoreFactory = $temp_store_factory;
    $this->straker_translate = \Drupal::service('straker_translate');
    $this->straker_translateConfiguration = $straker_translate_configuration;
    $this->languageLocaleMapper = $language_locale_mapper;
    $this->mappers = $mappers;
    $this->filter = 'config';
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('language_manager'),
      $container->get('straker_translate.configuration'),
      $container->get('straker_translate.language_locale_mapper'),
      $container->get('straker_translate.config_translation'),
      $container->get('tempstore.private'),
      $container->get('plugin.manager.config_translation.mapper')->getMappers()
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'straker_translate_config_management';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    if ($redirect = $this->checkSetup()) {
      return $redirect;
    }
    $showingFields = FALSE;

    $this->filter = $this->getFilter();
    $temp_store = $this->getFilterTempStore();

    // Create the headers first so they can be used for sorting.
    $headers = [
      'title' => [
        'data' => $this->t('Entity'),
      ],
      'source' => $this->t('Source'),
      'project_status' => $this->t('Project Status'),
      'translations' => $this->t('Translations'),
      'target_status' => $this->t('Target Status'),
      'profile' => $this->t('Profile'),
    ];

    // @todo Find a better filter?
    if ($this->filter === 'config') {
      $mappers = array_filter($this->mappers, function ($mapper) {
        return ($mapper instanceof ConfigNamesMapper
          && !$mapper instanceof ConfigEntityMapper
          && !$mapper instanceof ConfigFieldMapper);
      });
    }
    elseif (substr($this->filter, -7) == '_fields') {
      $showingFields = TRUE;
      /** @var \Drupal\config_translation\ConfigFieldMapper $mapper */
      $mapper = $this->mappers[$this->filter];
      $base_entity_type = $mapper->getPluginDefinition()['base_entity_type'];

      // If we are showing field config instances, we need to show bundles for
      // a better UX.
      $headers = [
        'bundle' => [
          'data' => $this->t('Bundle'),
          'specifier' => 'bundle',
        ],
      ] + $headers;

      // Make the table sortable by field label.
      $headers['title']['specifier'] = 'label';

      $ids = \Drupal::entityQuery('field_config')
        ->accessCheck(FALSE)
        ->condition('id', $base_entity_type . '.', 'STARTS_WITH')
        ->tableSort($headers)
        ->execute();
      $fields = FieldConfig::loadMultiple($ids);
      $mappers = [];
      foreach ($fields as $id => $field) {
        $new_mapper = clone $mapper;
        $new_mapper->setEntity($field);
        $mappers[$field->id()] = $new_mapper;
      }
    }
    else {
      $mapper = $this->mappers[$this->filter];
      $query = \Drupal::entityQuery($this->filter);
      $query->accessCheck(FALSE);
      $label_filter = $temp_store->get('label');

      // Determine the machine name of the title for this entity type.
      $entity_storage = \Drupal::entityTypeManager()->getStorage($this->filter);
      $entity_keys = $entity_storage->getEntityType()->getKeys();
      if (isset($entity_keys['label'])) {
        $label_key = $entity_keys['label'];
        if ($label_filter) {
          $query->condition($label_key, $label_filter, 'CONTAINS');
        }

        $headers['title']['specifier'] = $label_key;
        $query->tableSort($headers);
      }

      $ids = $query->execute();
      $entities = $entity_storage->loadMultiple($ids);
      /** @var \Drupal\config_translation\ConfigEntityMapper $mapper  */
      $mappers = [];
      foreach ($entities as $entity) {
        $new_mapper = clone $mapper;
        $new_mapper->setEntity($entity);
        $mappers[$entity->id()] = $new_mapper;
      }
    }

    $rows = [];
    foreach ($mappers as $mapper_id => $mapper) {
      if ($mapper_id == 'system.rss_feeds_settings') {
        continue;
      }
      if (!in_array($mapper->getLangcode(), [LanguageInterface::LANGCODE_NOT_SPECIFIED, LanguageInterface::LANGCODE_NOT_APPLICABLE])) {
        $is_config_entity = $mapper instanceof ConfigEntityMapper;

        // Get the language and display the full language name instead of styled status
        $language = ConfigurableLanguage::load($mapper->getLangcode());
        $source = [
          'data' => [
            '#markup' => $language ? $language->getName() : $mapper->getLangcode(),
          ],
        ];
        $translations = $this->getTranslationsStatuses($mapper);

        $profile = $is_config_entity ?
          $this->straker_translateConfiguration->getConfigEntityProfile($mapper->getEntity()) :
          $this->straker_translateConfiguration->getConfigProfile($mapper_id, TRUE);
        // Get project status from source status and build dropdown actions
        $project_status = '';
        $project_css_class = '';
        $project_actions = [];
        $project_status_tooltip = '';
        $query_options = ['query' => ['destination' => \Drupal::request()->getRequestUri()]];

        if ($is_config_entity && $mapper->getEntity()) {
          $entity = $mapper->getEntity();
          $source_status = $this->translationService->getSourceStatus($entity);
          $document_id = $this->translationService->getDocumentId($entity);

          // Map status to human-readable text matching ProjectStatus.php
          switch ($source_status) {
            case 'CURRENT':
              $project_status = $this->t('Verify Translated');
              $project_css_class = 'source-current';
              // if ($document_id) {
              //   $project_actions[] = [
              //     'title' => $this->t('Re-upload document'),
              //     'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
              //       'entity_type' => $entity->getEntityTypeId(),
              //       'entity_id' => $entity->id(),
              //     ], $query_options),
              //   ];
              // }
              break;
            case 'UNTRACKED':
              $project_status = $this->t('Not Started');
              $project_css_class = 'source-request';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              }
              break;
            case 'READY':
              $project_status = $this->t('Check Status');
              $project_css_class = 'source-ready';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Check translation status'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.check_upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                    'document_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'PROCESSING':
              $project_status = $this->t('In Progress');
              $project_css_class = 'source-processing';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Check translation status'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.check_upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                    'document_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'IMPORTING':
              $project_status = $this->t('Importing');
              $project_css_class = 'source-importing';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('View in Verify'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.workbench', [
                    'doc_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'PENDING':
              $project_status = $this->t('Pending');
              $project_css_class = 'target-pending';
              break;
            case 'REQUEST':
              $project_status = $this->t('Not Started');
              $project_css_class = 'source-request';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              }
              break;
            case 'EDITED':
              $project_status = $this->t('Document Edited');
              $project_css_class = 'source-edited';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              }
              break;
            case 'ERROR':
              $project_status = $this->t('Error');
              $project_css_class = 'source-error';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Check translation status'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.check_upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                    'document_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'CANCELLED':
              $project_status = $this->t('Cancelled');
              $project_css_class = 'source-cancelled';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              }
              break;
            case 'DISABLED':
              $project_status = $this->t('Disabled');
              $project_css_class = 'source-disabled';
              break;
            case 'DELETED':
              $project_status = $this->t('Deleted');
              $project_css_class = 'source-deleted';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              }
              break;
            case 'ARCHIVED':
              $project_status = $this->t('Archived');
              $project_css_class = 'source-archived';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $entity->getEntityTypeId(),
                    'entity_id' => $entity->id(),
                  ], $query_options),
                ];
              }
              break;
            case 'NONE':
              $project_status = $this->t('No Status');
              $project_css_class = 'source-none';
              break;
            default:
              $project_status = $this->t('Unknown');
              $project_css_class = 'source-none';
          }

          // Generate tooltip text
          $project_status_tooltip = $this->getProjectStatusTooltip($source_status, $is_config_entity, $entity, $document_id);

          // Add secondary actions like in the source column
          $secondary_actions = $this->getProjectStatusSecondaryActions($mapper, $source_status, $document_id, $query_options);
          $project_actions = array_merge($project_actions, $secondary_actions);

          // Always add view option if available
          if ($entity->hasLinkTemplate('canonical')) {
            $project_actions[] = [
              'title' => $this->t('View'),
              'url' => $entity->toUrl(),
            ];
          }
        } else {
          // For non-entity config, get the config source status
          $source_status = $this->translationService->getConfigSourceStatus($mapper);
          $document_id = $this->translationService->getConfigDocumentId($mapper);

          // Map status to human-readable text matching ProjectStatus.php
          switch ($source_status) {
            case 'CURRENT':
              $project_status = $this->t('Verify Translated');
              $project_css_class = 'source-current';
              // if ($document_id) {
              //   $project_actions[] = [
              //     'title' => $this->t('Re-upload document'),
              //     'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
              //       'entity_type' => $mapper_id,
              //       'entity_id' => $mapper_id,
              //     ], $query_options),
              //   ];
              // }
              break;
            case 'UNTRACKED':
              $project_status = $this->t('Not Started');
              $project_css_class = 'source-request';
              // Add actions for config items - for non-entity configs, use the same value for both parameters
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              }
              break;
            case 'READY':
              $project_status = $this->t('Check Status');
              $project_css_class = 'source-ready';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Check translation status'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.check_upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                    'document_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'PROCESSING':
              $project_status = $this->t('In Progress');
              $project_css_class = 'source-processing';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Check translation status'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.check_upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                    'document_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'IMPORTING':
              $project_status = $this->t('Importing');
              $project_css_class = 'source-importing';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('View in Verify'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.workbench', [
                    'doc_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'PENDING':
              $project_status = $this->t('Pending');
              $project_css_class = 'target-pending';
              break;
            case 'REQUEST':
              $project_status = $this->t('Not Started');
              $project_css_class = 'source-request';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              }
              break;
            case 'EDITED':
              $project_status = $this->t('Document Edited');
              $project_css_class = 'source-edited';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              }
              break;
            case 'ERROR':
              $project_status = $this->t('Error');
              $project_css_class = 'source-error';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Check translation status'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.check_upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                    'document_id' => $document_id,
                  ], $query_options),
                ];
              }
              break;
            case 'CANCELLED':
              $project_status = $this->t('Cancelled');
              $project_css_class = 'source-cancelled';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              }
              break;
            case 'DISABLED':
              $project_status = $this->t('Disabled');
              $project_css_class = 'source-disabled';
              break;
            case 'DELETED':
              $project_status = $this->t('Deleted');
              $project_css_class = 'source-deleted';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              }
              break;
            case 'ARCHIVED':
              $project_status = $this->t('Archived');
              $project_css_class = 'source-archived';
              if ($document_id) {
                $project_actions[] = [
                  'title' => $this->t('Update Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              } else {
                $project_actions[] = [
                  'title' => $this->t('Upload Document'),
                  'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.upload', [
                    'entity_type' => $mapper_id,
                    'entity_id' => $mapper_id,
                  ], $query_options),
                ];
              }
              break;
            case 'NONE':
              $project_status = $this->t('No Status');
              $project_css_class = 'source-none';
              break;
            default:
              $project_status = $this->t('Unknown');
              $project_css_class = 'source-none';
          }
        }

        // Generate tooltip text for non-entity config if not already set
        if (empty($project_status_tooltip)) {
          $project_status_tooltip = $this->getProjectStatusTooltip($source_status, $is_config_entity, $mapper, $document_id);
        }

        // Add secondary actions for non-entity configs if not already added
        if (!$is_config_entity || !$mapper->getEntity()) {
          $secondary_actions = $this->getProjectStatusSecondaryActions($mapper, $source_status, $document_id, $query_options);
          $project_actions = array_merge($project_actions, $secondary_actions);
        }

        // Build dropdown HTML
        $dropdown_html = '';
        $dropdown_id = 'project-status-config-' . $mapper_id;
        $wrapper_class = 'project-status-wrapper';
        if (!empty($project_actions)) {
          $wrapper_class .= ' has-dropdown';
          $dropdown_html = '<span class="project-status-dropdown-toggle"></span>';
          $dropdown_html .= '<ul class="project-status-dropdown-menu" id="' . $dropdown_id . '">';
          foreach ($project_actions as $action) {
            $target = isset($action['new_window']) && $action['new_window'] ? ' target="_blank"' : '';
            $dropdown_html .= '<li><a href="' . $action['url']->toString() . '"' . $target . '>' . $action['title'] . '</a></li>';
          }
          $dropdown_html .= '</ul>';
        }

        $form['table'][$mapper_id] = [
          '#type' => 'checkbox',
          '#value' => $mapper_id,
        ];
        $rows[$mapper_id] = [];
        $rows[$mapper_id] += [
          'title' => trim($mapper->getTitle()),
          'source' => $source,
          'project_status' => [
            'data' => [
              '#markup' => '<div class="' . $wrapper_class . '"><span class="language-icon ' . $project_css_class . '" title="' . $project_status_tooltip . '">' . $project_status . $dropdown_html . '</span></div>',
              '#attached' => [
                'library' => ['straker_translate/base', 'straker_translate/project_status_dropdown'],
              ],
            ],
          ],
          'translations' => $translations,
          'target_status' => $this->getTargetStatusContent($mapper),
          'profile' => $profile ? $profile->label() : '',
        ];
        if ($is_config_entity) {
          $link = NULL;
          if ($mapper->getEntity()->hasLinkTemplate('canonical')) {
            $link = $mapper->getEntity()->toLink(trim($mapper->getTitle()));
          }
          elseif ($mapper->getEntity()->hasLinkTemplate('edit-form')) {
            $link = $mapper->getEntity()
              ->toLink(trim($mapper->getTitle()), 'edit-form');
          }
          if ($link !== NULL) {
            $rows[$mapper_id]['title'] = $link;
          }
        }

        if ($showingFields) {
          $entity_type_id = $mapper->getEntity()->get('entity_type');
          $bundle = $mapper->getEntity()->get('bundle');
          $bundle_info = \Drupal::service('entity_type.bundle.info')
            ->getBundleInfo($entity_type_id);
          if (isset($bundle_info[$bundle])) {
            $rows[$mapper_id]['bundle'] = trim($bundle_info[$bundle]['label']);
          }
          else {
            $rows[$mapper_id]['bundle'] = trim($bundle);
          }
        }
      }
    }
    // Add filters.
    $form['filters'] = [
      '#type' => 'details',
      '#title' => $this->t('Select config bundle'),
      '#open' => TRUE,
      '#weight' => 5,
      '#tree' => TRUE,
    ];
    $form['filters']['wrapper'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['form--inline', 'clearfix']],
    ];
    $form['filters']['wrapper']['bundle'] = [
      '#type' => 'select',
      '#title' => $this->t('Filter'),
      '#options' => ['config' => $this->t('Simple configuration')] + $this->getAllBundles(),
      '#default_value' => $this->filter,
      '#attributes' => ['class' => ['form-item']],
    ];
    if (isset($mapper) && $mapper instanceof ConfigEntityMapper) {
      $form['filters']['wrapper']['label'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Label'),
        '#default_value' => $temp_store->get('label'),
        '#attributes' => ['class' => ['form-item']],
      ];
    }
    $form['filters']['actions'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['clearfix']],
    ];
    $form['filters']['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Filter'),
      '#submit' => ['::filterForm'],
    ];
    $form['filters']['actions']['reset'] = [
      '#type' => 'submit',
      '#value' => $this->t('Reset'),
      '#submit' => ['::resetFilterForm'],
    ];

    // Build an 'Update options' form.
    $form['actions'] = [
      '#type' => 'details',
      '#title' => $this->t('Bulk document management'),
      '#open' => TRUE,
      '#attributes' => ['class' => ['container-inline']],
      '#weight' => 10,
    ];
    $form['actions']['operation'] = [
      '#type' => 'select',
      '#title' => $this->t('Action'),
      '#title_display' => 'invisible',
      '#options' => $this->generateBulkOptions(),
    ];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Execute'),
    ];
    $form['actions']['options'] = [
      '#type' => 'container',
      '#tree' => TRUE,
    ];
    $form['table'] = [
      '#header' => $headers,
      '#options' => $rows,
      '#empty' => $this->t('No content available'),
      '#type' => 'tableselect',
      '#weight' => 30,
    ];
    $form['pager'] = [
      '#type' => 'pager',
      '#weight' => 50,
    ];
    $form['#attached']['library'][] = 'straker_translate/straker_translate';
    return $form;
  }

  /**
   * Gets the filter to be applied. By default will be 'config'.
   *
   * @return string
   */
  protected function getFilter() {
    /** @var \Drupal\Core\TempStore\PrivateTempStore $temp_store */
    $temp_store = $this->getFilterTempStore();
    $value = $temp_store->get('bundle');
    if (!$value) {
      $value = 'config';
    }
    return $value;
  }

  /**
   * Form submission handler for filtering.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function filterForm(array &$form, FormStateInterface $form_state) {
    $value = $form_state->getValue(['filters', 'wrapper', 'bundle']);
    $label = $form_state->getValue(['filters', 'wrapper', 'label']) ?: NULL;

    /** @var \Drupal\Core\TempStore\PrivateTempStore $temp_store */
    $temp_store = $this->getFilterTempStore();
    $temp_store->set('bundle', $value);
    $temp_store->set('label', trim($label ?? ''));
    $this->filter = $value;
    // If we apply any filters, we need to go to the first page again.
    $form_state->setRedirect('<current>');
  }

  /**
   * Form submission handler for resetting the filters.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function resetFilterForm(array &$form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\TempStore\PrivateTempStore $temp_store */
    $temp_store = $this->getFilterTempStore();
    $temp_store->delete('bundle');
    $temp_store->delete('label');
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $operation = $form_state->getValue('operation');
    $values = array_keys(array_filter($form_state->getValue(['table'])));
    $processed = FALSE;
    switch ($operation) {
      case 'debug_export':
        $this->createDebugExportBatch($values);
        $processed = TRUE;
        break;

      case 'upload':
        $this->createUploadBatch($values);
        $processed = TRUE;
        break;

      case 'force_reupload':
        $this->createForceReuploadBatch($values);
        $processed = TRUE;
        break;

      case 'check_upload':
        $this->createUploadCheckStatusBatch($values);
        $processed = TRUE;
        break;

      case 'download_translations':
        $this->createDownloadBatch($values);
        $processed = TRUE;
        break;

    }
    if (!$processed) {
      if (0 === strpos($operation, 'download_translation:')) {
        [$operation, $language] = explode(':', $operation);
        $this->createLanguageDownloadBatch($values, $language);
        $processed = TRUE;
      }
      if (0 === strpos($operation, 'change_profile:')) {
        [$operation, $profile_id] = explode(':', $operation);
        $this->createChangeProfileBatch($values, $profile_id);
        $processed = TRUE;
      }
    }
  }

  /**
   *
   */
  protected function getAllBundles() {
    $mappers = array_filter($this->mappers, function ($mapper) {
      // Filter config entity mappers and config field mappers.
      return ($mapper instanceof ConfigEntityMapper);
    });
    $bundles = [];
    foreach ($mappers as $bundle => $mapper) {
      /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
      $bundles[$bundle] = $mapper->getTypeLabel();
    }
    return $bundles;
  }

  /**
   * Performs an operation to several values in a batch.
   *
   * @param string $operation
   *   The method in this object we need to call.
   * @param array $values
   *   Array of ids to process.
   * @param string $title
   *   The title for the batch progress.
   * @param string $language
   *   (Optional) The language code for the request. NULL if is not applicable.
   */
  protected function createBatch($operation, $values, $title, $language = NULL) {
    $operations = $this->generateOperations($operation, $values, $language);
    $batch = [
      'title' => $title,
      'operations' => $operations,
      'finished' => [$this, 'batchFinished'],
      'progressive' => TRUE,
    ];
    batch_set($batch);
  }

  /**
   *
   */
  public function batchFinished($success, $results, $operations) {
    if ($success) {
      $this->messenger()->addStatus('Operations completed.');
    }
  }

  /**
   * Create and set an upload batch.
   *
   * @param array $values
   *   Array of ids to upload.
   */
  protected function createUploadBatch($values) {
    $this->createBatch('uploadDocument', $values, $this->t('Uploading content to Verify'), NULL);
  }

  /**
   * Create and set a force re-upload batch.
   *
   * @param array $values
   *   Array of ids to force re-upload.
   */
  protected function createForceReuploadBatch($values) {
    $this->createBatch('forceReuploadDocument', $values, $this->t('Forcefully re-uploading content to Verify'), NULL);
  }

  /**
   * Create and set an export batch.
   *
   * @param array $values
   *   Array of ids to upload.
   */
  protected function createDebugExportBatch($values) {
    $operations = $this->generateOperations('debugExport', $values, NULL);
    $batch = [
      'title' => $this->t('Exporting config entities (debugging purposes)'),
      'operations' => $operations,
      'finished' => [$this, 'debugExportFinished'],
      'progressive' => TRUE,
    ];
    batch_set($batch);
  }

  /**
   *
   */
  public function debugExportFinished($success, $results, $operations) {
    if ($success) {
      $links = [];
      foreach ($results['exported'] as $result) {
        $links[] = [
          '#theme' => 'file_link',
          '#file' => File::load($result),
        ];
      }
      $build = [
        '#theme' => 'item_list',
        '#items' => $links,
      ];
      $this->messenger()->addStatus($this->t('Exports available at: @exports',
        ['@exports' => \Drupal::service('renderer')->render($build)]));
    }
  }

  /**
   * Create and set a check upload status batch.
   *
   * @param array $values
   *   Array of ids to upload.
   */
  protected function createUploadCheckStatusBatch($values) {
    $this->createBatch('checkDocumentUploadStatus', $values, $this->t('Checking content upload status with Verify'));
  }

  /**
   * Create and set a check translation status batch for a given language.
   *
   * @param array $values
   *   Array of ids to upload.
   * @param string $language
   *   Language code for the request.
   */
  protected function createLanguageTranslationCheckStatusBatch($values, $language) {
    $this->createBatch('checkTranslationStatus', $values, $this->t('Checking translations status from Verify'), $language);
  }

  /**
   * Create and set a request target and download batch for all languages.
   *
   * @param array $values
   *   Array of ids to upload.
   */
  protected function createDownloadBatch($values) {
    $this->createBatch('downloadTranslations', $values, $this->t('Downloading translations from Verify'));
  }

  /**
   * Create and set a request target and download batch for a given language.
   *
   * @param array $values
   *   Array of ids to upload.
   * @param string $language
   *   Language code for the request.
   */
  protected function createLanguageDownloadBatch($values, $language) {
    $this->createBatch('downloadTranslation', $values, $this->t('Downloading translation to Verify'), $language);
  }


  /**
   * Create and set a profile change batch.
   *
   * @param array $values
   *   Array of ids to change the Profile.
   */
  protected function createChangeProfileBatch($values, $profile_id) {
    $this->createBatch('changeProfile', $values, $this->t('Updating Translation Profile'), $profile_id);
  }

  /**
   * Export source for debugging purposes.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   */
  public function debugExport(ConfigMapperInterface $mapper, $language, &$context) {
    $context['message'] = $this->t('Exporting %label.', ['%label' => $mapper->getTitle()]);
    /** @var \Drupal\config_translation\ConfigFieldMapper $mapper */
    $profile = ($mapper instanceof ConfigEntityMapper) ?
      $this->straker_translateConfiguration->getConfigEntityProfile($mapper->getEntity()) :
      $this->straker_translateConfiguration->getConfigProfile($mapper->getPluginId());

    $data = $this->translationService->getConfigSourceData($mapper);
    $data['_debug'] = [
      'title' => trim($mapper->getPluginId() . ' (config): ' . $mapper->getTitle()),
      'profile' => $profile ? $profile->id() : '<null>',
      'source_locale' => $this->translationService->getConfigSourceLocale($mapper),
    ];
    $filename = 'config.' . $mapper->getPluginId() . '.json';
    $plugin_definition = $mapper->getPluginDefinition();
    if (isset($plugin_definition['entity_type']) && 'field_config' === $plugin_definition['entity_type']) {
      $entity = $mapper->getEntity();
      $data['_debug']['title'] = $entity->id() . ' (config): ' . $entity->label();
      $filename = 'config.' . $entity->id() . '.json';
    }
    $source_data = json_encode($data);

    $file = File::create([
      'uid' => 1,
      'filename' => $filename,
      'uri' => 'temporary://' . $filename,
      'filemime' => 'text/plain',
      'created' => \Drupal::time()->getRequestTime(),
      'changed' => \Drupal::time()->getRequestTime(),
    ]);
    if ($file) {
      file_put_contents($file->getFileUri(), $source_data);
      $file->save();
      $context['results']['exported'][] = $file->id();
    }
  }

  /**
   * Upload source for translation.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   */
  public function uploadDocument(ConfigMapperInterface $mapper, $language, &$context) {
    $context['message'] = $this->t('Uploading %label.', ['%label' => $mapper->getTitle()]);
    /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
    $entity = $mapper instanceof ConfigEntityMapper ? $mapper->getEntity() : NULL;
    $document_id = $this->getDocumentId($this->translationService, $mapper, $entity);
    $profile = $this->getProfile($this->straker_translateConfiguration, $mapper, $entity);

    // If there is no entity, it's a config object and we don't abort based on
    // the profile.
    if ($entity === NULL || $profile !== NULL) {
      if ($mapper instanceof ConfigEntityMapper) {
        try {
          if ($this->translationService->uploadDocument($entity)) {
            $this->messenger()->addStatus($this->t('%label uploaded successfully', ['%label' => $entity->label()]));
          }
          else {
            $this->messenger()->addError($this->t('%label upload failed. Please try again. Check the logs for more details',
              ['%label' => $entity->label()]));
          }
        }
        catch (StrakerTranslateDocumentAlreadyUploaded $exception) {
          $this->messenger()->addWarning($this->t('Document @entity_type %title was already uploaded. Please check uploaded status.',[
            '@entity_type' => $entity->getEntityTypeId(),
            '%title' => $entity->label(),
            ]
          ));
        }
        catch (StrakerTranslateDocumentLockedException $exception) {
          $this->messenger()->addError(t('Document @entity_type %title has a new version. The document id has been updated for all future interactions. Please try again.', ['@entity_type' => $entity->getEntityTypeId(), '%title' => $entity->label()]));
        }
        catch (StrakerTranslateApiException $e) {
          if ($document_id) {
            $this->messenger()->addError($this->t('%label update failed. Please try again.',
              ['%label' => $entity->label()]));
          }
          else {
            $this->messenger()->addError($this->t('%label upload failed. Please try again.',
              ['%label' => $entity->label()]));
          }
        }
      }
      else {
        try {
          /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
          if ($this->translationService->uploadConfig($mapper->getPluginId())) {
            $this->messenger()->addStatus($this->t('%label uploaded successfully', ['%label' => $mapper->getPluginId()]));
          }
          else {
            $this->messenger()->addError($this->t('%label upload failed. Please try again. Check the logs for more details',
              ['%label' => $mapper->getPluginId()]));
          }
        }
        catch (StrakerTranslateDocumentAlreadyUploaded $exception) {
          $this->messenger()->addWarning($this->t('Document %label was already uploaded. Please check uploaded status.', [
            '%label' => $mapper->getTitle(),
          ]));
        }
        catch (StrakerTranslatePaymentRequiredException $exception) {
          $this->messenger()->addError(t('Community has been disabled. Please contact support@straker_translate.com to re-enable your community.'));
        }
        catch (StrakerTranslateDocumentLockedException $exception) {
          $this->messenger()->addError(t('Document %label has a new version. The document id has been updated for all future interactions. Please try again.',
            ['%label' => $mapper->getTitle()]));
        }
        catch (StrakerTranslateApiException $e) {
          if ($document_id) {
            $this->messenger()->addError($this->t('%label update failed. Please try again.',
              ['%label' => $mapper->getTitle()]));
          }
          else {
            $this->messenger()->addError($this->t('%label upload failed. Please try again.',
              ['%label' => $mapper->getTitle()]));
          }
        }
      }
    }
    else {
      $this->messenger()->addWarning($this->t('%label has no profile assigned so it was not processed.',
        ['%label' => $mapper->getTitle()]));
    }
  }

  /**
   * Force re-upload source for translation, ignoring existing document status.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   */
  public function forceReuploadDocument(ConfigMapperInterface $mapper, $language, &$context) {
    $context['message'] = $this->t('Forcefully re-uploading %label.', ['%label' => $mapper->getTitle()]);
    /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
    $entity = $mapper instanceof ConfigEntityMapper ? $mapper->getEntity() : NULL;
    $document_id = $this->getDocumentId($this->translationService, $mapper, $entity);
    $profile = $this->getProfile($this->straker_translateConfiguration, $mapper, $entity);

    // If there is no entity, it's a config object and we don't abort based on
    // the profile.
    if ($entity === NULL || $profile !== NULL) {
      if ($mapper instanceof ConfigEntityMapper) {
        try {
          // Force re-upload by calling uploadDocument with force parameter set to TRUE
          if ($this->translationService->uploadDocument($entity, TRUE)) {
            $this->messenger()->addStatus($this->t('@entity_type %title has been Re-uploaded successfully.', ['@entity_type' => ucfirst($entity->getEntityTypeId()), '%title' => $entity->label()]));
          }
          else {
            $this->messenger()->addError($this->t('The forceful upload for @entity_type %title failed. Check your configuration and profile and try again.', ['@entity_type' => $entity->getEntityTypeId(), '%title' => $entity->label()]));
          }
        }
        catch (StrakerTranslateDocumentAlreadyUploaded $exception) {
          // This catch block is kept for consistency but should ideally not be reached
          // if the force upload parameter works as expected.
          $this->messenger()->addWarning($this->t('Document @entity_type %title was already uploaded. Skipping forceful upload (via exception).', ['@entity_type' => $entity->getEntityTypeId(), '%title' => $entity->label()]));
        }
        catch (StrakerTranslatePaymentRequiredException $exception) {
          $this->messenger()->addError($this->t('Community has been disabled. Please contact support@straker_translate.com to re-enable your community.'));
        }
        catch (StrakerTranslateDocumentLockedException $exception) {
          $this->messenger()->addError($this->t('Document @entity_type %title has a new version. The document id has been updated for all future interactions. Please try again.', ['@entity_type' => $entity->getEntityTypeId(), '%title' => $entity->label()]));
        }
        catch (StrakerTranslateApiException $e) {
          if ($this->translationService->getDocumentId($entity)) {
            $this->messenger()->addError($this->t('The forceful update for @entity_type %title failed. Please try again.', ['@entity_type' => $entity->getEntityTypeId(), '%title' => $entity->label()]));
          }
          else {
            $this->messenger()->addError($this->t('The forceful upload for @entity_type %title failed. Please try again.', ['@entity_type' => $entity->getEntityTypeId(), '%title' => $entity->label()]));
          }
        }
      }
      else {
        try {
          /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
          // Force re-upload by calling uploadConfig with force parameter set to TRUE
          if ($this->translationService->uploadConfig($mapper->getPluginId(), TRUE)) {
            $this->messenger()->addStatus($this->t('%label has been Re-uploaded successfully.', ['%label' => $mapper->getTitle()]));
          }
          else {
            $this->messenger()->addError($this->t('The forceful upload for %label failed. Check your configuration and profile and try again.', ['%label' => $mapper->getTitle()]));
          }
        }
        catch (StrakerTranslateDocumentAlreadyUploaded $exception) {
          // This catch block is kept for consistency but should ideally not be reached
          // if the force upload parameter works as expected.
          $this->messenger()->addWarning($this->t('Document %label was already uploaded. Skipping forceful upload (via exception).', ['%label' => $mapper->getTitle()]));
        }
        catch (StrakerTranslatePaymentRequiredException $exception) {
          $this->messenger()->addError($this->t('Community has been disabled. Please contact support@straker_translate.com to re-enable your community.'));
        }
        catch (StrakerTranslateDocumentLockedException $exception) {
          $this->messenger()->addError($this->t('Document %label has a new version. The document id has been updated for all future interactions. Please try again.', ['%label' => $mapper->getTitle()]));
        }
        catch (StrakerTranslateApiException $e) {
          if ($this->translationService->getConfigDocumentId($mapper)) {
            $this->messenger()->addError($this->t('The forceful update for %label failed. Please try again.', ['%label' => $mapper->getTitle()]));
          }
          else {
            $this->messenger()->addError($this->t('The forceful upload for %label failed. Please try again.', ['%label' => $mapper->getTitle()]));
          }
        }
      }
    }
    else {
      $this->messenger()->addWarning($this->t('%label has no profile assigned so it was not processed.',
        ['%label' => $mapper->getTitle()]));
    }
  }

  /**
   * Check document upload status for a given content.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   */
  public function checkDocumentUploadStatus(ConfigMapperInterface $mapper, $language, &$context) {
    $context['message'] = $this->t('Checking status of %label.', ['%label' => $mapper->getTitle()]);
    $entity = $mapper instanceof ConfigEntityMapper ? $mapper->getEntity() : NULL;
    $profile = $mapper instanceof ConfigEntityMapper ?
      $this->straker_translateConfiguration->getConfigEntityProfile($entity, FALSE)
      : NULL;
    if (empty($profile)) {
      try {
        /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
        $profile = $this->straker_translateConfiguration->getConfigProfile($mapper->getPluginId(), FALSE);
      }
      catch (\Exception $e) {

      }
    }
    // If there is no entity, it's a config object and we don't abort based on
    // the profile.
    if ($entity === NULL || $profile !== NULL) {
      if ($mapper instanceof ConfigEntityMapper) {
        try {
          $status = $this->translationService->checkSourceStatus($entity);
          if ($status) {
            $this->messenger()->addStatus($this->t('The %label translation process is completed, Now ready for download.', [
              '%label' => $entity->label(),
            ]));
          }
          else {
            $this->messenger()->addWarning($this->t('The %label translation process failed or skipped. Try again after some time.', [
              '%label' => $entity->label(),
            ]));
          }
        }
        catch (StrakerTranslateApiException $e) {
          $this->messenger()->addError($this->t('%label upload failed. Please try again.',
            ['%label' => $entity->label()]));
        }
      }
      else {
        try {
          /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
          $status = $this->translationService->checkConfigSourceStatus($mapper->getPluginId());
          if ($status) {
            $this->messenger()->addStatus($this->t('The %label translation process is completed, Now ready for download.', [
              '%label' => $mapper->getTitle(),
            ]));
          }
          else {
            $this->messenger()->addWarning($this->t('The %label translation process failed or skipped. Try again after some time.', [
              '%label' => $mapper->getTitle(),
            ]));
          }
        }
        catch (StrakerTranslateApiException $e) {
          $this->messenger()->addError($this->t('%label check status failed. Please try again.',
            ['%label' => $mapper->getTitle()]));
        }
      }
    }
    else {
      $this->messenger()->addWarning($this->t('%label has no profile assigned so it was not processed.',
        ['%label' => $mapper->getTitle()]));
    }
  }

  /**
   * Download translation for a given content in a given language.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   * @param string $langcode
   *   The language to download.
   */
  public function downloadTranslation(ConfigMapperInterface $mapper, $langcode, &$context) {
    $context['message'] = $this->t('Downloading translation for %label in language @language.', ['%label' => $mapper->getTitle(), '@language' => $langcode]);
    $entity = $mapper instanceof ConfigEntityMapper ? $mapper->getEntity() : NULL;
    $profile = $this->getProfile($this->straker_translateConfiguration, $mapper, $entity);

    // If there is no entity, it's a config object and we don't abort based on
    // the profile.
    if ($entity === NULL || $profile !== NULL) {
      $this->performTranslationDownload($mapper, $entity, $langcode);
    }
    else {
      $this->messenger()->addWarning($this->t('%label has no profile assigned so it was not processed.',
        ['%label' => $mapper->getTitle()]));
    }
  }

  /**
   * Download translations for a given content in all enabled languages.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   */
  public function downloadTranslations(ConfigMapperInterface $mapper, $langcode, &$context) {
    $context['message'] = $this->t('Downloading all translations for %label.', ['%label' => $mapper->getTitle()]);
    $entity = $mapper instanceof ConfigEntityMapper ? $mapper->getEntity() : NULL;
    $profile = $this->getProfile($this->straker_translateConfiguration, $mapper, $entity);

    // If there is no entity, it's a config object and we don't abort based on
    // the profile.
    if ($entity === NULL || $profile !== NULL) {
      $languages = $this->straker_translateConfiguration->getEnabledLanguages();
      foreach ($languages as $langcode => $language) {
        if ($langcode !== $mapper->getLangcode()) {
          $this->performTranslationDownload($mapper, $entity, $langcode);
        }
      }
    }
    else {
      $this->messenger()->addWarning($this->t('%label has no profile assigned so it was not processed.',
        ['%label' => $mapper->getTitle()]));
    }
  }

  /**
   * Change Translation Profile.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   */
  public function changeProfile(ConfigMapperInterface $mapper, $profile_id = NULL, &$context = NULL) {
    $context['message'] = $this->t('Changing Translation Profile for @type %label.', [
      '@type' => $mapper->getTypeLabel(),
      '%label' => $mapper->getTitle(),
    ]);
    try {
      /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
      $entity = ($mapper instanceof ConfigEntityMapper) ? $mapper->getEntity() : NULL;
      if ($mapper instanceof ConfigEntityMapper) {
        $this->straker_translateConfiguration->setConfigEntityProfile($entity, $profile_id);
      }
      else {
        /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
        $this->straker_translateConfiguration->setConfigProfile($mapper->getPluginId(), $profile_id);
      }
    }
    catch (StrakerTranslateApiException $exception) {
      $this->messenger()->addError(t('The Translation Profile change for %title failed. Please try again.', ['%title' => $mapper->getTitle()]));
    }
    if ($profile_id === StrakerTranslate::PROFILE_DISABLED) {
      /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
      $entity = ($mapper instanceof ConfigEntityMapper) ? $mapper->getEntity() : NULL;
      if ($mapper instanceof ConfigEntityMapper) {
        $this->translationService->setSourceStatus($entity, StrakerTranslate::STATUS_DISABLED);
        $this->translationService->setTargetStatuses($entity, StrakerTranslate::STATUS_DISABLED);
      }
      else {
        $this->translationService->setConfigTargetStatuses($mapper, StrakerTranslate::STATUS_DISABLED);
        $this->translationService->setConfigSourceStatus($mapper, StrakerTranslate::STATUS_DISABLED);
      }
    }
    else {
      if ($mapper instanceof ConfigEntityMapper) {
        $entity = $mapper->getEntity();
        if ($this->translationService->getSourceStatus($entity) == StrakerTranslate::STATUS_DISABLED) {
          if ($this->translationService->getDocumentId($entity) !== NULL) {
            $this->translationService->setSourceStatus($entity, StrakerTranslate::STATUS_CURRENT);
          }
          else {
            $this->translationService->setSourceStatus($entity, StrakerTranslate::STATUS_CURRENT);
          }
          if ($this->translationService->getDocumentId($entity)) {
            $this->translationService->checkTargetStatuses($entity);
          }
        }
      }
      else {
        if ($this->translationService->getConfigSourceStatus($mapper) == StrakerTranslate::STATUS_DISABLED) {
          if ($this->translationService->getConfigDocumentId($mapper) !== NULL) {
            $this->translationService->setConfigSourceStatus($mapper, StrakerTranslate::STATUS_CURRENT);
          }
          else {
            $this->translationService->setConfigSourceStatus($mapper, StrakerTranslate::STATUS_CURRENT);
          }
          if ($this->translationService->getConfigDocumentId($mapper)) {
            $this->translationService->checkConfigTargetStatuses($mapper->getPluginId());
          }
        }
      }
    }
  }

  /**
   *
   */
  protected function getTargetStatusText(ConfigMapperInterface $mapper, $status, $langcode) {
    $language = ConfigurableLanguage::load($langcode);
    if ($language) {
      switch ($status) {
        case StrakerTranslate::STATUS_UNTRACKED:
          return $language->label() . ' ' . $this->t('translation not started');

        case StrakerTranslate::STATUS_PENDING:
          return $language->label() . ' ' . $this->t('translation is pending');

        case StrakerTranslate::STATUS_READY:
          return $language->label() . ' ' . $this->t('translation is ready for download');

        case StrakerTranslate::STATUS_CURRENT:
          return $language->label() . ' ' . $this->t('translation is complete and up-to-date');

        case StrakerTranslate::STATUS_EDITED:
          return $language->label() . ' ' . $this->t('translation is not current');

        case StrakerTranslate::STATUS_INTERMEDIATE:
          return $language->label() . ' ' . $this->t('translation in-progress (interim translation downloaded)');

        case StrakerTranslate::STATUS_ERROR:
          return $language->label() . ' ' . $this->t('Error');

        case StrakerTranslate::STATUS_CANCELLED:
          return $language->label() . ' ' . $this->t('cancelled by user');

        case StrakerTranslate::STATUS_DELETED:
          if ($mapper->hasTranslation($language)) {
            return $language->label() . ' ' . $this->t('was deleted in Verify and the translation exists.');
          }
          return $language->label() . ' ' . $this->t('was deleted in Verify and the translation does not exist.');

        case StrakerTranslate::STATUS_ARCHIVED:
          if ($mapper->hasTranslation($language)) {
            return $language->label() . ' ' . $this->t('was archived in Verify and the translation exists.');
          }
          return $language->label() . ' ' . $this->t('was archived in Verify and the translation does not exist.');

        default:
          return $language->label() . ' ' . ucfirst(strtolower($status));
      }
    }
  }

  /**
   *
   */
  protected function getActionUrlArguments(ConfigMapperInterface &$mapper) {
    /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
    $config_mapper = $mapper;
    $args = [
      'entity_type' => $config_mapper->getPluginId(),
      'entity_id' => $config_mapper->getPluginId(),
    ];
    if ($mapper instanceof ConfigEntityMapper && !$mapper instanceof ConfigFieldMapper) {
      $args['entity_id'] = $mapper->getEntity()->id();
    }
    elseif ($mapper instanceof ConfigFieldMapper) {
      $args['entity_type'] = $mapper->getType();
      $args['entity_id'] = $mapper->getEntity()->id();
    }
    return $args;
  }

  /**
   * Gets the translation status of an entity in a format ready to displayS.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   *
   * @return array
   *   A render array.
   */
  protected function getTranslationsStatuses(ConfigMapperInterface &$mapper) {
    $is_config_entity = $mapper instanceof ConfigEntityMapper;
    $translations = [];
    $languages = $this->straker_translateConfiguration->getEnabledLanguages();

    $document_id = $is_config_entity ?
      $this->translationService->getDocumentId($mapper->getEntity()) :
      $this->translationService->getConfigDocumentId($mapper);
    $entity = $is_config_entity ? $mapper->getEntity() : NULL;

    $translations_statuses = $mapper instanceof ConfigEntityMapper ?
      $this->translationService->getTargetStatuses($entity) :
      $this->translationService->getConfigTargetStatuses($mapper);

    $profile = $this->getProfile($this->straker_translateConfiguration, $mapper, $entity);

    array_walk($translations_statuses, function (&$status, $langcode) use ($profile) {
      if ($profile !== NULL && $profile->hasDisabledTarget($langcode)) {
        $status = StrakerTranslate::STATUS_DISABLED;
      }
    });

    foreach ($languages as $langcode => $language) {
      // Show the untracked translations in the bulk management form, unless it's the
      // source one.
      if ($mapper->hasTranslation($language) && $mapper->getLangcode() !== $langcode) {
        $translations[$langcode] = [
          'status' => StrakerTranslate::STATUS_UNTRACKED,
          'url' => NULL,
          'new_window' => FALSE,
        ];
      }
    }

    foreach ($translations_statuses as $langcode => $status) {
      if (isset($languages[$langcode]) && $langcode !== $mapper->getLangcode() && array_key_exists($langcode, $languages)) {
        if ($mapper->hasTranslation($languages[$langcode]) && $status == StrakerTranslate::STATUS_REQUEST) {
          $translations[$langcode] = [
            'status' => StrakerTranslate::STATUS_UNTRACKED,
            'url' => $this->getTargetActionUrl($mapper, StrakerTranslate::STATUS_UNTRACKED, $langcode),
            'new_window' => $status == StrakerTranslate::STATUS_CURRENT,
          ];
        }
        else {
          $translations[$langcode] = [
            'status' => $status,
            'url' => $this->getTargetActionUrl($mapper, $status, $langcode),
            'new_window' => $status == StrakerTranslate::STATUS_CURRENT,
          ];
        }
      }
    }
    array_walk($languages, function ($language, $langcode) use ($document_id, $mapper, &$translations, $profile) {
      if ($document_id && !isset($translations[$langcode]) && $langcode !== $mapper->getLangcode()) {
        if ($profile !== NULL && $profile->hasDisabledTarget($langcode)) {
          $translations[$langcode] = [
            'status' => StrakerTranslate::STATUS_DISABLED,
            'url' => NULL,
            'new_window' => FALSE,
          ];
        }
        else {
          $translations[$langcode] = [
            'status' => StrakerTranslate::STATUS_REQUEST,
            'url' => $this->getTargetActionUrl($mapper, StrakerTranslate::STATUS_REQUEST, $langcode),
            'new_window' => FALSE,
          ];
        }
      }
    });
    ksort($translations);
    return $this->formatTranslations($mapper, $translations);
  }

  /**
   * Formats the translation statuses for display.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   * @param array $translations
   *   Pairs of language - status.
   *
   * @return array
   *   A render array.
   */
  protected function formatTranslations(ConfigMapperInterface $mapper, array $translations) {
    // Get languages in weight order instead of relying on $translations array order
    $languages = $this->straker_translateConfiguration->getEnabledLanguages();
    $language_items = [];

    // Iterate through languages in weight order, only include those that have translations
    foreach ($languages as $langcode => $language) {
      if (isset($translations[$langcode])) {
        $language_items[] = '<div class="config-translation-language-item">' . $language->getName() . '</div>';
      }
    }

    return [
      'data' => [
        '#markup' => '<div class="config-translation-languages-wrapper">' . implode('', $language_items) . '</div>',
      ],
    ];
  }

  /**
   * Get the bulk operations for the management form.
   *
   * @return array
   *   Array with the bulk operations.
   */
  public function generateBulkOptions() {
    $operations = [];
    $operations['upload'] = $this->t('Upload source for translation');
    $operations['force_reupload'] = $this->t('Re-upload document forcefully');
    $operations['check_upload'] = $this->t('Check translation status');
    $operations[(string) $this->t('Download')]['download_translations'] = $this->t('Download all translations');

    $target_languages = $this->straker_translateConfiguration->getEnabledLanguages();
    foreach ($target_languages as $langcode => $language) {
      $operations[(string) $this->t('Download')]['download_translation:' . $langcode] = $this->t('Download @language translation', ['@language' => $language->getName() . ' (' . $language->getId() . ')']);
    }
    foreach ($this->straker_translateConfiguration->getProfileOptions() as $profile_id => $profile) {
      $operations[(string) $this->t('Change Translation Profile')]['change_profile:' . $profile_id] = $this->t('Change to @profile Profile', ['@profile' => $profile]);
    }
    $debug_enabled = \Drupal::state()->get('straker_translate.enable_debug_utilities', FALSE);
    if ($debug_enabled) {
      $operations[(string) $this->t('Debug')]['debug_export'] = $this->t('Debug: Export sources as JSON');
    }

    return $operations;
  }

  /**
   *
   */
  protected function getTargetActionUrl(ConfigMapperInterface &$mapper, $target_status, $langcode) {
    $url = NULL;
    $args = $this->getActionUrlArguments($mapper);

    $document_id = $mapper instanceof ConfigEntityMapper ?
      $this->translationService->getDocumentId($mapper->getEntity()) :
      $this->translationService->getConfigDocumentId($mapper);

    $locale = $this->languageLocaleMapper->getLocaleForLangcode($langcode);
    if ($locale) {
      if ($target_status == StrakerTranslate::STATUS_READY || $target_status == StrakerTranslate::STATUS_ERROR) {
        $file_id = $mapper instanceof ConfigEntityMapper ?
            $this->translationService->getTargetFileId($mapper->getEntity(), $langcode) :
            $this->translationService->getConfigTargetFileId($mapper, $langcode);
        $args['langcode'] = $langcode;
        if ($file_id) {
          $args['file_id'] = $file_id;
          $url = Url::fromRoute('straker_translate.config.download',
            $args,
            ['query' => $this->getDestinationArray()]);
        }
      }
      if ($target_status == StrakerTranslate::STATUS_CURRENT ||
          $target_status == StrakerTranslate::STATUS_INTERMEDIATE ||
          $target_status == StrakerTranslate::STATUS_EDITED) {
        $url = Url::fromRoute('straker_translate.workbench', [
          'doc_id' => $document_id,
        ]);
      }
    }
    return $url;
  }

  /**
   * Generates an array of operations to be performed in a batch.
   *
   * @param string $operation
   *   The operation (method of this object) to be executed.
   * @param array $values
   *   The mappers this operation will be applied to.
   * @param $language
   *   The language to be passed to that operation.
   *
   * @return array
   *   An array of operations suitable for a batch.
   */
  protected function generateOperations($operation, $values, $language) {
    $operations = [];

    $mappers = $this->getSelectedMappers($values);

    foreach ($mappers as $mapper) {
      $operations[] = [[$this, $operation], [$mapper, $language]];
    }
    return $operations;
  }

  /**
   * Actually performs the translation download.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper to be used.
   * @param $entity
   *   The entity (in case it is a config entity mapper).
   * @param $langcode
   *   The langauge code to be downloaded.
   */
  protected function performTranslationDownload(ConfigMapperInterface $mapper, $entity, $langcode) {
    if ($mapper instanceof ConfigEntityMapper) {
      try {
        $file_id = $this->translationService->getTargetFileId($entity, $langcode);
        $success = $this->translationService->downloadDocument($entity, $file_id, $langcode);
        if ($success === FALSE) {
          $this->messenger()->addError($this->t('%label @langcode translation download failed. Please try again.',
            ['%label' => $entity->label(), '@langcode' => $langcode]));
        }
      }
      catch (StrakerTranslateApiException $e) {
        $this->messenger()->addError($this->t('%label @langcode translation download failed. Please try again.',
          ['%label' => $entity->label(), '@langcode' => $langcode]));
      }
    }
    else {
      try {
        /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
        $config_mapper = $mapper;
        $file_id = $this->translationService->getConfigTargetFileId($config_mapper, $langcode);
        $success = $this->translationService->downloadConfig($config_mapper->getPluginId(), $file_id, $langcode);
        if ($success === FALSE) {
          $this->messenger()->addError($this->t('%label @langcode translation download failed. Please try again.',
            ['%label' => $config_mapper->getTitle(), '@langcode' => $langcode]));
        }
      }
      catch (StrakerTranslateApiException $e) {
        $this->messenger()->addError($this->t('%label @langcode translation download failed. Please try again.',
          ['%label' => $mapper->getTitle(), '@langcode' => $langcode]));
      }
      catch (StrakerTranslateDocumentNotFoundException $e) {
        $this->messenger()->addError($this->t('%label @langcode translation download failed. Could not find document. Please try again.',
        ['%label' => $mapper->getTitle(), '@langcode' => $langcode]));
      }
    }
  }

  /**
   * Gets the select mappers from their IDs.
   *
   * @param $values
   *   Array of ids.
   *
   * @return \Drupal\config_translation\ConfigNamesMapper[]
   *   The mappers.
   */
  protected function getSelectedMappers($values) {
    $mappers = [];
    if ($this->filter === 'config') {
      foreach ($values as $value) {
        $mappers[$value] = $this->mappers[$value];
      }
    }
    elseif (substr($this->filter, -7) == '_fields') {
      $mapper = $this->mappers[$this->filter];
      $ids = \Drupal::entityQuery('field_config')
        ->accessCheck(FALSE)
        ->condition('id', $values)
        ->execute();
      $fields = FieldConfig::loadMultiple($ids);
      $mappers = [];
      foreach ($fields as $id => $field) {
        /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
        $new_mapper = clone $mapper;
        $new_mapper->setEntity($field);
        $mappers[$field->id()] = $new_mapper;
      }
    }
    else {
      $entities = \Drupal::entityTypeManager()
        ->getStorage($this->filter)
        ->loadMultiple($values);
      foreach ($entities as $entity) {
        /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
        $mapper = clone $this->mappers[$this->filter];
        $mapper->setEntity($entity);
        $mappers[$entity->id()] = $mapper;
      }
    }
    return $mappers;
  }

  /**
   *
   */
  protected function getDestinationWithQueryArray() {
    return ['destination' => \Drupal::request()->getRequestUri()];
  }

  /**
   *
   */
  protected function getFilterTempStore() {
    return $this->tempStoreFactory->get('straker_translate.config_management.filter');
  }

  /**
   * Tries to determine the correct profile for a given mapper and entity.
   *
   * @param Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface $straker_translateConfiguration
   *
   * @param object $mapper
   *
   * @param Drupal\Core\Config\Entity\ConfigEntityInterface|null $entity
   *   The entity.
   *
   * @return \Drupal\straker_translate\StrakerTranslateProfileInterface|null
   */
  private function getProfile(StrakerTranslateConfigurationServiceInterface $straker_translateConfiguration, object $mapper, ConfigEntityInterface|null $entity): StrakerTranslateProfileInterface|null {
    if (!empty($entity)) {
      $profile = $mapper instanceof ConfigEntityMapper ? $straker_translateConfiguration->getConfigEntityProfile($entity) : NULL;
    }
    if (empty($profile)) {
      /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
      $profile = $straker_translateConfiguration->getConfigProfile($mapper->getPluginId());
    }
    /** @var \Drupal\straker_translate\StrakerTranslateProfileInterface $profile */
    return $profile;
  }

  /**
   * Tries to determine the correct document ID for a given mapper and entity.
   *
   * @param \Drupal\straker_translate\StrakerTranslateConfigTranslationServiceInterface $translationService
   * @param object $mapper
   *
   * @param \Drupal\Core\Config\Entity\ConfigEntityInterface|null $entity
   *   The entity.
   *
   * @return string|null
   */
  /**
   * Get the tooltip text for project status based on the source status.
   *
   * @param string $source_status
   *   The source status.
   * @param bool $is_config_entity
   *   Whether this is a config entity.
   * @param mixed $mapper_or_entity
   *   The mapper or entity object.
   * @param string $document_id
   *   The document ID if available.
   *
   * @return string
   *   The tooltip text.
   */
  protected function getProjectStatusTooltip($source_status, $is_config_entity, $mapper_or_entity, $document_id) {
    // If no document ID and status is REQUEST, show upload message
    if (!$document_id && ($source_status == 'REQUEST' || $source_status == 'UNTRACKED')) {
      return $this->t('Upload to start translation project');
    }

    switch ($source_status) {
      case 'UNTRACKED':
      case 'REQUEST':
        return $this->t('Upload');

      case 'DISABLED':
        return $this->t('Disabled, cannot request translation');

      case 'READY':
        return $this->t('Project Created, Check Status Pending');

      case 'EDITED':
        return $document_id ?
          $this->t('Re-upload (content has changed since last upload)') : $this->t('Upload');

      case 'IMPORTING':
        return $this->t('Token payment pending');

      case 'PROCESSING':
        return $this->t('Project created, Translation in progress');

      case 'CURRENT':
        return $this->t('Source Translated');

      case 'ERROR':
        return $this->t('Error');

      case 'CANCELLED':
        return $this->t('Cancelled by user');

      case 'ARCHIVED':
        return $this->t('This document was archived in Verify. Re-upload to translate.');

      case 'DELETED':
        return $this->t('This document was deleted in Verify. Re-upload to translate.');

      case 'PENDING':
        return $this->t('Translation requested but not started yet');

      case 'NONE':
        return $this->t('No status available');

      default:
        return $this->t('Unknown status');
    }
  }

  /**
   * Get the target status content for the Target Status column.
   *
   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
   *   The mapper.
   *
   * @return array
   *   A render array.
   */
  protected function getTargetStatusContent(ConfigMapperInterface &$mapper) {
    $is_config_entity = $mapper instanceof ConfigEntityMapper;
    $languages = $this->straker_translateConfiguration->getEnabledLanguages();

    $document_id = $is_config_entity ?
      $this->translationService->getDocumentId($mapper->getEntity()) :
      $this->translationService->getConfigDocumentId($mapper);
    $entity = $is_config_entity ? $mapper->getEntity() : NULL;

    $translations_statuses = $mapper instanceof ConfigEntityMapper ?
      $this->translationService->getTargetStatuses($entity) :
      $this->translationService->getConfigTargetStatuses($mapper);

    $profile = $this->getProfile($this->straker_translateConfiguration, $mapper, $entity);

    array_walk($translations_statuses, function (&$status, $langcode) use ($profile) {
      if ($profile !== NULL && $profile->hasDisabledTarget($langcode)) {
        $status = StrakerTranslate::STATUS_DISABLED;
      }
    });

    $target_status_html = '';
    $query_options = ['query' => ['destination' => \Drupal::request()->getRequestUri()]];

    foreach ($languages as $langcode => $language) {
      if ($langcode !== $mapper->getLangcode()) {
        $status = isset($translations_statuses[$langcode]) ? $translations_statuses[$langcode] : StrakerTranslate::STATUS_REQUEST;

        // If there's a translation but status is REQUEST, it's UNTRACKED
        if ($mapper->hasTranslation($language) && $status == StrakerTranslate::STATUS_REQUEST) {
          $status = StrakerTranslate::STATUS_UNTRACKED;
        }

        // Skip if no document and status is REQUEST (not tracked)
        if (!$document_id && $status == StrakerTranslate::STATUS_REQUEST) {
          continue;
        }

        // Handle "New Language" case: document exists but target status is UNTRACKED or REQUEST
        // This indicates the language was added after the document was uploaded
        if ($document_id && ($status == StrakerTranslate::STATUS_UNTRACKED || $status == StrakerTranslate::STATUS_REQUEST)) {
          $status_text = $this->t('New Language');
          // Use a special CSS class for "New Language" display
          $css_class = 'target-newlanguage';
          $tooltip = $this->t('@language was added after document upload', ['@language' => $language->getName()]);
        } else {
          $status_text = $this->getTargetStatusShortText($status, $langcode);
          $css_class = $this->getTargetStatusCssClass($status);
          $tooltip = $this->getTargetStatusText($mapper, $status, $langcode);
        }
        $actions = $this->getTargetStatusActions($mapper, $status, $langcode, $query_options);

        $wrapper_class = 'target-status-pill-wrapper';
        $dropdown_html = '';

        if (!empty($actions)) {
          $wrapper_class .= ' has-dropdown';
          $dropdown_html = '<span class="target-status-dropdown-toggle"></span>';
          $dropdown_id = 'target-status-' . $mapper->getPluginId() . '-' . $langcode;
          $dropdown_html .= '<ul class="target-status-dropdown-menu" id="' . $dropdown_id . '">';
          foreach ($actions as $action) {
            $target = isset($action['new_window']) && $action['new_window'] ? ' target="_blank"' : '';
            $dropdown_html .= '<li><a href="' . $action['url']->toString() . '"' . $target . '>' . $action['title'] . '</a></li>';
          }
          $dropdown_html .= '</ul>';
        }

        $target_status_html .= '<div class="' . $wrapper_class . '">';
        $target_status_html .= '<span class="language-icon ' . $css_class . '" title="' . $tooltip . '">';
        $target_status_html .= $status_text . $dropdown_html;
        $target_status_html .= '</span></div>';
      }
    }

    if (empty($target_status_html)) {
      $target_status_html = '<span class="target-status-none">' . $this->t('No targets') . '</span>';
    }

    return [
      'data' => [
        '#markup' => $target_status_html,
        '#attached' => [
          'library' => ['straker_translate/base', 'straker_translate/target_status_dropdown'],
        ],
      ],
    ];
  }

  /**
   * Get short status text for target status pills.
   */
  protected function getTargetStatusShortText($status, $langcode) {
    $language = ConfigurableLanguage::load($langcode);
    $lang_code = $language ? strtoupper($language->getId()) : strtoupper($langcode);

    switch ($status) {
      case StrakerTranslate::STATUS_CURRENT:
        return $this->t('Synced with Verify');
      case StrakerTranslate::STATUS_READY:
        return $this->t('Ready for Download');
      case StrakerTranslate::STATUS_PENDING:
        return $this->t('Pending');
      case StrakerTranslate::STATUS_ERROR:
        return $this->t('Error');
      case StrakerTranslate::STATUS_UNTRACKED:
        return $this->t('Not Started');
      case StrakerTranslate::STATUS_DISABLED:
        return $this->t('Disabled');
      case StrakerTranslate::STATUS_PROCESSING:
        return $this->t('Processing');
      case StrakerTranslate::STATUS_INTERMEDIATE:
        return $this->t('Intermediate');
      case StrakerTranslate::STATUS_CANCELLED:
        return $this->t('Cancelled');
      case StrakerTranslate::STATUS_DELETED:
        return $this->t('Deleted');
      case StrakerTranslate::STATUS_ARCHIVED:
        return $this->t('Archived');
      case StrakerTranslate::STATUS_REQUEST:
        return $this->t('Request');
      default:
        return ucfirst(strtolower($status));
    }
  }

  /**
   * Get CSS class for target status.
   */
  protected function getTargetStatusCssClass($status) {
    switch ($status) {
      case StrakerTranslate::STATUS_CURRENT:
        return 'target-current';
      case StrakerTranslate::STATUS_PENDING:
        return 'target-pending';
      case StrakerTranslate::STATUS_READY:
        return 'target-ready';
      case StrakerTranslate::STATUS_REQUEST:
        return 'target-request';
      case StrakerTranslate::STATUS_UNTRACKED:
        return 'target-untracked';
      case StrakerTranslate::STATUS_PROCESSING:
        return 'target-processing';
      case StrakerTranslate::STATUS_INTERMEDIATE:
        return 'target-intermediate';
      case StrakerTranslate::STATUS_ERROR:
        return 'target-error';
      case StrakerTranslate::STATUS_CANCELLED:
        return 'target-cancelled';
      case StrakerTranslate::STATUS_DISABLED:
        return 'target-disabled';
      case StrakerTranslate::STATUS_DELETED:
        return 'target-deleted';
      case StrakerTranslate::STATUS_ARCHIVED:
        return 'target-archived';
      case 'NEWLANGUAGE':
        return 'target-newlanguage';
      default:
        return 'target-unknown';
    }
  }

  /**
   * Get actions for target status dropdown.
   */
  protected function getTargetStatusActions(ConfigMapperInterface &$mapper, $status, $langcode, $query_options) {
    $actions = [];
    $document_id = $mapper instanceof ConfigEntityMapper ?
      $this->translationService->getDocumentId($mapper->getEntity()) :
      $this->translationService->getConfigDocumentId($mapper);

    $locale = $this->languageLocaleMapper->getLocaleForLangcode($langcode);
    if ($locale) {
      switch ($status) {
        case StrakerTranslate::STATUS_READY:
        case StrakerTranslate::STATUS_ERROR:
          if ($document_id) {
            $file_id = $mapper instanceof ConfigEntityMapper ?
              $this->translationService->getTargetFileId($mapper->getEntity(), $langcode) :
              $this->translationService->getConfigTargetFileId($mapper, $langcode);

            if ($file_id) {
              $actions[] = [
                'title' => $this->t('Download translation'),
                'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.download', [
                  'entity_type' => $mapper instanceof ConfigEntityMapper ? $mapper->getEntity()->getEntityTypeId() : $mapper->getPluginId(),
                  'entity_id' => $mapper instanceof ConfigEntityMapper ? $mapper->getEntity()->id() : $mapper->getPluginId(),
                  'file_id' => $file_id,
                  'langcode' => $langcode,
                ], $query_options),
                'new_window' => FALSE,
              ];
            }

            // Add "Open in Verify" option - matching the existing Translations column implementation
            $actions[] = [
              'title' => $this->t('Open in Verify'),
              'url' => \Drupal\Core\Url::fromRoute('straker_translate.workbench', [
                'doc_id' => $document_id,
              ]),
              'new_window' => TRUE,
            ];
          }
          break;

        case StrakerTranslate::STATUS_CURRENT:
        case StrakerTranslate::STATUS_INTERMEDIATE:
        case StrakerTranslate::STATUS_EDITED:
          if ($document_id) {
            $actions[] = [
              'title' => $this->t('Open in Verify'),
              'url' => \Drupal\Core\Url::fromRoute('straker_translate.workbench', [
                'doc_id' => $document_id,
              ]),
              'new_window' => TRUE,
            ];
          }
          break;
      }
    }

    return $actions;
  }

  /**
   * Get secondary actions for Project Status dropdown (matching source column actions).
   */
  protected function getProjectStatusSecondaryActions(ConfigMapperInterface &$mapper, $source_status, $document_id, $query_options) {
    $actions = [];
    $is_config_entity = $mapper instanceof ConfigEntityMapper;

    // Add Open in Verify for CURRENT and EDITED statuses (shown at top like source column)
    if (in_array($source_status, [StrakerTranslate::STATUS_CURRENT, StrakerTranslate::STATUS_EDITED]) && $document_id) {
      $actions[] = [
        'title' => $this->t('Open in Verify'),
        'url' => \Drupal\Core\Url::fromRoute('straker_translate.workbench', [
          'doc_id' => $document_id,
        ]),
        'new_window' => TRUE,
      ];
    }

    // Add Re-upload document for all statuses except "Not Started" (REQUEST/UNTRACKED)
    // Only show when document exists
    if ($document_id && !in_array($source_status, [StrakerTranslate::STATUS_REQUEST, StrakerTranslate::STATUS_UNTRACKED])) {
      $reupload_query_options = $query_options;
      $reupload_query_options['query']['reupload'] = '1';
      $actions[] = [
        'title' => $this->t('Re-upload document'),
        'url' => \Drupal\Core\Url::fromRoute('straker_translate.config.update', [
          'entity_type' => $is_config_entity ? $mapper->getEntity()->getEntityTypeId() : $mapper->getPluginId(),
          'entity_id' => $is_config_entity ? $mapper->getEntity()->id() : $mapper->getPluginId(),
        ], $reupload_query_options),
        'new_window' => FALSE,
      ];
    }

    return $actions;
  }

  public function getDocumentId(StrakerTranslateConfigTranslationServiceInterface $translationService, object $mapper, ConfigEntityInterface|null $entity): ?string {
    $document_id = $mapper instanceof ConfigEntityMapper ? $this->translationService->getDocumentId($entity) : NULL;
    if (empty($document_id)) {
      /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */
      $document_id = $translationService->getConfigDocumentId($mapper);
    }
    return $document_id;
  }

}
