<?php

declare(strict_types=1);

namespace Drupal\Tests\nodehive_path_alias\Unit;

use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\decoupled_router\PathTranslatorEvent;
use Drupal\nodehive_path_alias\EventSubscriber\SpaceIdPathTranslatorSubscriber;
use Drupal\nodehive_path_alias\PathAliasSpaceLookupInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;

/**
 * Unit tests for SpaceIdPathTranslatorSubscriber.
 */
#[CoversClass(SpaceIdPathTranslatorSubscriber::class)]
#[Group('nodehive_path_alias')]
final class SpaceIdPathTranslatorSubscriberTest extends TestCase {

  /**
   * Tests that the subscriber skips when no space_id query param is present.
   */
  public function testSkipsWhenNoSpaceId(): void {
    $lookup = $this->createMock(PathAliasSpaceLookupInterface::class);
    $lookup->expects(self::never())->method('resolve');

    $event = $this->buildEvent('/about', NULL);
    $event->expects(self::never())->method('setPath');

    $subscriber = new SpaceIdPathTranslatorSubscriber($lookup);
    $subscriber->onPathTranslation($event);
  }

  /**
   * Tests that the subscriber skips when space_id is zero or negative.
   */
  public function testSkipsWhenSpaceIdIsZeroOrNegative(): void {
    $lookup = $this->createMock(PathAliasSpaceLookupInterface::class);
    $lookup->expects(self::never())->method('resolve');

    $event = $this->buildEvent('/about', '0');
    $event->expects(self::never())->method('setPath');

    $subscriber = new SpaceIdPathTranslatorSubscriber($lookup);
    $subscriber->onPathTranslation($event);
  }

  /**
   * Tests that the subscriber skips when the lookup returns NULL.
   */
  public function testSkipsWhenLookupReturnsNull(): void {
    $lookup = $this->createMock(PathAliasSpaceLookupInterface::class);
    $lookup->method('resolve')->willReturn(NULL);

    $event = $this->buildEvent('/about', '1');
    $event->expects(self::never())->method('setPath');

    $subscriber = new SpaceIdPathTranslatorSubscriber($lookup);
    $subscriber->onPathTranslation($event);
  }

  /**
   * Tests that setPath is called when the alias resolves successfully.
   */
  public function testSetsPathWhenResolved(): void {
    $lookup = $this->createMock(PathAliasSpaceLookupInterface::class);
    $lookup->method('resolve')->with('/about', 1, NULL)->willReturn('/node/42');

    $event = $this->buildEvent('/about', '1');
    $event->expects(self::once())->method('setPath')->with('/node/42');

    $subscriber = new SpaceIdPathTranslatorSubscriber($lookup);
    $subscriber->onPathTranslation($event);
  }

  /**
   * Tests that the langcode query parameter is forwarded to the lookup.
   */
  public function testPassesLangcodeToLookup(): void {
    $lookup = $this->createMock(PathAliasSpaceLookupInterface::class);
    $lookup->expects(self::once())
      ->method('resolve')
      ->with('/about', 2, 'cs')
      ->willReturn('/node/7');

    $event = $this->buildEvent('/about', '2', 'cs');
    $event->method('setPath')->willReturn(NULL);

    $subscriber = new SpaceIdPathTranslatorSubscriber($lookup);
    $subscriber->onPathTranslation($event);
  }

  /**
   * Tests that getSubscribedEvents returns the correct priority.
   */
  public function testGetSubscribedEventsReturnsCorrectPriority(): void {
    $events = SpaceIdPathTranslatorSubscriber::getSubscribedEvents();

    self::assertArrayHasKey(PathTranslatorEvent::TRANSLATE, $events);
    self::assertSame('onPathTranslation', $events[PathTranslatorEvent::TRANSLATE][0]);
    self::assertSame(100, $events[PathTranslatorEvent::TRANSLATE][1]);
  }

  /**
   * Builds a mock PathTranslatorEvent with the given path and query params.
   */
  private function buildEvent(string $path, ?string $spaceId, ?string $langcode = NULL): PathTranslatorEvent {
    $queryParams = [];
    if ($spaceId !== NULL) {
      $queryParams['space_id'] = $spaceId;
    }
    if ($langcode !== NULL) {
      $queryParams['langcode'] = $langcode;
    }

    $request = Request::create('/router/translate-path', 'GET', $queryParams);
    $event = $this->getMockBuilder(PathTranslatorEvent::class)
      ->disableOriginalConstructor()
      ->onlyMethods(['getRequest', 'setPath', 'getPath', 'getResponse'])
      ->getMock();
    $event->method('getRequest')->willReturn($request);
    $event->method('getPath')->willReturn($path);
    $metadata = $this->createMock(CacheableMetadata::class);
    $metadata->method('addCacheContexts')->willReturnSelf();
    $response = $this->createMock(CacheableJsonResponse::class);
    $response->method('getCacheableMetadata')->willReturn($metadata);
    $event->method('getResponse')->willReturn($response);

    return $event;
  }

}
