<?php

declare(strict_types=1);

namespace Drupal\Tests\ui_patterns\Kernel\PropTypeNormalization;

use Drupal\Core\Render\Markup;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\Tests\ui_patterns\Kernel\PropTypeNormalizationTestBase;
use Drupal\ui_patterns\Plugin\UiPatterns\PropType\LinksPropType;

/**
 * Test LinksPropType normalization.
 *
 * @coversDefaultClass \Drupal\ui_patterns\Plugin\UiPatterns\PropType\LinksPropType
 * @group ui_patterns
 */
class LinksPropTypeTest extends PropTypeNormalizationTestBase {

  /**
   * Test normalize static method.
   *
   * @dataProvider normalizationTests
   */
  public function testNormalization(mixed $value, mixed $expected) : void {
    $normalized = LinksPropType::normalize($value, $this->testComponentProps['links']);
    $this->assertEquals($normalized, $expected);
  }

  /**
   * Test normalize static method with manual data.
   */
  public function testNormalizationManualData() : void {
    $tests = [
      "Menu, as generated by the Menu module" => self::menu(),
      "Standardized structure, flat, with objects" => self::standardizedFlatObjects(),
    ];
    foreach ($tests as $test) {
      $value = $test[0];
      $expected = $test[1];
      $normalized = LinksPropType::normalize($value, $this->testComponentProps['links']);
      $n_items = count($normalized);
      $this->assertCount(count($expected), $normalized);
      for ($i = 0; $i < $n_items; $i++) {
        $item = array_pop($normalized);
        $expected_item = array_pop($expected);
        foreach ($expected_item as $key => $value) {
          $this->assertEquals($value, $item[$key] ?? NULL);
          break;
        }
      }
      // $this->assertEquals($normalized, $expected);
    }
  }

  /**
   * Test rendered component with prop.
   *
   * @dataProvider renderingTests
   */
  public function testRendering(mixed $value, mixed $rendered_value) : void {
    $this->runRenderPropTest('links', ["value" => $value, "rendered_value" => $rendered_value]);
  }

  /**
   * Provides data for testNormalization.
   */
  public static function normalizationTests() : array {
    return [
      "null value" => [NULL, []],
      "single item" => [
        [[
          "url" => "https://drupal.org",
          "title" => "Drupal",
        ],
        ],
        [[
          "url" => "https://drupal.org",
          "title" => "Drupal",
        ],
        ],
      ],
      "single item with integer title" => [
        [[
          "url" => "https://drupal.org",
          "title" => 2,
        ],
        ],
        [[
          "url" => "https://drupal.org",
          "title" => 2,
        ],
        ],
      ],
      "single item with markup title" => [
        [[
          "url" => Url::fromUri("https://drupal.org"),
          "title" => Markup::create("Drupal"),
        ],
        ],
        [[
          "url" => "https://drupal.org",
          "title" => "Drupal",
        ],
        ],
      ],
      "single item with nolink url" => [
        [[
          "url" => "<nolink>",
          "title" => "Drupal",
        ],
        ],
        [[
          "url" => "<nolink>",
          "title" => "Drupal",
        ],
        ],
      ],
      "single item with front url and svg" => [
        [[
          "url" => "<front>",
          "title" => "<svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">
            <path d=\"m8 3.293 4.712 4.712A4.5 4.5 0 0 0 8.758 15H3.5A1.5 1.5 0 0 1 2 13.5V9.293z\"></path>
          </svg>",
        ],
        ],
        [[
          "url" => "<front>",
          "title" => "<svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">
            <path d=\"m8 3.293 4.712 4.712A4.5 4.5 0 0 0 8.758 15H3.5A1.5 1.5 0 0 1 2 13.5V9.293z\"></path>
          </svg>",
        ],
        ],
      ],
      "Standardized structure, flat, only primitives" => self::standardizedFlatPrimitives(),
      "Breadcrumb structure, as generated by the core service" => self::breadcrumb(),
      "Mini pager, as generated by the Views module" => self::viewsMiniPager(),
      "Pager's pages, as generated by the Views module" => self::pagerPages(),
      "Pager's navigation links, as generated by the Views module" => self::pagesNavigationLinks(),
      "Where link_attributes is already manually set" => self::linkAttributes(),
    ];
  }

