<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Kernel\Plugin\VisitorsEvent;

use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Session\SessionManager;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\Routing\Route;
use Drupal\visitors\Plugin\VisitorsEvent\Form;

/**
 * Tests the Form visitors event plugin.
 *
 * @group visitors
 * @coversDefaultClass \Drupal\visitors\Plugin\VisitorsEvent\Form
 */
class FormTest extends KernelTestBase {

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

  /**
   * The visitors event plugin manager.
   *
   * @var \Drupal\visitors\VisitorsEventPluginManager
   */
  protected $eventPluginManager;

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

    $this->installEntitySchema('user');
    $this->installSchema('system', ['sequences']);
    $this->installSchema('visitors', ['visitors_visit', 'visitors_event', 'visitors_counter']);
    $this->installConfig(['visitors']);

    // Set up the request stack with a request object.
    $request = new Request();
    $request->server->set('REQUEST_URI', '/test-form');

    // Initialize session handling.
    $session_manager = $this->container->get('session_manager');
    if (!$session_manager instanceof SessionManager) {
      $session_manager = new SessionManager(
        $this->container->get('request_stack'),
        $this->container->get('database'),
        $this->container->get('session_manager.metadata_bag'),
        $this->container->get('session_configuration'),
        $this->container->get('session_handler')
      );
      $this->container->set('session_manager', $session_manager);
    }

    // Start the session.
    $session_manager->start();
    $request->setSession($this->container->get('session'));

    $this->container->get('request_stack')->push($request);

    $this->eventPluginManager = $this->container->get('visitors.event_plugin_manager');
  }

  /**
   * Tests creating the plugin.
   *
   * @covers ::create
   */
  public function testCreate(): void {
    $plugin = Form::create($this->container, [], 'form', []);
    $this->assertInstanceOf(Form::class, $plugin);
  }

  /**
   * Tests creating the plugin.
   *
   * @covers ::__construct
   */
  public function testConstructor(): void {
    $plugin = new Form([], 'form', [], $this->container->get('current_route_match'));
    $this->assertInstanceOf(Form::class, $plugin);
  }

  /**
   * Tests processing a form view.
   *
   * @covers ::process
   */
  public function testProcessFormView(): void {
    // Create a mock route match.
    $route = new Route('/test-form');
    $route_match = new RouteMatch(
      'test.form',
      $route,
      [],
      []
    );
    $this->container->set('current_route_match', $route_match);

    /** @var \Drupal\visitors\Plugin\VisitorsEvent\Form $plugin */
    $plugin = $this->eventPluginManager->createInstance('form');

    // Simulate a form being processed.
    $form_id = 'test_form';
    $base_form_id = 'test_base_form';

    // Set the form info in drupal_static.
    $forms = &drupal_static('visitors_form_alter', []);
    $forms[$form_id] = $base_form_id;

    // Test viewing a form.
    $context = [
      'route' => 'test.form',
      'path' => '/test-form',
    ];
    $result = $plugin->process($context);

    $this->assertNotNull($result);
    $this->assertEquals('view', $result['event']);
    $this->assertEquals($base_form_id, $result['variables']['plugin_var_3']);
    $this->assertEquals($form_id, $result['variables']['plugin_var_4']);
  }

  /**
   * Tests processing a form validation.
   *
   * @covers ::process
   * @covers ::validate
   */
  public function testProcessFormValidate(): void {
    // Create a mock route match.
    $route = new Route('/test-form');
    $route_match = new RouteMatch(
      'test.form',
      $route,
      [],
      []
    );
    $this->container->set('current_route_match', $route_match);

    /** @var \Drupal\visitors\Plugin\VisitorsEvent\Form $plugin */
    $plugin = $this->eventPluginManager->createInstance('form');

    // Simulate a form being processed.
    $form_id = 'test_form';
    $base_form_id = 'test_base_form';

    // Set the form info in drupal_static.
    $forms = &drupal_static('visitors_form_alter', []);
    $forms[$form_id] = $base_form_id;

    // Set validation info in drupal_static.
    $form_validate = &drupal_static('visitors_form_validate', []);
    $form_validate[$form_id] = $base_form_id;

    // Test form validation.
    $context = [
      'route' => 'test.form',
      'path' => '/test-form',
    ];
    $result = $plugin->process($context);

    $this->assertNotNull($result);
    $this->assertEquals('validate', $result['event']);
    $this->assertEquals($base_form_id, $result['variables']['plugin_var_3']);
    $this->assertEquals($form_id, $result['variables']['plugin_var_4']);
  }

  /**
   * Tests processing a form submission.
   *
   * @covers ::process
   * @covers ::submit
   */
  public function testProcessFormSubmit(): void {
    // Create a mock route match.
    $route = new Route('/test-form');
    $route_match = new RouteMatch(
      'test.form',
      $route,
      [],
      []
    );
    $this->container->set('current_route_match', $route_match);

    /** @var \Drupal\visitors\Plugin\VisitorsEvent\Form $plugin */
    $plugin = $this->eventPluginManager->createInstance('form');

    // Simulate a form being processed.
    $form_id = 'test_form';
    $base_form_id = 'test_base_form';

    // Set the form info in drupal_static.
    $forms = &drupal_static('visitors_form_alter', []);
    $forms[$form_id] = $base_form_id;

    // Set submit info in drupal_static.
    $form_submit = &drupal_static('visitors_form_submit', []);
    $form_submit[$form_id] = $base_form_id;

    // Test form submission.
    $context = [
      'route' => 'test.form',
      'path' => '/test-form',
    ];
    $result = $plugin->process($context);

    $this->assertNotNull($result);
    $this->assertEquals('submit', $result['event']);
    $this->assertEquals($base_form_id, $result['variables']['plugin_var_3']);
    $this->assertEquals($form_id, $result['variables']['plugin_var_4']);
  }

  /**
   * Tests processing with multiple forms.
   *
   * @covers ::process
   */
  public function testProcessWithMultipleForms(): void {
    // Create a mock route match.
    $route = new Route('/test-form');
    $route_match = new RouteMatch(
      'test.form',
      $route,
      [],
      []
    );
    $this->container->set('current_route_match', $route_match);

    /** @var \Drupal\visitors\Plugin\VisitorsEvent\Form $plugin */
    $plugin = $this->eventPluginManager->createInstance('form');

    // Simulate multiple forms being processed.
    $forms = &drupal_static('visitors_form_alter', []);
    $forms['test_form_1'] = 'test_base_form_1';
    $forms['test_form_2'] = 'test_base_form_2';

    // Test with multiple forms.
    $context = [
      'route' => 'test.form',
      'path' => '/test-form',
    ];
    $result = $plugin->process($context);

    $this->assertNull($result);
  }

}
