<?php

declare(strict_types=1);

namespace Drupal\Tests\nodehive_path_alias\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\nodehive_path_alias\PathAliasSpaceLookup;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;

/**
 * Kernel tests for PathAliasSpaceLookup with a real SQLite database.
 *
 * We bypass the full nodehive_core dependency by manually adding the
 * nodehive_space column to the path_alias table after installEntitySchema().
 */
#[CoversClass(PathAliasSpaceLookup::class)]
#[Group('nodehive_path_alias')]
final class PathAliasSpaceLookupKernelTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   *
   * @var string[]
   */
  protected static $modules = ['system', 'path_alias', 'user'];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installEntitySchema('path_alias');
    $this->installEntitySchema('user');

    // Manually add nodehive_space integer column to path_alias table.
    // This replicates what nodehive_path_alias module does via
    // hook_entity_base_field_info, without requiring nodehive_core.
    $schema = $this->container->get('database')->schema();
    if (!$schema->fieldExists('path_alias', 'nodehive_space')) {
      $schema->addField('path_alias', 'nodehive_space', [
        'type' => 'int',
        'size' => 'normal',
        'unsigned' => TRUE,
        'not null' => FALSE,
        'default' => NULL,
        'description' => 'NodeHive space ID (FK to nodehive_space).',
      ]);
    }

    $this->insertAlias('/node/1', '/homepage', 'cs', 1);
    $this->insertAlias('/node/2', '/homepage', 'cs', 2);
    $this->insertAlias('/node/3', '/homepage', 'cs', 3);
    $this->insertAlias('/node/4', '/about', 'cs', 1);
    $this->insertAlias('/node/5', '/about', 'und', 1);
    // The /contact alias only has 'und' — used to test langcode fallback.
    $this->insertAlias('/node/6', '/contact', 'und', 1);
  }

  /**
   * Tests that resolve returns the correct path for a given space and langcode.
   */
  public function testResolvesBySpaceAndLangcode(): void {
    $lookup = $this->container->get('database');
    $service = new PathAliasSpaceLookup($lookup);

    self::assertSame('/node/1', $service->resolve('/homepage', 1, 'cs'));
    self::assertSame('/node/2', $service->resolve('/homepage', 2, 'cs'));
    self::assertSame('/node/3', $service->resolve('/homepage', 3, 'cs'));
  }

  /**
   * Tests that resolve returns NULL for an unknown alias.
   */
  public function testResolveReturnsNullForUnknownAlias(): void {
    $lookup = new PathAliasSpaceLookup($this->container->get('database'));

    self::assertNull($lookup->resolve('/unknown', 1, 'cs'));
  }

  /**
   * Tests that resolve returns NULL when the alias exists in a different space.
   */
  public function testResolveReturnsNullForWrongSpace(): void {
    $lookup = new PathAliasSpaceLookup($this->container->get('database'));

    // /homepage exists in space 1 (cs) but NOT in space 99.
    self::assertNull($lookup->resolve('/homepage', 99, 'cs'));
  }

  /**
   * Tests that resolve falls back to the 'und' langcode.
   */
  public function testResolveFallsBackToUndLangcode(): void {
    $lookup = new PathAliasSpaceLookup($this->container->get('database'));

    // /contact in space 1: only 'und' version exists (no 'cs' version).
    // When langcode=cs is requested, the lookup falls back to 'und'.
    self::assertSame('/node/6', $lookup->resolve('/contact', 1, 'cs'));
  }

  /**
   * Tests that resolve prefers the requested langcode over 'und'.
   */
  public function testResolvePrefersCsOverUnd(): void {
    $lookup = new PathAliasSpaceLookup($this->container->get('database'));

    // /about in space 1 has both 'cs' (node/4) and 'und' (node/5).
    // When langcode=cs is requested, 'cs' should be returned first.
    self::assertSame('/node/4', $lookup->resolve('/about', 1, 'cs'));
  }

  /**
   * Tests that a NULL langcode resolves using the 'und' alias.
   */
  public function testResolveWithNullLangcodeUsesUnd(): void {
    $lookup = new PathAliasSpaceLookup($this->container->get('database'));

    // A null langcode tries 'und' first; /about has a 'und' row.
    self::assertSame('/node/5', $lookup->resolve('/about', 1, NULL));
    // /homepage has no 'und' row → fallback returns any alias for the space.
    self::assertSame('/node/1', $lookup->resolve('/homepage', 1, NULL));
  }

  /**
   * Inserts a path alias row directly into the database.
   */
  private function insertAlias(string $path, string $alias, string $langcode, ?int $spaceId): void {
    $db = $this->container->get('database');
    $db->insert('path_alias')
      ->fields([
        'uuid' => \Drupal::service('uuid')->generate(),
        'path' => $path,
        'alias' => $alias,
        'langcode' => $langcode,
        'status' => 1,
        'nodehive_space' => $spaceId,
      ])
      ->execute();
  }

}
