<?php
/**
 * WPDB audit repository.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Repository;

// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception messages are escaped at UI boundaries.

use ScheduledChanges\Audit\AuditRedactor;
use ScheduledChanges\Database\TableNames;
use ScheduledChanges\Domain\Audit\AuditEvent;
use ScheduledChanges\Domain\Shared\Uuid;
use ScheduledChanges\Support\Json;

/**
 * Stores redacted audit events in the scheduler audit table.
 */
final class WpdbAuditRepository implements AuditRepository {
	/**
	 * WordPress database connection.
	 *
	 * @var \wpdb
	 */
	private \wpdb $wpdb;

	/**
	 * Audit event table.
	 *
	 * @var string
	 */
	private string $table;

	/**
	 * Sensitive-value redactor.
	 *
	 * @var AuditRedactor
	 */
	private AuditRedactor $redactor;

	/**
	 * Creates the repository.
	 *
	 * @param \wpdb|null         $database WordPress database connection.
	 * @param TableNames|null    $tables Table resolver.
	 * @param AuditRedactor|null $redactor Context redactor.
	 */
	public function __construct(
		?\wpdb $database = null,
		?TableNames $tables = null,
		?AuditRedactor $redactor = null
	) {
		$database = \ScheduledChanges\Support\WpdbConnection::resolve( $database );

		$this->wpdb     = $database;
		$this->table    = ( $tables ?? new TableNames( $database ) )->get( 'audit_events' );
		$this->redactor = $redactor ?? new AuditRedactor();
	}

	/**
	 * {@inheritDoc}
	 */
	public function record( AuditEvent $event ): int {
		$inserted = $this->wpdb->insert(
			$this->table,
			array(
				'uuid'           => $event->uuid()->value(),
				'launch_id'      => $event->launchId(),
				'run_id'         => $event->runId(),
				'actor_id'       => $event->actorId(),
				'event_type'     => $event->eventType(),
				'severity'       => $event->severity(),
				'context_json'   => Json::encode( $this->redactor->redact( $event->context() ) ),
				'created_at_utc' => $event->createdAt()->toStorage(),
			),
			array( '%s', '%d', '%d', '%d', '%s', '%s', '%s', '%s' )
		);

		if ( false === $inserted ) {
			throw new RepositoryException( 'Unable to record audit event: ' . $this->wpdb->last_error );
		}

		return (int) $this->wpdb->insert_id;
	}

	/**
	 * {@inheritDoc}
	 */
	public function deleteByUuid( Uuid $uuid ): bool {
		$deleted = $this->wpdb->delete(
			$this->table,
			array( 'uuid' => $uuid->value() ),
			array( '%s' )
		);

		if ( false === $deleted ) {
			throw new RepositoryException( 'Unable to delete audit event: ' . $this->wpdb->last_error );
		}

		return $deleted > 0;
	}
}
