<?php

declare(strict_types=1);

namespace Drupal\nodehive_path_alias\Plugin\Validation\Constraint;

use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

use function assert;

/**
 * Validates path alias uniqueness scoped per NodeHive space.
 *
 * Uniqueness is checked per (alias + langcode + nodehive_space).
 * A NULL space means a global alias with no space constraint.
 */
final class NodehiveSpaceUniquePathAliasConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {

  /**
   * Constructs a NodehiveSpaceUniquePathAliasConstraintValidator instance.
   */
  public function __construct(
    private readonly EntityTypeManagerInterface $entityTypeManager,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    return new self(
      $container->get('entity_type.manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function validate(mixed $entity, Constraint $constraint): void {
    assert($constraint instanceof NodehiveSpaceUniquePathAliasConstraint);

    /** @var \Drupal\path_alias\PathAliasInterface $entity */
    $path = $entity->getPath();
    $alias = $entity->getAlias();
    $langcode = $entity->language()->getId();
    /** @var int|null $spaceId */
    $spaceId = $entity->get('nodehive_space')->target_id ?? NULL;

    $storage = $this->entityTypeManager->getStorage('path_alias');
    $query = $storage->getQuery()
      ->accessCheck(FALSE)
      ->condition('alias', $alias, '=')
      ->condition('langcode', $langcode, '=');

    if (!$entity->isNew()) {
      $query->condition('id', $entity->id(), '<>');
    }
    if ($path) {
      $query->condition('path', $path, '<>');
    }

    // Scope uniqueness to the space. NULL means global (no space assigned).
    if ($spaceId !== NULL) {
      $query->condition('nodehive_space', $spaceId, '=');
    }
    else {
      $query->notExists('nodehive_space');
    }

    if ($result = $query->range(0, 1)->execute()) {
      $existingAliasId = reset($result);
      $existingAlias = $storage->load($existingAliasId);
      assert($existingAlias !== NULL);

      if ($existingAlias->getAlias() !== $alias) {
        $this->context->buildViolation($constraint->differentCapitalizationMessage, [
          '%alias' => $alias,
          '%stored_alias' => $existingAlias->getAlias(),
        ])->addViolation();
      }
      else {
        $this->context->buildViolation($constraint->message, [
          '%alias' => $alias,
        ])->addViolation();
      }
    }
  }

}
