<?php

namespace Drupal\Tests\eb\Unit\Plugin\EbValidator;

use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\eb\Plugin\EbValidator\FieldTypeValidator;
use Drupal\Tests\eb\Traits\ValidationAssertionsTrait;
use Drupal\Tests\eb\Unit\EbUnitTestBase;
use PHPUnit\Framework\MockObject\MockObject;

/**
 * Unit tests for FieldTypeValidator plugin.
 *
 * @coversDefaultClass \Drupal\eb\Plugin\EbValidator\FieldTypeValidator
 * @group eb
 */
class FieldTypeValidatorTest extends EbUnitTestBase {

  use ValidationAssertionsTrait;

  /**
   * Mock field type plugin manager.
   */
  protected FieldTypePluginManagerInterface|MockObject $fieldTypeManager;

  /**
   * The validator under test.
   */
  protected FieldTypeValidator $validator;

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

    $this->fieldTypeManager = $this->createMock(FieldTypePluginManagerInterface::class);

    $this->validator = new FieldTypeValidator(
      [],
      'field_type',
      [
        'id' => 'field_type',
        'label' => 'Field Type Validator',
      ],
      $this->fieldTypeManager
    );
  }

  /**
   * Tests validation passes with valid field type.
   *
   * @covers ::validate
   */
  public function testValidatePassesWithValidFieldType(): void {
    $this->fieldTypeManager
      ->method('getDefinitions')
      ->willReturn([
        'text' => ['id' => 'text', 'label' => 'Text'],
        'string' => ['id' => 'string', 'label' => 'String'],
        'integer' => ['id' => 'integer', 'label' => 'Integer'],
      ]);

    $data = ['field_type' => 'text'];
    $result = $this->validator->validate($data);

    $this->assertValidationResultPasses($result);
  }

  /**
   * Tests validation fails with invalid field type.
   *
   * @covers ::validate
   */
  public function testValidateFailsWithInvalidFieldType(): void {
    $this->fieldTypeManager
      ->method('getDefinitions')
      ->willReturn([
        'text' => ['id' => 'text', 'label' => 'Text'],
        'string' => ['id' => 'string', 'label' => 'String'],
      ]);

    $data = ['field_type' => 'invalid_type'];
    $result = $this->validator->validate($data);

    $this->assertValidationResultFails($result);
    $this->assertValidationHasErrorCode($result, 'field_type_not_found');
  }

  /**
   * Tests validation passes when field_type is not present.
   *
   * @covers ::validate
   */
  public function testValidatePassesWhenFieldTypeNotPresent(): void {
    $data = ['entity_type' => 'node', 'bundle' => 'article'];
    $result = $this->validator->validate($data);

    $this->assertValidationResultPasses($result);
  }

  /**
   * Tests validation includes suggestions for similar field types.
   *
   * @covers ::validate
   */
  public function testValidateIncludesSuggestionsForSimilarTypes(): void {
    $this->fieldTypeManager
      ->method('getDefinitions')
      ->willReturn([
        'text' => ['id' => 'text', 'label' => 'Text'],
        'text_long' => ['id' => 'text_long', 'label' => 'Text (long)'],
        'text_with_summary' => ['id' => 'text_with_summary', 'label' => 'Text (with summary)'],
      ]);

    $data = ['field_type' => 'textt'];
    $result = $this->validator->validate($data);

    $this->assertValidationResultFails($result);
    $errors = $result->getErrors();
    $this->assertNotEmpty($errors);
    // Should include suggestions.
    $this->assertStringContainsString('text', (string) $errors[0]['message']);
  }

  /**
   * Tests validation with various valid field types.
   *
   * @covers ::validate
   * @dataProvider validFieldTypeProvider
   */
  public function testValidateWithVariousValidFieldTypes(string $fieldType): void {
    $definitions = [
      'text' => ['id' => 'text'],
      'text_long' => ['id' => 'text_long'],
      'string' => ['id' => 'string'],
      'integer' => ['id' => 'integer'],
      'boolean' => ['id' => 'boolean'],
      'entity_reference' => ['id' => 'entity_reference'],
      'datetime' => ['id' => 'datetime'],
      'link' => ['id' => 'link'],
      'list_string' => ['id' => 'list_string'],
    ];

    $this->fieldTypeManager
      ->method('getDefinitions')
      ->willReturn($definitions);

    $data = ['field_type' => $fieldType];
    $result = $this->validator->validate($data);

    $this->assertValidationResultPasses($result);
  }

  /**
   * Data provider for valid field types.
   *
   * @return array<string, array<string>>
   *   Test cases.
   */
  public static function validFieldTypeProvider(): array {
    return [
      'text' => ['text'],
      'text_long' => ['text_long'],
      'string' => ['string'],
      'integer' => ['integer'],
      'boolean' => ['boolean'],
      'entity_reference' => ['entity_reference'],
      'datetime' => ['datetime'],
      'link' => ['link'],
      'list_string' => ['list_string'],
    ];
  }

  /**
   * Tests validation error contains the invalid field type.
   *
   * @covers ::validate
   */
  public function testValidateErrorContainsInvalidFieldType(): void {
    $this->fieldTypeManager
      ->method('getDefinitions')
      ->willReturn(['text' => ['id' => 'text']]);

    $data = ['field_type' => 'completely_made_up_type'];
    $result = $this->validator->validate($data);

    $this->assertValidationResultFails($result);
    $errors = $result->getErrors();
    $this->assertStringContainsString('completely_made_up_type', (string) $errors[0]['message']);
  }

  /**
   * Tests validation reports correct field path.
   *
   * @covers ::validate
   */
  public function testValidateReportsCorrectFieldPath(): void {
    $this->fieldTypeManager
      ->method('getDefinitions')
      ->willReturn(['text' => ['id' => 'text']]);

    $data = ['field_type' => 'invalid'];
    $result = $this->validator->validate($data);

    $this->assertValidationResultFails($result);
    $errors = $result->getErrors();
    $this->assertEquals('field_type', $errors[0]['field']);
  }

  /**
   * Tests getPluginId returns correct ID.
   *
   * @covers ::getPluginId
   */
  public function testGetPluginIdReturnsCorrectId(): void {
    $this->assertEquals('field_type', $this->validator->getPluginId());
  }

}
