<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop\Unit\DTO;

use Drupal\Tests\UnitTestCase;
use Drupal\flowdrop\DTO\ValidationResult;
use Drupal\flowdrop\DTO\ValidationError;
use Drupal\flowdrop\DTO\ValidationWarning;

/**
 * Tests the ValidationResult DTO.
 *
 * @coversDefaultClass \Drupal\flowdrop\DTO\ValidationResult
 * @group flowdrop
 */
class ValidationResultTest extends UnitTestCase {

  /**
   * @covers ::success
   * @covers ::isValid
   */
  public function testSuccess(): void {
    $result = ValidationResult::success();
    $this->assertTrue($result->isValid());
    $this->assertFalse($result->hasErrors());
    $this->assertFalse($result->hasWarnings());
  }

  /**
   * @covers ::success
   */
  public function testSuccessWithWarnings(): void {
    $warning = new ValidationWarning("field", "Consider using X");
    $result = ValidationResult::success([$warning]);
    $this->assertTrue($result->isValid());
    $this->assertTrue($result->hasWarnings());
    $this->assertEquals(1, $result->getWarningCount());
  }

  /**
   * @covers ::failure
   * @covers ::isValid
   * @covers ::hasErrors
   */
  public function testFailure(): void {
    $error = new ValidationError("field", "Required");
    $result = ValidationResult::failure([$error]);
    $this->assertFalse($result->isValid());
    $this->assertTrue($result->hasErrors());
    $this->assertEquals(1, $result->getErrorCount());
  }

  /**
   * @covers ::error
   */
  public function testError(): void {
    $result = ValidationResult::error("email", "Invalid email", "ERR_EMAIL");
    $this->assertFalse($result->isValid());
    $this->assertEquals(1, $result->getErrorCount());
    $errors = $result->getErrors();
    $this->assertEquals("email", $errors[0]->getParameter());
    $this->assertEquals("Invalid email", $errors[0]->getMessage());
    $this->assertEquals("ERR_EMAIL", $errors[0]->getCode());
  }

  /**
   * @covers ::getErrors
   * @covers ::getWarnings
   */
  public function testGetErrorsAndWarnings(): void {
    $errors = [new ValidationError("a", "msg_a"), new ValidationError("b", "msg_b")];
    $warnings = [new ValidationWarning("c", "msg_c")];
    $result = ValidationResult::failure($errors, $warnings);
    $this->assertCount(2, $result->getErrors());
    $this->assertCount(1, $result->getWarnings());
  }

  /**
   * @covers ::getErrorCount
   * @covers ::getWarningCount
   */
  public function testCounts(): void {
    $result = ValidationResult::failure(
      [new ValidationError("a", "msg"), new ValidationError("b", "msg")],
      [new ValidationWarning("c", "msg")],
    );
    $this->assertEquals(2, $result->getErrorCount());
    $this->assertEquals(1, $result->getWarningCount());
  }

  /**
   * @covers ::getErrorMessages
   */
  public function testGetErrorMessages(): void {
    $result = ValidationResult::failure([
      new ValidationError("a", "Error A"),
      new ValidationError("b", "Error B"),
    ]);
    $this->assertEquals(["Error A", "Error B"], $result->getErrorMessages());
  }

  /**
   * @covers ::getWarningMessages
   */
  public function testGetWarningMessages(): void {
    $result = ValidationResult::success([
      new ValidationWarning("a", "Warning A"),
      new ValidationWarning("b", "Warning B"),
    ]);
    $this->assertEquals(["Warning A", "Warning B"], $result->getWarningMessages());
  }

  /**
   * @covers ::toArray
   */
  public function testToArray(): void {
    $result = ValidationResult::error("field", "Required", "REQ");
    $array = $result->toArray();
    $this->assertFalse($array["valid"]);
    $this->assertCount(1, $array["errors"]);
    $this->assertEquals("field", $array["errors"][0]["parameter"]);
    $this->assertEquals("Required", $array["errors"][0]["message"]);
    $this->assertEquals("REQ", $array["errors"][0]["code"]);
    $this->assertCount(0, $array["warnings"]);
  }

  /**
   * @covers ::merge
   */
  public function testMerge(): void {
    $result1 = ValidationResult::success([new ValidationWarning("a", "warn")]);
    $result2 = ValidationResult::error("b", "Error");

    $merged = $result1->merge($result2);
    $this->assertFalse($merged->isValid());
    $this->assertEquals(1, $merged->getErrorCount());
    $this->assertEquals(1, $merged->getWarningCount());
  }

  /**
   * @covers ::merge
   */
  public function testMergeTwoSuccesses(): void {
    $result1 = ValidationResult::success();
    $result2 = ValidationResult::success();
    $merged = $result1->merge($result2);
    $this->assertTrue($merged->isValid());
  }

}
