<?php

namespace Drupal\Tests\eb\Unit\Result;

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

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

  /**
   * Tests new result is not successful by default.
   *
   * @covers ::isSuccessful
   * @covers ::isSuccess
   * @covers ::__construct
   */
  public function testNewResultIsNotSuccessfulByDefault(): void {
    $result = new ExecutionResult();

    $this->assertFalse($result->isSuccessful());
    $this->assertFalse($result->isSuccess());
  }

  /**
   * Tests successful result.
   *
   * @covers ::isSuccessful
   */
  public function testSuccessfulResult(): void {
    $result = new ExecutionResult(successful: TRUE);

    $this->assertTrue($result->isSuccessful());
    $this->assertTrue($result->isSuccess());
  }

  /**
   * Tests timestamp is auto-set.
   *
   * @covers ::getTimestamp
   * @covers ::__construct
   */
  public function testTimestampAutoSet(): void {
    $before = time();
    $result = new ExecutionResult();
    $after = time();

    $this->assertGreaterThanOrEqual($before, $result->getTimestamp());
    $this->assertLessThanOrEqual($after, $result->getTimestamp());
  }

  /**
   * Tests explicit timestamp.
   *
   * @covers ::getTimestamp
   */
  public function testExplicitTimestamp(): void {
    $timestamp = 1700000000;
    $result = new ExecutionResult(timestamp: $timestamp);

    $this->assertEquals($timestamp, $result->getTimestamp());
  }

  /**
   * Tests adding affected entity.
   *
   * @covers ::addAffectedEntity
   * @covers ::getAffectedEntities
   */
  public function testAddAffectedEntity(): void {
    $result = new ExecutionResult();

    $entityInfo = ['entity_type' => 'node', 'entity_id' => '1'];
    $result->addAffectedEntity($entityInfo);

    $entities = $result->getAffectedEntities();
    $this->assertCount(1, $entities);
    $this->assertEquals('node', $entities[0]['entity_type']);
  }

  /**
   * Tests adding affected entity by ID.
   *
   * @covers ::addAffectedEntityById
   */
  public function testAddAffectedEntityById(): void {
    $result = new ExecutionResult();

    $result->addAffectedEntityById('node_type', 'article');

    $entities = $result->getAffectedEntities();
    $this->assertCount(1, $entities);
    $this->assertEquals('node_type', $entities[0]['entity_type']);
    $this->assertEquals('article', $entities[0]['entity_id']);
  }

  /**
   * Tests rollback data.
   *
   * @covers ::setRollbackData
   * @covers ::getRollbackData
   */
  public function testRollbackData(): void {
    $result = new ExecutionResult();

    $rollbackData = [
      'operation' => 'delete_bundle',
      'bundle_id' => 'article',
    ];
    $result->setRollbackData($rollbackData);

    $this->assertEquals($rollbackData, $result->getRollbackData());
  }

  /**
   * Tests constructor with rollback data.
   *
   * @covers ::__construct
   */
  public function testConstructorWithRollbackData(): void {
    $rollbackData = ['key' => 'value'];
    $result = new ExecutionResult(rollbackData: $rollbackData);

    $this->assertEquals($rollbackData, $result->getRollbackData());
  }

  /**
   * Tests adding a message.
   *
   * @covers ::addMessage
   * @covers ::getMessages
   */
  public function testAddMessage(): void {
    $result = new ExecutionResult();

    $result->addMessage('Operation completed');
    $result->addMessage('Additional info');

    $messages = $result->getMessages();
    $this->assertCount(2, $messages);
    $this->assertEquals('Operation completed', $messages[0]);
    $this->assertEquals('Additional info', $messages[1]);
  }

  /**
   * Tests adding an error marks result as unsuccessful.
   *
   * @covers ::addError
   * @covers ::getErrors
   */
  public function testAddErrorMarksAsUnsuccessful(): void {
    $result = new ExecutionResult(successful: TRUE);

    $this->assertTrue($result->isSuccessful());

    $result->addError('Something went wrong');

    $this->assertFalse($result->isSuccessful());
    $errors = $result->getErrors();
    $this->assertCount(1, $errors);
    $this->assertEquals('Something went wrong', $errors[0]);
  }

  /**
   * Tests adding multiple errors.
   *
   * @covers ::addError
   */
  public function testAddMultipleErrors(): void {
    $result = new ExecutionResult();

    $result->addError('Error 1');
    $result->addError('Error 2');
    $result->addError('Error 3');

    $errors = $result->getErrors();
    $this->assertCount(3, $errors);
  }

  /**
   * Tests constructor with all parameters.
   *
   * @covers ::__construct
   */
  public function testConstructorWithAllParameters(): void {
    $result = new ExecutionResult(
      successful: TRUE,
      affectedEntities: [['entity_type' => 'node', 'entity_id' => '1']],
      rollbackData: ['action' => 'delete'],
      messages: ['Created successfully'],
      errors: [],
      timestamp: 1700000000
    );

    $this->assertTrue($result->isSuccessful());
    $this->assertCount(1, $result->getAffectedEntities());
    $this->assertEquals(['action' => 'delete'], $result->getRollbackData());
    $this->assertCount(1, $result->getMessages());
    $this->assertEmpty($result->getErrors());
    $this->assertEquals(1700000000, $result->getTimestamp());
  }

  /**
   * Tests empty arrays by default.
   *
   * @covers ::getAffectedEntities
   * @covers ::getRollbackData
   * @covers ::getMessages
   * @covers ::getErrors
   */
  public function testEmptyArraysByDefault(): void {
    $result = new ExecutionResult();

    $this->assertIsArray($result->getAffectedEntities());
    $this->assertEmpty($result->getAffectedEntities());
    $this->assertIsArray($result->getRollbackData());
    $this->assertEmpty($result->getRollbackData());
    $this->assertIsArray($result->getMessages());
    $this->assertEmpty($result->getMessages());
    $this->assertIsArray($result->getErrors());
    $this->assertEmpty($result->getErrors());
  }

}
