<?php

declare(strict_types=1);

namespace App\Framework\Database;

use App\Framework\Database\ValueObjects\SqlQuery;

/**
 * Represents a connection wrapper in a connection pool.
 *
 * The PooledConnection class acts as a proxy for an actual database connection, managing the lifecycle
 * of the underlying connection and facilitating its return to the connection pool upon release or destruction.
 * It delegates operations to the underlying `ConnectionInterface` implementation.
 */
final class PooledConnection implements ConnectionInterface
{
    private ConnectionInterface $connection;

    private ConnectionPool $pool;

    private string $id;

    private bool $released = false;

    public function __construct(ConnectionInterface $connection, ConnectionPool $pool, string $id)
    {
        $this->connection = $connection;
        $this->pool = $pool;
        $this->id = $id;
    }

    public function execute(SqlQuery $query): int
    {
        $startTime = microtime(true);
        $successful = false;

        try {
            $result = $this->connection->execute($query);
            $successful = true;

            return $result;
        } finally {
            $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds
            $this->pool->recordQuery($this->id, $executionTime, $successful);
        }
    }

    public function query(SqlQuery $query): ResultInterface
    {
        $startTime = microtime(true);
        $successful = false;

        try {
            $result = $this->connection->query($query);
            $successful = true;

            return $result;
        } finally {
            $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds
            $this->pool->recordQuery($this->id, $executionTime, $successful);
        }
    }

    public function queryOne(SqlQuery $query): ?array
    {
        $startTime = microtime(true);
        $successful = false;

        try {
            $result = $this->connection->queryOne($query);
            $successful = true;

            return $result;
        } finally {
            $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds
            $this->pool->recordQuery($this->id, $executionTime, $successful);
        }
    }

    public function queryColumn(SqlQuery $query): array
    {
        $startTime = microtime(true);
        $successful = false;

        try {
            $result = $this->connection->queryColumn($query);
            $successful = true;

            return $result;
        } finally {
            $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds
            $this->pool->recordQuery($this->id, $executionTime, $successful);
        }
    }

    public function queryScalar(SqlQuery $query): mixed
    {
        $startTime = microtime(true);
        $successful = false;

        try {
            $result = $this->connection->queryScalar($query);
            $successful = true;

            return $result;
        } finally {
            $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds
            $this->pool->recordQuery($this->id, $executionTime, $successful);
        }
    }

    public function beginTransaction(): void
    {
        $this->connection->beginTransaction();
    }

    public function commit(): void
    {
        $this->connection->commit();
    }

    public function rollback(): void
    {
        $this->connection->rollback();
    }

    public function inTransaction(): bool
    {
        return $this->connection->inTransaction();
    }

    public function lastInsertId(): string
    {
        return $this->connection->lastInsertId();
    }

    public function getPdo(): \PDO
    {
        return $this->connection->getPdo();
    }

    public function release(): void
    {
        if (! $this->released) {
            $this->pool->releaseConnection($this->id);
            $this->released = true;
        }
    }

    public function __destruct()
    {
        $this->release();
    }
}