  /**
   * Provides data for testNormalization.
   */
  public static function renderingTests() : array {
    return [
      "null value" => [
        NULL,
        '<div class="ui-patterns-props-links"></div>',
      ],
      "single item" => [
        [[
          "url" => "https://drupal.org",
          "title" => "Drupal",
        ],
        ],
        '<a href="https://drupal.org">Drupal</a>',
      ],
      "single item with integer title" => [
        [[
          "url" => "https://drupal.org",
          "title" => 2,
        ],
        ],
        '<a href="https://drupal.org">2</a>',
      ],
      "single item with markup title" => [
        [[
          "url" => Url::fromUri("https://drupal.org"),
          "title" => Markup::create("Drupal"),
        ],
        ],
        '<a href="https://drupal.org">Drupal</a>',
      ],
      "single item with nolink url" => [
        [[
          "url" => "<nolink>",
          "title" => "Drupal",
        ],
        ],
        '<div class="ui-patterns-props-url"></div>',
      ],
      "single item with front url and svg" => [
        [[
          "url" => "<front>",
          "title" => "<svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">
            <path d=\"m8 3.293 4.712 4.712A4.5 4.5 0 0 0 8.758 15H3.5A1.5 1.5 0 0 1 2 13.5V9.293z\"></path>
          </svg>",
        ],
        ],
        "><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"m8 3.293 4.712 4.712A4.5 4.5 0 0 0 8.758 15H3.5A1.5 1.5 0 0 1 2 13.5V9.293z\"></path></svg></a>",
      ],
    ];
  }

  /**
   * Standardized structure, flat, only primitives.
   */
  protected static function standardizedFlatPrimitives() {
    $value = [
      [
        "title" => "With an absolute URL",
        "url" => "http://wwww.example.org/foo/bar",
      ],
      [
        "title" => "With a relative URL",
        "url" => "/foo/bar",
        "attributes" => [
          "foo" => "bar",
        ],
        "link_attributes" => [
          "foo" => "baz",
        ],
      ],
    ];
    $expected = $value;
    return [$value, $expected];
  }

  /**
   * Standardized structure, flat, with objects.
   */
  protected static function standardizedFlatObjects() {
    $value = [
      [
        "title" => "With an absolute URL",
        "url" => Url::fromUri("http://wwww.example.org//foo/bar"),
      ],
      [
        "title" => "With a relative URL",
        "url" => Url::fromUserInput("/foo/bar", ["attributes" => ["foo" => "baz"]]),
        "attributes" => new Attribute([
          "foo" => "bar",
        ]),
      ],
      [
        "title" => "With empty attributes",
        "url" => Url::fromUserInput("/foo/bar", ["attributes" => []]),
        "attributes" => new Attribute([]),
      ],
    ];
    $expected = [
      [
        "title" => "With an absolute URL",
        "url" => "http://wwww.example.org/foo/bar",
      ],
      [
        "title" => "With a relative URL",
        "url" => "/foo/bar",
        "attributes" => [
          "foo" => "bar",
        ],
      ],
      [
        "title" => "With empty attributes",
        "url" => "/foo/bar",
      ],
    ];
    return [$value, $expected];
  }

  /**
   * Breadcrumb structure, as generated by the core service.
   *
   * However, we replaced TranslatableMarkup("Home") by a simple string to
   * avoid initializing a service container in the test.
   */
  protected static function breadcrumb() {
    $value = [
      [
        "text" => "Home",
        "url" => "/",
      ],
      [
        "text" => "Foo",
        "url" => "/foo",
      ],
    ];
    $expected = [
      [
        "title" => "Home",
        "url" => "/",
      ],
      [
        "title" => "Foo",
        "url" => "/foo",
      ],
    ];
    return [$value, $expected];
  }

