<?php

declare(strict_types=1);

namespace Apexl\Vault\Interfaces;

/**
 * The link to the store, and the one thing that owns a transaction on it.
 *
 * A connection is shared: one lazily-opened handle per named vault per process, which
 * is what every mature data layer does and what ETL throughput depends on. Sharing was
 * never the problem. Having no owner was — vault had no beginTransaction/commit/rollBack
 * at all, so callers reached for the raw handle and each wrapped it in bookkeeping of
 * its own. Wizaplace's Transaction, for instance, tracks whether a transaction is open
 * per instance, while the connection has exactly one: two of those on one handle and the
 * inner one's commit ends the outer one's transaction. Newton's on-demand importer
 * passes `transaction => false` to work around precisely that.
 *
 * transaction() is the answer: depth is counted here, on the object that owns the
 * handle, so a nested call joins the work already in flight instead of ending it.
 */
interface ConnectionInterface
{
    /**
     * The native handle — a PDO for SQL stores, a client for others.
     *
     * For libraries that must be handed the real thing (Wizaplace's ETL manager, the
     * Monolog MySQL handler). Reaching for this to run your own queries or your own
     * transactions is what this interface exists to stop: use the query builder, and
     * transaction() below.
     */
    public function handle(): mixed;

    public function lastInsertId(): int;

    /**
     * Run $work inside a transaction, committing when it returns and rolling back if it
     * throws. Nested calls join the outer transaction rather than starting or ending one
     * of their own, so a helper that wants atomicity can ask for it without having to
     * know whether its caller already did.
     *
     * @template T
     * @param callable(): T $work
     * @return T
     */
    public function transaction(callable $work): mixed;

    public function inTransaction(): bool;
}
