<?php
/**
 * Scheduler-owned transaction boundary helper.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Database;

use ScheduledChanges\Support\WpdbConnection;

// phpcs:disable WordPress.DB -- Scheduler-owned custom tables require direct wpdb queries.

/**
 * Runs transactional statements only when the relevant scheduler tables support them.
 */
final class SchedulerTransaction {
	private \wpdb $wpdb;
	private TableHealth $health;
	/**
	 * @var array<string,mixed>
	 */
	private array $last_diagnostic = array(
		'requested_tables'    => array(),
		'transaction_capable' => false,
		'statements_executed' => false,
		'non_transactional'   => array(),
		'missing'             => array(),
		'tables'              => array(),
	);

	/**
	 * Creates the transaction helper.
	 *
	 * @param \wpdb|null       $database WordPress database connection.
	 * @param TableNames|null  $tables Scheduler table resolver.
	 * @param TableHealth|null $health Table health inspector.
	 */
	public function __construct( ?\wpdb $database = null, ?TableNames $tables = null, ?TableHealth $health = null ) {
		$database     = WpdbConnection::resolve( $database );
		$table_names  = $tables ?? new TableNames( $database );
		$this->wpdb   = $database;
		$this->health = $health ?? new TableHealth( $database, $table_names );
	}

	/**
	 * Runs a scheduler-owned sequence with transaction statements when safe.
	 *
	 * @template T
	 *
	 * @param string[]     $table_keys Scheduler table keys used by the sequence.
	 * @param callable():T $callback Work to execute.
	 * @return T
	 */
	public function run( array $table_keys, callable $callback ) {
		$diagnostic                        = $this->health->transactionDiagnostic( $table_keys );
		$diagnostic['statements_executed'] = false;
		$this->last_diagnostic             = $diagnostic;

		if ( ! (bool) $diagnostic['transaction_capable'] ) {
			return $callback();
		}

		$this->last_diagnostic['statements_executed'] = true;
		$this->wpdb->query( 'START TRANSACTION' );

		try {
			$result = $callback();
			$this->wpdb->query( 'COMMIT' );

			return $result;
		} catch ( \Throwable $throwable ) {
			$this->wpdb->query( 'ROLLBACK' );
			throw $throwable;
		}
	}

	/**
	 * Returns diagnostics for the last run.
	 *
	 * @return array<string,mixed>
	 */
	public function lastDiagnostic(): array {
		return $this->last_diagnostic;
	}
}
