<?php
/**
 * Schema installer abstraction.
 *
 * affilun applies DDL (CREATE TABLE, ALTER TABLE) through
 * WordPress's `dbDelta()` helper, which inspects the live schema and
 * emits the narrowest set of ALTER statements needed to reach the
 * desired shape. Routing every DDL call through this narrow interface
 * gives migration classes a seam they can unit-test against: production
 * code uses {@see WpdbSchemaInstaller}, tests inject an in-memory fake
 * that records the issued SQL for assertion.
 *
 * Implementations MUST:
 *
 *   - Accept a single fully-formed `CREATE TABLE` statement per call.
 *     Batched or multi-statement SQL is explicitly out of scope; each
 *     migration step should call {@see self::install()} once per table
 *     so that failures localize to the offending table.
 *
 *   - Be idempotent at the DDL level. Calling {@see self::install()}
 *     with the same SQL against an already-installed table MUST be a
 *     safe no-op (or, at worst, issue only the ALTERs needed to bring
 *     the live schema in line with the supplied definition). This
 *     matches the contract of {@see Migration::up()} and lets the
 *     migration runner safely resume after a partial failure.
 *
 *   - Return the list of messages produced by the underlying installer
 *     (for `dbDelta()`, the array of "created" / "altered" strings it
 *     normally prints). Callers use the list for diagnostics and
 *     structured logging; an empty array signals "no change required".
 *
 *   - NOT throw for ordinary "table already exists" outcomes. Those
 *     are the nominal no-op path. Implementations SHOULD throw only
 *     when the DDL itself is malformed or the underlying database
 *     surface reports an error that prevents the schema from reaching
 *     the desired state.
 *
 * Implementations MAY perform additional normalization (for example,
 * collapsing redundant whitespace before passing the SQL to dbDelta),
 * but MUST NOT silently rewrite column or index definitions — those
 * are the migration author's responsibility and must round-trip
 * verbatim.
 *
 * @package Affilun\Infrastructure\Persistence
 */

declare(strict_types=1);

namespace Affilun\Infrastructure\Persistence;

/**
 * Narrow DDL-application contract over WordPress's `dbDelta()`.
 *
 * See the file-level docblock for idempotency, error signaling, and
 * the one-table-per-call rule.
 */
interface SchemaInstaller {

	/**
	 * Apply a single `CREATE TABLE` DDL statement.
	 *
	 * The supplied SQL MUST define exactly one table and MUST be
	 * formatted according to the conventions that `dbDelta()` requires
	 * (one column per line, two spaces after `PRIMARY KEY`, explicit
	 * index names, no trailing comma, no transactional wrapping). The
	 * migration author is responsible for producing well-formed SQL;
	 * this interface does not validate it beyond passing it through to
	 * the underlying installer.
	 *
	 * Idempotency: repeated calls with the same SQL against an
	 * already-installed table MUST be safe and SHOULD be a no-op. This
	 * allows the {@see MigrationRunner} to retry after a partial
	 * failure without double-applying.
	 *
	 * Return value: the list of diagnostic messages produced by the
	 * underlying installer. For `dbDelta()` this is the array of
	 * human-readable "created table" / "altered column" strings that
	 * it normally prints to stdout. An empty array signals "no change
	 * required". Callers SHOULD treat these messages as opaque text
	 * suitable for logging and MUST NOT parse them for branching
	 * decisions.
	 *
	 * @param string $createTableSql Fully-formed `CREATE TABLE` DDL.
	 *                               Must define exactly one table and
	 *                               follow dbDelta formatting rules.
	 *
	 * @return list<string> Diagnostic messages from the underlying
	 *                      installer; empty when no change was needed.
	 */
	public function install( string $createTableSql ): array;
}
