<?php
declare( strict_types = 1 );

namespace MediaWiki\Composer\PhpUnitSplitter;

class PhpUnitFailure {

	private const STATE_EXPECT_TEST_ID_AND_NAME = 0;
	private const STATE_EXPECT_FAILURE_DETAIL = 1;
	private const STATE_EXPECT_LOGS = 2;

	private int $failureNumber = 0;
	private string $testCase;
	private ?string $dataSet = null;
	private ?string $failureDetail;
	private ?string $logs;
	private int $state = self::STATE_EXPECT_TEST_ID_AND_NAME;

	public function getFailureDetails(): ?string {
		$result = $this->failureNumber . ") " . $this->testCase;
		if ( $this->dataSet ) {
			$result .= " with data set " . $this->dataSet;
		}
		$result .= PHP_EOL;
		$result .= $this->failureDetail;
		return $result;
	}

	public function empty(): bool {
		return $this->failureNumber === 0;
	}

	/**
	 * @throws PhpUnitConsoleOutputProcessingException
	 */
	public function processLine( string $line ): bool {
		$matches = [];
		switch ( $this->state ) {
			case self::STATE_EXPECT_TEST_ID_AND_NAME:
				if ( preg_match(
					"/^(\d+)\) (.*::[^\b]+)( with data set (.*))?$/",
					$line,
					$matches,
					PREG_UNMATCHED_AS_NULL
				) ) {
					$this->failureNumber = intval( $matches[1] );
					$this->testCase = $matches[2];
					if ( $matches[3] !== null ) {
						$this->dataSet = $matches[4];
					}
					$this->state = self::STATE_EXPECT_FAILURE_DETAIL;
					$this->failureDetail = "";
				}
				break;

			case self::STATE_EXPECT_FAILURE_DETAIL:
				if ( $line === "=== Logs generated by test case" ) {
					$this->state = self::STATE_EXPECT_LOGS;
					$this->logs = "";
					break;
				}
				if ( preg_match( "/^(\d+)\) (.*::[^\b]+)/", $line ) ) {
					// Start of next error case
					return false;
				}
				$this->failureDetail .= $line . PHP_EOL;
				break;

			case self::STATE_EXPECT_LOGS:
				if ( preg_match( "/^(\d+)\) (.*::[^\b]+)/", $line ) ||
					$line === "===" ) {
					// Start of next error case
					return false;
				}
				$this->logs .= $line . PHP_EOL;
				break;

			default:
				throw new PhpUnitConsoleOutputProcessingException(
					"Unexpected processing state " . $this->state
				);
		}
		return true;
	}
}