  /**
   * Mini pager, as generated by the Views module.
   *
   * Usually, we don't expect the theme owner to use the normalization
   * directly, but to add some custom logic around 'current' in a preprocess.
   */
  protected static function viewsMiniPager() {
    $value = [
      "current" => 2,
      "previous" => [
        "href" =>
        "/articles?page=0",
        "text" => "‹‹",
        "attributes" => new Attribute(),
      ],
      "next" => [
        "href" => "/articles?page=2",
        "text" => "››",
        "attributes" => new Attribute(),
      ],
    ];
    $expected = [
      [
        "title" => "‹‹",
        "url" => "/articles?page=0",
      ],
      [
        "title" => "››",
        "url" => "/articles?page=2",
      ],
    ];
    return [$value, $expected];
  }

  /**
   * Pager's pages, as generated by the Views module.
   */
  protected static function pagerPages() {
    $value = [
      [
        "href" => "?page=0",
        "attributes" => new Attribute(),
      ],
      [
        "href" => "?page=1",
        "attributes" => new Attribute([
          "aria-current" => "page",
        ]),
      ],
      [
        "href" => "?page=2",
        "attributes" => new Attribute(),
      ],
      [
        "href" => "?page=3",
        "attributes" => new Attribute(),
      ],
    ];
    $expected = [
      [
        "title" => '0',
        "url" => "?page=0",
      ],
      [
        "title" => '1',
        "url" => "?page=1",
        "attributes" => [
          "aria-current" => "page",
        ],
      ],
      [
        "title" => '2',
        "url" => "?page=2",
      ],
      [
        "title" => '3',
        "url" => "?page=3",
      ],
    ];
    return [$value, $expected];
  }

  /**
   * Pager's navigation links, as generated by the Views module.
   */
  protected static function pagesNavigationLinks() {
    $value = [
      "first" => [
        "attributes" => new Attribute(),
        "href" => "?page=0",
        "text" => "« First",
      ],
      "previous" => [
        "attributes" => new Attribute(),
        "href" => "?page=0",
        "text" => "‹‹",
      ],
      "next" => [
        "attributes" => new Attribute(),
        "href" => "?page=2",
        "text" => "››",
      ],
      "last" => [
        "attributes" => new Attribute(),
        "href" => "?page=3",
        "text" => "Last »",
      ],
    ];
    $expected = [
      [
        "title" => "« First",
        "url" => "?page=0",
      ],
      [
        "title" => "‹‹",
        "url" => "?page=0",
      ],
      [
        "title" => "››",
        "url" => "?page=2",
      ],
      [
        "title" => "Last »",
        "url" => "?page=3",
      ],
    ];
    return [$value, $expected];
  }

  /**
   * Menu, as generated by the Menu module.
   *
   * However, we replaced TranslatableMarkup() by a simple string to avoid
   * initializing a service container in the test.
   * And we replaced Url::fromRoute() by Url::fromUserInput() to avoid
   * initializing a service container in the test.
   */
  protected static function menu() {
    $value = [
      "user.page" => [
        "is_expanded" => FALSE,
        "is_collapsed" => FALSE,
        "in_active_trail" => FALSE,
        "attributes" => [],
        "title" => "My account",
        "url" => Url::fromUserInput("/user", ["set_active_class" => TRUE]),
        "below" => [],
      ],
      "user.logout" => [
        "is_expanded" => FALSE,
        "is_collapsed" => FALSE,
        "in_active_trail" => FALSE,
        "attributes" => [],
        "title" => "Log out",
        "url" => Url::fromUserInput("/user/logout", ["set_active_class" => TRUE]),
        "below" => [],
      ],
    ];
    $expected = [
      [
        "title" => "My account",
        "url" => "/user",
      ],
      [
        "title" => "Log out",
        "url" => "/user/logout",
      ],
    ];
    return [$value, $expected];
  }

  /**
   * Where link_attributes is already manually set.
   *
   * Instead of being generated from an Url object.
   */
  protected static function linkAttributes() {
    $value = [
      [
        "href" => "?page=0",
        "link_attributes" => new Attribute([
          'class' => [
            'display-flex',
            'flex-align-center',
            'flex-no-wrap',
          ],
        ]),
      ],
    ];
    $expected = [
      [
        "title" => '0',
        "url" => "?page=0",
        "link_attributes" => [
          'class' => [
            'display-flex',
            'flex-align-center',
            'flex-no-wrap',
          ],
        ],
      ],
    ];
    return [$value, $expected];
  }

}
