<?php

namespace MintyPHP\Repository\Support;

use MintyPHP\DB;

/** Executes database session operations: advisory locks, transactions, and commit/rollback. */
class DatabaseSessionRepository implements DatabaseSessionRepositoryInterface
{
    public function acquireAdvisoryLock(string $lockName, int $timeoutSeconds = 0): bool
    {
        $lockName = trim($lockName);
        if ($lockName === '') {
            return false;
        }

        $timeoutSeconds = max(0, (int) $timeoutSeconds);
        $got = DB::selectValue(
            'select GET_LOCK(?, ?) as got_lock',
            $lockName,
            (string) $timeoutSeconds
        );

        return (int) $got === 1;
    }

    public function releaseAdvisoryLock(string $lockName): void
    {
        $lockName = trim($lockName);
        if ($lockName === '') {
            return;
        }

        DB::selectValue('select RELEASE_LOCK(?) as released_lock', $lockName);
    }

    public function beginTransaction(): void
    {
        DB::handle()->begin_transaction();
    }

    public function commitTransaction(): void
    {
        DB::handle()->commit();
    }

    public function rollbackTransaction(): void
    {
        DB::handle()->rollback();
    }

    /**
     * Run a callback inside a DB transaction. Commits on success, rolls back on exception.
     *
     * Eliminates the need for manual begin/commit/rollback + rollbackQuietly() boilerplate
     * that is duplicated across multiple services. The callback receives no arguments;
     * use closures to capture dependencies.
     *
     * @template T
     * @param callable(): T $callback
     * @return T The value returned by the callback.
     * @throws \Throwable Re-throws the original exception after rollback.
     */
    public function transaction(callable $callback): mixed
    {
        $this->beginTransaction();
        try {
            $result = $callback();
            $this->commitTransaction();
            return $result;
        } catch (\Throwable $e) {
            try {
                $this->rollbackTransaction();
            } catch (\Throwable) {
                // Swallow — the original exception is more important.
            }
            throw $e;
        }
    }
}
