<?php

namespace Drupal\Tests\eb\Unit\Result;

use Drupal\eb\Result\PreviewResult;
use Drupal\Tests\UnitTestCase;

/**
 * Unit tests for PreviewResult value object.
 *
 * @coversDefaultClass \Drupal\eb\Result\PreviewResult
 * @group eb
 */
class PreviewResultTest extends UnitTestCase {

  /**
   * Tests new result has empty description.
   *
   * @covers ::getDescription
   * @covers ::__construct
   */
  public function testNewResultHasEmptyDescription(): void {
    $result = new PreviewResult();

    $this->assertEquals('', $result->getDescription());
  }

  /**
   * Tests constructor with description.
   *
   * @covers ::__construct
   */
  public function testConstructorWithDescription(): void {
    $result = new PreviewResult(description: 'Will create bundle article');

    $this->assertEquals('Will create bundle article', $result->getDescription());
  }

  /**
   * Tests adding created entity.
   *
   * @covers ::addCreatedEntity
   * @covers ::getCreatedEntities
   */
  public function testAddCreatedEntity(): void {
    $result = new PreviewResult();

    $entityInfo = ['entity_type' => 'node_type', 'entity_id' => 'article'];
    $result->addCreatedEntity($entityInfo);

    $created = $result->getCreatedEntities();
    $this->assertCount(1, $created);
    $this->assertEquals('node_type', $created[0]['entity_type']);
  }

  /**
   * Tests adding modified entity.
   *
   * @covers ::addModifiedEntity
   * @covers ::getModifiedEntities
   */
  public function testAddModifiedEntity(): void {
    $result = new PreviewResult();

    $entityInfo = [
      'entity_type' => 'field_config',
      'before' => ['label' => 'Old Label'],
      'after' => ['label' => 'New Label'],
    ];
    $result->addModifiedEntity($entityInfo);

    $modified = $result->getModifiedEntities();
    $this->assertCount(1, $modified);
    $this->assertEquals('Old Label', $modified[0]['before']['label']);
    $this->assertEquals('New Label', $modified[0]['after']['label']);
  }

  /**
   * Tests adding deleted entity.
   *
   * @covers ::addDeletedEntity
   * @covers ::getDeletedEntities
   */
  public function testAddDeletedEntity(): void {
    $result = new PreviewResult();

    $entityInfo = ['entity_type' => 'field_config', 'entity_id' => 'field_test'];
    $result->addDeletedEntity($entityInfo);

    $deleted = $result->getDeletedEntities();
    $this->assertCount(1, $deleted);
    $this->assertEquals('field_test', $deleted[0]['entity_id']);
  }

  /**
   * Tests adding warning.
   *
   * @covers ::addWarning
   * @covers ::getWarnings
   * @covers ::hasWarnings
   */
  public function testAddWarning(): void {
    $result = new PreviewResult();

    $this->assertFalse($result->hasWarnings());

    $result->addWarning('This operation cannot be undone');

    $this->assertTrue($result->hasWarnings());
    $warnings = $result->getWarnings();
    $this->assertCount(1, $warnings);
    $this->assertEquals('This operation cannot be undone', $warnings[0]);
  }

  /**
   * Tests adding operation for create.
   *
   * @covers ::addOperation
   */
  public function testAddOperationCreate(): void {
    $result = new PreviewResult();

    $result->addOperation('create', 'node_type', 'article', 'Create bundle');

    $created = $result->getCreatedEntities();
    $this->assertCount(1, $created);
    $this->assertEquals('create', $created[0]['operation']);
    $this->assertEquals('node_type', $created[0]['entity_type']);
  }

  /**
   * Tests adding operation for update.
   *
   * @covers ::addOperation
   */
  public function testAddOperationUpdate(): void {
    $result = new PreviewResult();

    $result->addOperation('update', 'field_config', 'field_test', 'Update field');

    $modified = $result->getModifiedEntities();
    $this->assertCount(1, $modified);
    $this->assertEquals('update', $modified[0]['operation']);
  }

  /**
   * Tests adding operation for delete.
   *
   * @covers ::addOperation
   */
  public function testAddOperationDelete(): void {
    $result = new PreviewResult();

    $result->addOperation('delete', 'field_config', 'field_test', 'Delete field');

    $deleted = $result->getDeletedEntities();
    $this->assertCount(1, $deleted);
    $this->assertEquals('delete', $deleted[0]['operation']);
  }

