<?php

declare(strict_types=1);

namespace App\Framework\Database\Profiling;

use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ResultInterface;
use App\Framework\Database\ValueObjects\SqlQuery;

/**
 * Connection wrapper that adds profiling capabilities to any database connection
 */
final class ProfilingConnection implements ConnectionInterface
{
    private bool $profilingEnabled = true;

    /** @var array<callable> */
    private array $queryExecutedCallbacks = [];

    public function __construct(
        private readonly ConnectionInterface $connection,
        private readonly QueryProfiler $profiler,
        private readonly ?QueryLogger $logger = null
    ) {
    }

    /**
     * Execute SQL with profiling
     */
    public function execute(SqlQuery $query): int
    {
        if (! $this->profilingEnabled) {
            return $this->connection->execute($query);
        }

        $profile = $this->profiler->profile(
            $query,
            fn () => $this->connection->execute($query)
        );

        $this->logger?->logQuery($profile);
        $this->notifyQueryExecuted($profile);

        return $profile->affectedRows ?? 0;
    }

    /**
     * Query with profiling
     */
    public function query(SqlQuery $query): ResultInterface
    {
        if (! $this->profilingEnabled) {
            return $this->connection->query($query);
        }

        $result = null;
        $profile = $this->profiler->profile(
            $query,
            function () use ($query, &$result) {
                $result = $this->connection->query($query);

                return $result;
            }
        );

        $this->logger?->logQuery($profile);
        $this->notifyQueryExecuted($profile);

        return $result;
    }

    /**
     * Query single row with profiling
     */
    public function queryOne(SqlQuery $query): ?array
    {
        if (! $this->profilingEnabled) {
            return $this->connection->queryOne($query);
        }

        $result = null;
        $profile = $this->profiler->profile(
            $query,
            function () use ($query, &$result) {
                $result = $this->connection->queryOne($query);

                return $result;
            }
        );

        $this->logger?->logQuery($profile);
        $this->notifyQueryExecuted($profile);

        return $result;
    }

    /**
     * Query column with profiling
     */
    public function queryColumn(SqlQuery $query): array
    {
        if (! $this->profilingEnabled) {
            return $this->connection->queryColumn($query);
        }

        $result = null;
        $profile = $this->profiler->profile(
            $query,
            function () use ($query, &$result) {
                $result = $this->connection->queryColumn($query);

                return $result;
            }
        );

        $this->logger?->logQuery($profile);
        $this->notifyQueryExecuted($profile);

        return $result;
    }

    /**
     * Query scalar with profiling
     */
    public function queryScalar(SqlQuery $query): mixed
    {
        if (! $this->profilingEnabled) {
            return $this->connection->queryScalar($query);
        }

        $result = null;
        $profile = $this->profiler->profile(
            $query,
            function () use ($query, &$result) {
                $result = $this->connection->queryScalar($query);

                return $result;
            }
        );

        $this->logger?->logQuery($profile);
        $this->notifyQueryExecuted($profile);

        return $result;
    }

    /**
     * Begin transaction (no profiling needed, but logged)
     */
    public function beginTransaction(): void
    {
        if ($this->profilingEnabled && $this->logger) {
            $query = SqlQuery::create('BEGIN TRANSACTION');
            $profile = $this->profiler->profile(
                $query,
                fn () => $this->connection->beginTransaction()
            );

            $this->logger->logQuery($profile);
        } else {
            $this->connection->beginTransaction();
        }
    }

    /**
     * Commit transaction (no profiling needed, but logged)
     */
    public function commit(): void
    {
        if ($this->profilingEnabled && $this->logger) {
            $query = SqlQuery::create('COMMIT');
            $profile = $this->profiler->profile(
                $query,
                fn () => $this->connection->commit()
            );

            $this->logger->logQuery($profile);
        } else {
            $this->connection->commit();
        }
    }

    /**
     * Rollback transaction (no profiling needed, but logged)
     */
    public function rollback(): void
    {
        if ($this->profilingEnabled && $this->logger) {
            $query = SqlQuery::create('ROLLBACK');
            $profile = $this->profiler->profile(
                $query,
                fn () => $this->connection->rollback()
            );

            $this->logger->logQuery($profile);
        } else {
            $this->connection->rollback();
        }
    }

    /**
     * Check if in transaction
     */
    public function inTransaction(): bool
    {
        return $this->connection->inTransaction();
    }

    /**
     * Get last insert ID
     */
    public function lastInsertId(): string
    {
        return $this->connection->lastInsertId();
    }

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

    /**
     * Enable/disable profiling
     */
    public function setProfilingEnabled(bool $enabled): void
    {
        $this->profilingEnabled = $enabled;
    }

    /**
     * Check if profiling is enabled
     */
    public function isProfilingEnabled(): bool
    {
        return $this->profilingEnabled;
    }

    /**
     * Get the profiler instance
     */
    public function getProfiler(): QueryProfiler
    {
        return $this->profiler;
    }

    /**
     * Get the logger instance
     */
    public function getLogger(): ?QueryLogger
    {
        return $this->logger;
    }

    /**
     * Get the underlying connection
     */
    public function getConnection(): ConnectionInterface
    {
        return $this->connection;
    }

    /**
     * Get profiling statistics
     */
    public function getProfilingStatistics(): array
    {
        $stats = [
            'profiling_enabled' => $this->profilingEnabled,
            'profiler_stats' => [
                'total_profiles' => $this->profiler->getProfilesCount(),
                'active_profiles' => $this->profiler->getActiveProfilesCount(),
                'slow_query_threshold_ms' => $this->profiler->getSlowQueryThreshold() * 1000,
            ],
        ];

        if ($this->logger) {
            $stats['logger_stats'] = $this->logger->getLogStatistics();
        }

        return $stats;
    }

    /**
     * Clear all profiling data
     */
    public function clearProfilingData(): void
    {
        $this->profiler->clearProfiles();
        $this->logger?->clearLog();
    }

    /**
     * Get profiling summary
     */
    public function getProfilingSummary(): ProfileSummary
    {
        return $this->profiler->getSummary();
    }

    /**
     * Export profiling data
     */
    public function exportProfilingData(string $format = 'json'): string
    {
        if (! $this->logger) {
            throw new \RuntimeException('No logger configured for export');
        }

        return $this->logger->export($format);
    }

    /**
     * Register callback to be called after each query execution
     */
    public function onQueryExecuted(callable $callback): void
    {
        $this->queryExecutedCallbacks[] = $callback;
    }

    /**
     * Notify all registered callbacks about query execution
     */
    private function notifyQueryExecuted(QueryProfile $profile): void
    {
        foreach ($this->queryExecutedCallbacks as $callback) {
            $callback($profile);
        }
    }
}
