<?php

namespace Drupal\Tests\gcds\Functional;

use Drupal\Tests\BrowserTestBase;

/**
 * Test the gcds theme.
 *
 * @group gcds
 */
class GcdsTest extends BrowserTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'block',
    'gcds_test',
    'twig_tools',
    'twig_tweak',
  ];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'starterkit_theme';

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

    // Set theme to gcds. It does not work to do this with $defaultTheme.
    $this->assertTrue(\Drupal::service('theme_installer')->install(['gcds']));
    $this->container->get('config.factory')
      ->getEditable('system.theme')
      ->set('default', 'gcds')
      ->save();
    // Clear cache so that Drupal can find the SDCs in gcds.
    drupal_flush_all_caches();

    // Place blocks.
    $this->drupalPlaceBlock('page_title_block');
    $this->drupalPlaceBlock('system_main_block');
  }

  /**
   * Tests.
   */
  public function testGcds(): void {
    $theme_config = $this->container->get('config.factory')->getEditable('gcds.settings');

    // Home page loads.
    $this->drupalGet('');
    $this->assertSession()->statusCodeEquals(200);
    // Libraries are included.
    $this->gcdsLibraryTest();
    // Header.
    $this->assertSession()->elementExists('xpath', '//header/gcds-header[@skip-to-href = "#mc"]/gcds-breadcrumbs[@slot = "breadcrumb"]');
    // Main heading.
    $this->assertSession()->elementExists('xpath', '//main//div//gcds-heading[@tag = "h1"]');
    // Footer.
    $this->assertSession()->elementExists('xpath', '//footer/gcds-footer[@contextual-links]');

    // Load test form without GCDS form widgets. These test the same components
    // as the assertions under "Use GCDS form widgets", but with GCDS form
    // widgets not in use. These do not fully test the components since that
    // would just duplicate Drupal core testing.
    $this->drupalGet('gcds-test-form');
    $this->assertSession()->statusCodeEquals(200);
    $this->assertSession()->elementExists('xpath', '//gcds-heading/span[text() = "GCDS Testing Form"]');
    // Test gcds-fieldset.
    // 1.
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[contains(@class, "class_1 class_2")][@id = "fieldset-id"]/legend/span[text() = "Test fieldset 1 title"]');
    $element = $this->assertSession()->elementExists('xpath', '//main//form/fieldset/div[div[text() = "Test fieldset 1 description"]]');
    $this->assertTrue(str_contains($element->getText(), 'Test fieldset 1 content'));
    // 2.
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-fieldset-2"]/legend/span[text() = "Test fieldset 2 title"]');
    $element = $this->assertSession()->elementExists('xpath', '//main//form/fieldset/div[not(div)]');
    $this->assertTrue(str_contains($element->getText(), 'Test fieldset 2 content'));
    // Test gcds-radios.
    // 1.
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-radios-1--wrapper"][@required]');
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-radios-1--wrapper"][@required]/legend/span[text() = "Test radios 1 title"]');
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-radios-1--wrapper"][@required]//div/input[@name = "radios_1"]');
    // 2.
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-radios-2--wrapper"][not(@required)]');
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-radios-2--wrapper"][not(@required)]/legend/span[text() = "Test radios 2 title"]');
    $this->assertSession()->elementExists('xpath', '//main//form/fieldset[@id = "edit-radios-2--wrapper"][not(@required)]//div/input[@name = "radios_2"]');
    // Test gcds-checkboxes.
    // 1.
    $this->assertSession()->elementExists('xpath', '//main//form//div/input[@type = "checkbox"][@name = "checkboxes_1"][@required][@value = "1"]');
    // 2.
    $this->assertSession()->elementExists('xpath', '//main//form//div/input[@type = "checkbox"][@name = "checkboxes_2"][not(@required)][@value = "two"]');
    // Test submit buttons.
    $this->assertSession()->elementExists('xpath', '//main//form/input[@id = "edit-submit"][@type = "submit"][@value = "Submit"]');
    $this->assertSession()->elementExists('xpath', '//main//form/input[@id = "edit-reset"][@type = "submit"][@value = "Reset"]');

    // Use GCDS form widgets.
    $gcds_version = '0.43.1';
    $gcds_version_css_shortcuts = '1.0.1';
    $theme_config->set('form_widgets_default', TRUE);
    $theme_config->set('gcds_version', $gcds_version);
    $theme_config->set('gcds_version_css_shortcuts', $gcds_version_css_shortcuts);
    $theme_config->save();
    drupal_flush_all_caches();
    // Load test form with GCDS form widgets.
    $this->drupalGet('gcds-test-form');
    $this->assertSession()->statusCodeEquals(200);
    $this->gcdsLibraryTest($gcds_version, $gcds_version_css_shortcuts);
    $this->assertSession()->elementExists('xpath', '//gcds-heading/span[text() = "GCDS Testing Form"]');
    // Test gcds-fieldset.
    $this->assertSession()->elementExists('xpath', '//main//gcds-fieldset[@legend = "Test fieldset 1 title"][@legend-size = "h6"][@hint = "Test fieldset 1 description"][contains(@class, "class_1 class_2")][@id = "fieldset-id"][contains(text(), "Test fieldset 1 content")]');
    $this->assertSession()->elementExists('xpath', '//main//gcds-fieldset[@legend = "Test fieldset 2 title"][@legend-size = "h2"][not(@hint)][contains(text(), "Test fieldset 2 content")]');
    // Test gcds-radios.
    $args = [
      ':options' => htmlspecialchars('[{"id":"edit-radios-1-1","label":"One","value":"1","checked":false},{"id":"edit-radios-1-two","label":"Two","value":"two","checked":false}]'),
    ];
    $xpath = $this->assertSession()->buildXPathQuery('//main//form/gcds-radios[@legend = "Test radios 1 title"][@name = "radios_1"][@options = :options][@hint = "Test radios 1 description"][@required]', $args);
    $this->assertSession()->elementExists('xpath', $xpath);
    $args = [
      ':options' => htmlspecialchars('[{"id":"edit-radios-2-three","label":"Three","value":"three","checked":false},{"id":"edit-radios-2-four","label":"Four","value":"four","checked":true}]'),
    ];
    $xpath = $this->assertSession()->buildXPathQuery('//main//form/gcds-radios[@legend = "Test radios 2 title"][@name = "radios_2"][@options = :options][not(@hint)][not(@required)]', $args);
    $this->assertSession()->elementExists('xpath', $xpath);
    // Test gcds-checkboxes.
    $args = [
      ':options' => htmlspecialchars('[{"id":"edit-checkboxes-1","label":"Test checkboxes 1 title","value":"1","hint":"Test checkboxes 1 description"}]'),
    ];
    $xpath = $this->assertSession()->buildXPathQuery('//main//form//gcds-checkboxes[@name = "checkboxes_1"][@options = :options][not(@hint)][@required][not(@value)]', $args);
    $this->assertSession()->elementExists('xpath', $xpath);
    $args = [
      ':options' => htmlspecialchars('[{"id":"edit-checkboxes-2","label":"Test checkboxes 2 title","value":"two","checked":true}]'),
    ];
    $xpath = $this->assertSession()->buildXPathQuery('//main//form//gcds-checkboxes[@name = "checkboxes_2"][@options = :options][not(@hint)][not(@required)][@value = "two"]', $args);
    $this->assertSession()->elementExists('xpath', $xpath);
    // Test submit buttons.
    $this->assertSession()->elementExists('xpath', '//main//gcds-button[@button-id = "edit-submit"][@button-role = "primary"][@type = "submit"][@value = "Submit"]');
    $this->assertSession()->elementExists('xpath', '//main//gcds-button[@button-id = "edit-reset"][@button-role = "danger"][@type = "submit"][@value = "Reset"]');

    // Test the 404 page.
    $this->drupalGet('gcds-test-404-page');
    $this->assertSession()->statusCodeEquals(404);
    $this->assertSession()->elementExists('xpath', '//main/div[@class = "layout-content"]/div
      [p[contains(text(), "We are sorry you ended up here. Sometimes a page gets moved or deleted.")]/strong[text() = "Error 404"]]');
  }

  /**
   * Test that the GCDS libraries are on the page.
   *
   * @param string $gcds_version
   *   The version number for GCDS.
   * @param string $gcds_version_css_shortcuts
   *   The version number for gcds-css-shortcuts.
   */
  protected function gcdsLibraryTest(string $gcds_version = 'latest', string $gcds_version_css_shortcuts = 'latest') : void {
    $this->assertSession()->elementExists('xpath', '/head/link[@rel = "stylesheet"][@media = "all"][@href = "https://cdn.design-system.alpha.canada.ca/@gcds-core/css-shortcuts@' . $gcds_version_css_shortcuts . '/dist/gcds-css-shortcuts.min.css"]');
    $this->assertSession()->elementExists('xpath', '/head/link[@rel = "stylesheet"][@media = "all"][@href = "https://cdn.design-system.alpha.canada.ca/@cdssnc/gcds-components@' . $gcds_version . '/dist/gcds/gcds.css"]');
    $this->assertSession()->elementExists('xpath', '/body/script[@src = "https://cdn.design-system.alpha.canada.ca/@cdssnc/gcds-components@' . $gcds_version . '/dist/gcds/gcds.esm.js"][@type = "module"]');
    $this->assertSession()->elementExists('xpath', '/body/script[@src = "https://cdn.design-system.alpha.canada.ca/@cdssnc/gcds-components@' . $gcds_version . '/dist/gcds/gcds.js"][@nomodule]');
  }

}