  /**
   * Tests getting operation type based on entities.
   *
   * @covers ::getOperationType
   */
  public function testGetOperationType(): void {
    // Create operation.
    $createResult = new PreviewResult();
    $createResult->addCreatedEntity(['entity_type' => 'node_type']);
    $this->assertEquals('create', $createResult->getOperationType());

    // Update operation.
    $updateResult = new PreviewResult();
    $updateResult->addModifiedEntity(['entity_type' => 'field_config']);
    $this->assertEquals('update', $updateResult->getOperationType());

    // Delete operation.
    $deleteResult = new PreviewResult();
    $deleteResult->addDeletedEntity(['entity_type' => 'field_config']);
    $this->assertEquals('delete', $deleteResult->getOperationType());

    // Unknown operation.
    $emptyResult = new PreviewResult();
    $this->assertEquals('unknown', $emptyResult->getOperationType());
  }

  /**
   * Tests adding details to description.
   *
   * @covers ::addDetails
   */
  public function testAddDetails(): void {
    $result = new PreviewResult(description: 'Operation details:');

    $result->addDetails([
      'Field Name' => 'field_test',
      'Field Type' => 'text',
    ]);

    $description = $result->getDescription();
    $this->assertStringContainsString('Field Name: field_test', $description);
    $this->assertStringContainsString('Field Type: text', $description);
  }

  /**
   * Tests adding details with array value extracts type.
   *
   * @covers ::addDetails
   */
  public function testAddDetailsWithArrayValue(): void {
    $result = new PreviewResult();

    $result->addDetails([
      'Widget' => ['type' => 'string_textfield', 'settings' => []],
    ]);

    $description = $result->getDescription();
    $this->assertStringContainsString('Widget: string_textfield', $description);
  }

  /**
   * Tests getting summary (alias for description).
   *
   * @covers ::getSummary
   */
  public function testGetSummary(): void {
    $result = new PreviewResult(description: 'Summary text');

    $this->assertEquals('Summary text', $result->getSummary());
    $this->assertEquals($result->getDescription(), $result->getSummary());
  }

  /**
   * Tests getting details array.
   *
   * @covers ::getDetails
   */
  public function testGetDetailsArray(): void {
    $result = new PreviewResult();
    $result->addCreatedEntity(['entity_type' => 'node_type']);
    $result->addModifiedEntity(['entity_type' => 'field_config']);
    $result->addDeletedEntity(['entity_type' => 'field_storage']);

    $details = $result->getDetails();

    $this->assertArrayHasKey('created', $details);
    $this->assertArrayHasKey('modified', $details);
    $this->assertArrayHasKey('deleted', $details);
    $this->assertCount(1, $details['created']);
    $this->assertCount(1, $details['modified']);
    $this->assertCount(1, $details['deleted']);
  }

  /**
   * Tests constructor with initial entities.
   *
   * @covers ::__construct
   */
  public function testConstructorWithInitialEntities(): void {
    $result = new PreviewResult(
      description: 'Test',
      createdEntities: [['entity_type' => 'node_type']],
      modifiedEntities: [['entity_type' => 'field_config']],
      deletedEntities: [['entity_type' => 'field_storage']],
      warnings: ['Warning 1']
    );

    $this->assertCount(1, $result->getCreatedEntities());
    $this->assertCount(1, $result->getModifiedEntities());
    $this->assertCount(1, $result->getDeletedEntities());
    $this->assertTrue($result->hasWarnings());
  }

  /**
   * Tests empty arrays by default.
   *
   * @covers ::getCreatedEntities
   * @covers ::getModifiedEntities
   * @covers ::getDeletedEntities
   * @covers ::getWarnings
   */
  public function testEmptyArraysByDefault(): void {
    $result = new PreviewResult();

    $this->assertIsArray($result->getCreatedEntities());
    $this->assertEmpty($result->getCreatedEntities());
    $this->assertIsArray($result->getModifiedEntities());
    $this->assertEmpty($result->getModifiedEntities());
    $this->assertIsArray($result->getDeletedEntities());
    $this->assertEmpty($result->getDeletedEntities());
    $this->assertIsArray($result->getWarnings());
    $this->assertEmpty($result->getWarnings());
  }

}
