<?php

declare(strict_types=1);

namespace Drupal\Tests\netforum\Functional;

use Drupal\netforum\SoapHttpAuthenticationType;
use Drupal\netforum\SoapWsdlCacheType;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\netforum\Traits\NetForumConfigTrait;
use Drupal\Tests\netforum\Utilities\RandomText;
use Drupal\Tests\netforum\xWeb\AuthExpirationPolicy;
use Drupal\Tests\netforum\xWeb\NetForumXmlSettings;
use Drupal\Tests\netforum\xWeb\SoapServerManager;
use Drupal\user\Entity\User;

/**
 * Test administration of the NetForum module.
 *
 * @group netforum
 */
final class NetforumAdminTest extends BrowserTestBase {

  use NetForumConfigTrait;

  protected static $modules = ['netforum'];

  protected $defaultTheme = 'stark';

  private User $nfAdminUser;

  /**
   * Set up the test.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \Exception
   */
  public function setUp(): void {
    parent::setUp();

    $this->configureNetForumConfig($this->container->get('config.factory'), $this->container->get('config.storage'));

    $user = $this->drupalCreateUser(['administer netforum']);
    if ($user === FALSE) {
      $this->fail('Could not create admin user.');
    }
    $this->nfAdminUser = $user;
  }

  /**
   * Test that the settings form is accessible to the correct users.
   *
   * @throws \Behat\Mink\Exception\ExpectationException
   */
  public function testSettingsFormAccess(): void {
    // Test user without proper permission.
    $this->drupalGet('admin/config/services/netforum/settings');
    $this->assertSession()->statusCodeEquals(403);

    // Test user with proper permission.
    $this->drupalLogin($this->nfAdminUser);
    $this->drupalGet('admin/config/services/netforum/settings');
    $this->assertSession()->statusCodeEquals(200);
  }

  /**
   * Test saving configuration on the settings form.
   *
   * @throws \Random\RandomException
   * @throws \Behat\Mink\Exception\ResponseTextException
   * @throws \Behat\Mink\Exception\ExpectationException
   */
  public function testSavingConfiguration(): void {
    // Start with the default configuration.
    $this->resetConfiguration();

    $endpoint = 'https://example.com/' . RandomText::getRandomText(8) . '/netForumXML.asmx';
    $username = RandomText::getRandomText(4);
    $password = RandomText::getRandomText(8);
    $httpAuthUsername = RandomText::getRandomText(4);
    $httpAuthPassword = RandomText::getRandomText(8);
    $proxyHostname = '127.0.0.1';
    $proxyUsername = RandomText::getRandomText(4);
    $proxyPassword = RandomText::getRandomText(8);

    // Set each setting to something different from the default.
    $this->drupalLogin($this->nfAdminUser);
    $this->drupalGet('admin/config/services/netforum/settings');
    $this->submitForm([
      'xweb_endpoint' => $endpoint,
      'xweb_username' => $username,
      'xweb_password' => $password,
      'request_timeout' => 5,
      'wsdl_cache' => SoapWsdlCacheType::Memory->value,
      'http_keepalive' => FALSE,
      'http_compression' => FALSE,
      'verify_tls' => FALSE,
      'http_auth_enabled' => TRUE,
      'http_auth_type' => SoapHttpAuthenticationType::Digest->value,
      'http_auth_username' => $httpAuthUsername,
      'http_auth_password' => $httpAuthPassword,
      'debug_log_all' => TRUE,
      'proxy_enabled' => TRUE,
      'proxy_hostname' => $proxyHostname,
      'proxy_port' => 8080,
      'proxy_username' => $proxyUsername,
      'proxy_password' => $proxyPassword,
    ], 'Save configuration');

    $this->assertSession()
      ->pageTextContains('The configuration options have been saved.');

    // Check that the username and password show again on the form.
    $this->assertSession()->fieldValueEquals('xweb_endpoint', $endpoint);
    $this->assertSession()->fieldValueEquals('xweb_username', $username);
    $this->assertSession()->fieldValueEquals('xweb_password', $password);
    $this->assertSession()->fieldValueEquals('request_timeout', '5');
    $this->assertSession()
      ->fieldValueEquals('wsdl_cache', (string) SoapWsdlCacheType::Memory->value);
    $this->assertSession()->checkboxNotChecked('http_keepalive');
    $this->assertSession()->checkboxNotChecked('http_compression');
    $this->assertSession()->checkboxNotChecked('verify_tls');
    $this->assertSession()->checkboxChecked('http_auth_enabled');
    $this->assertSession()
      ->fieldValueEquals('http_auth_type', (string) SoapHttpAuthenticationType::Digest->value);
    $this->assertSession()
      ->fieldValueEquals('http_auth_username', $httpAuthUsername);
    $this->assertSession()
      ->fieldValueEquals('http_auth_password', $httpAuthPassword);
    $this->assertSession()->checkboxChecked('debug_log_all');
    $this->assertSession()->checkboxChecked('proxy_enabled');
    $this->assertSession()->fieldValueEquals('proxy_hostname', $proxyHostname);
    $this->assertSession()->fieldValueEquals('proxy_port', '8080');
    $this->assertSession()->fieldValueEquals('proxy_username', $proxyUsername);
    $this->assertSession()->fieldValueEquals('proxy_password', $proxyPassword);

    // Check that the settings were actually set too.
    $this->assertEquals($endpoint, $this->config('netforum.settings')
      ->get('xweb_endpoint'));
    $this->assertEquals($username, $this->config('netforum.settings')
      ->get('xweb_username'));
    $this->assertEquals($password, $this->config('netforum.settings')
      ->get('xweb_password'));
    $this->assertEquals(5, $this->config('netforum.settings')
      ->get('request_timeout'));
    $this->assertEquals(SoapWsdlCacheType::Memory->value, $this->config('netforum.settings')
      ->get('wsdl_cache'));
    $this->assertFalse($this->config('netforum.settings')
      ->get('http_keepalive'));
    $this->assertFalse($this->config('netforum.settings')
      ->get('http_compression'));
    $this->assertFalse($this->config('netforum.settings')->get('verify_tls'));
    $this->assertTrue($this->config('netforum.settings')
      ->get('http_auth_enabled'));
    $this->assertEquals(SoapHttpAuthenticationType::Digest->value, $this->config('netforum.settings')
      ->get('http_auth_type'));
    $this->assertEquals($httpAuthUsername, $this->config('netforum.settings')
      ->get('http_auth_username'));
    $this->assertEquals($httpAuthPassword, $this->config('netforum.settings')
      ->get('http_auth_password'));
    $this->assertTrue($this->config('netforum.settings')->get('debug_log_all'));
    $this->assertTrue($this->config('netforum.settings')->get('proxy_enabled'));
    $this->assertEquals($proxyHostname, $this->config('netforum.settings')
      ->get('proxy_hostname'));
    $this->assertEquals(8080, $this->config('netforum.settings')
      ->get('proxy_port'));
    $this->assertEquals($proxyUsername, $this->config('netforum.settings')
      ->get('proxy_username'));
    $this->assertEquals($proxyPassword, $this->config('netforum.settings')
      ->get('proxy_password'));
  }

