<?php

declare(strict_types=1);

namespace Drupal\Tests\farm_ui_views\Functional;

use Drupal\Tests\farm_test\Functional\FarmBrowserTestBase;
use Drupal\asset\Entity\Asset;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

/**
 * Tests the farm_ui_views taxonomy views routes.
 */
#[Group('farm')]
#[RunTestsInSeparateProcesses]
class TaxonomyTermTasksTest extends FarmBrowserTestBase {

  /**
   * Test user.
   *
   * @var \Drupal\user\Entity\User|bool
   */
  protected $user;

  /**
   * Test plant type.
   *
   * @var \Drupal\taxonomy\Entity\Term
   */
  protected $favaPlantType;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'block',
    'farm_plant',
    'farm_seed',
    'farm_ui_views',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->drupalPlaceBlock('local_tasks_block');

    // Create/login a user with permission to access taxonomy pages and assets.
    $this->user = $this->createUser(['access content', 'access asset collection', 'view any asset']);
    $this->drupalLogin($this->user);

    $entity_type_manager = $this->container->get('entity_type.manager');
    $term_storage = $entity_type_manager->getStorage('taxonomy_term');

    // Create a "Oat" plant type term.
    $oat_plant_type = $term_storage->create([
      'name' => 'Oat',
      'vid' => 'plant_type',
    ]);
    $oat_plant_type->save();

    // Create a oat plant.
    Asset::create([
      'name' => 'Pringle\'s Progress Oat Planting',
      'type' => 'plant',
      'plant_type' => ['target_id' => $oat_plant_type->id()],
    ])->save();

    // Create a "Fava Bean" plant type term.
    $this->favaPlantType = $term_storage->create([
      'name' => 'Fava Bean',
      'vid' => 'plant_type',
    ]);
    $this->favaPlantType->save();

    // Create a fava plant.
    Asset::create([
      'name' => 'Red Flowering Fava Planting',
      'type' => 'plant',
      'plant_type' => ['target_id' => $this->favaPlantType->id()],
    ])->save();

    // Create a fava seed.
    Asset::create([
      'name' => 'Red Flowering Fava Seeds',
      'type' => 'seed',
      'plant_type' => ['target_id' => $this->favaPlantType->id()],
    ])->save();
  }

  /**
   * Run all tests.
   */
  public function testAll() {
    $this->doTestTaxonomyTermAssetTaskTabsAppear();
    $this->doTestTaxonomyTermAssetViews();
  }

  /**
   * Test that the asset view task links appear on taxonomy term pages.
   */
  public function doTestTaxonomyTermAssetTaskTabsAppear() {
    $fava_term_url = 'taxonomy/term/' . $this->favaPlantType->id();

    $this->drupalGet($fava_term_url);
    $this->assertSession()->statusCodeEquals(200);

    $get_array_of_link_text_by_url = function ($elems) {
      $result = [];
      foreach ($elems as $elem) {
        $result[$elem->getAttribute('href')] = $elem->getText();
      }
      return $result;
    };

    // Select links from the first ul inside layout-container.
    $primary_tab_links = $get_array_of_link_text_by_url($this->xpath('(//div[@class="layout-container"]//ul)[1]/li/a'));

    $assert_has_link = function ($elements, $url, $label) {
      $this->assertArrayHasKey($url, $elements, "No link exists with url '$url' among: " . print_r($elements, TRUE));

      $this->assertEquals($label, $elements[$url], "Link label not as expected.");
    };

    $assert_has_link($primary_tab_links, "/$fava_term_url/assets", 'Assets');

    $this->drupalGet("$fava_term_url/assets/all");
    $this->assertSession()->statusCodeEquals(200);

    // Select links from the second ul inside layout-container.
    $secondary_tab_links = $get_array_of_link_text_by_url($this->xpath('(//div[@class="layout-container"]//ul)[2]/li/a'));

    $this->assertCount(3, $secondary_tab_links, 'Only 3 secondary tabs appear.');

    $assert_has_link($secondary_tab_links, "/$fava_term_url/assets", 'All');
    $assert_has_link($secondary_tab_links, "/$fava_term_url/assets/plant", 'Plant');
    $assert_has_link($secondary_tab_links, "/$fava_term_url/assets/seed", 'Seed');
  }

  /**
   * Test that the views of assets for terms show the correct assets.
   */
  public function doTestTaxonomyTermAssetViews() {
    $fava_term_url = 'taxonomy/term/' . $this->favaPlantType->id();

    $this->drupalGet("$fava_term_url/assets/all");
    $this->assertSession()->statusCodeEquals(200);

    $this->assertSession()->pageTextContains('Red Flowering Fava Planting');
    $this->assertSession()->pageTextContains('Red Flowering Fava Seeds');
    $this->assertSession()->pageTextNotContains('Pringle\'s Progress Oat Planting');

    $this->drupalGet("$fava_term_url/assets/plant");
    $this->assertSession()->statusCodeEquals(200);

    $this->assertSession()->pageTextContains('Red Flowering Fava Planting');
    $this->assertSession()->pageTextNotContains('Red Flowering Fava Seeds');
    $this->assertSession()->pageTextNotContains('Pringle\'s Progress Oat Planting');

    $this->drupalGet("$fava_term_url/assets/seed");
    $this->assertSession()->statusCodeEquals(200);

    $this->assertSession()->pageTextNotContains('Red Flowering Fava Planting');
    $this->assertSession()->pageTextContains('Red Flowering Fava Seeds');
    $this->assertSession()->pageTextNotContains('Pringle\'s Progress Oat Planting');

    // Check that invalid term IDs are handled gracefully by the access check.
    $this->drupalGet('/taxonomy/term/0/assets');
    $this->assertSession()->statusCodeEquals(404);
    $this->drupalGet('/taxonomy/term/-1/assets');
    $this->assertSession()->statusCodeEquals(404);
    $this->drupalGet('/taxonomy/term/foo/assets');
    $this->assertSession()->statusCodeEquals(404);

    // Check that an invalid entity_bundle parameter is handled gracefully by
    // the access check. /taxonomy/term/%/assets/0 will be passed on to the
    // Views contextual filter validation, so it should return a 404.
    // /taxonomy/term/%/assets/-1 and /taxonomy/term/%/assets/foo will be caught
    // by the access check, but will 403 because no assets will be found of that
    // type.
    $this->drupalGet("$fava_term_url/assets/0");
    $this->assertSession()->statusCodeEquals(404);
    $this->drupalGet("$fava_term_url/assets/-1");
    $this->assertSession()->statusCodeEquals(403);
    $this->drupalGet("$fava_term_url/assets/foo");
    $this->assertSession()->statusCodeEquals(403);
  }

}