  /**
   * Test the test connection button.
   *
   * @throws \Behat\Mink\Exception\ResponseTextException
   * @throws \Random\RandomException
   */
  public function testTestConnection(): void {
    // Start a mock xWeb SOAP server.
    $username = RandomText::getRandomText(4);
    $password = RandomText::getRandomText(8);
    $nfSettings = new NetForumXmlSettings($username, $password, AuthExpirationPolicy::Sliding);
    $soapServerManager = new SoapServerManager($nfSettings);
    $soapServerManager->startSoapServer();

    // Start with the default configuration.
    $this->resetConfiguration();

    // Configure settings to use the mock soap server.
    $this->drupalLogin($this->nfAdminUser);
    $this->drupalGet('admin/config/services/netforum/settings');
    $this->submitForm([
      'xweb_endpoint' => 'http://127.0.0.1:' . $soapServerManager->getSoapServerPort(),
      'xweb_username' => $username,
      'xweb_password' => $password,
      'wsdl_cache' => SoapWsdlCacheType::None->value,
    ], 'Save configuration');
    $this->assertSession()
      ->pageTextContains('The configuration options have been saved.');

    // Click only the test button.
    $this->submitForm([], 'Test connection');

    // Check that the expected test results appear.
    $this->assertSession()->pageTextContains('Test connection result');
    $this->assertSession()
      ->pageTextContains('Successfully created client using endpoint http://127.0.0.1:' . $soapServerManager->getSoapServerPort());
    $this->assertSession()
      ->pageTextContains('Called TestConnection() and received Success response.');
    $this->assertSession()
      ->pageTextContains('Called GetDateTime() and datetime on server is');
    $this->assertSession()
      ->pageTextContains('Called Authenticate() successfully, received token');
    $this->assertSession()
      ->pageTextContains('Called GetVersion() successfully. netFORUM Mock, 1.0.0.0 using database Mock Database on server Mock Server.');
    $this->assertSession()
      ->pageTextContains('Notice: Authentication token expiration policy is set to Sliding.');

    $soapServerManager->stopSoapServer();
  }

}
