<?php

namespace App\Drivers;

use PDO;
use PDOException;

/**
 * Microservice Database Connection Driver
 * Handles database connections for all microservices with proper error handling
 * and connection pooling support
 * 
 * @category Database
 * @package App\Shared\Drivers
 * @author Architecture Team
 * @version 1.0.0
 */
class DatabaseDriver
{
    private static $instances = [];
    private $connection;
    private $host;
    private $port;
    private $database;
    private $username;
    private $password;
    private $options;

    /**
     * DatabaseDriver constructor
     * 
     * @param string $host Database host
     * @param int $port Database port
     * @param string $database Database name
     * @param string $username Database username
     * @param string $password Database password
     * @param array $options PDO options
     */
    public function __construct(
        string $host,
        int $port,
        string $database,
        string $username,
        string $password,
        array $options = []
    ) {
        $this->host = ($host === 'localhost') ? '127.0.0.1' : $host;
        $this->port = $port;
        $this->database = $database;
        $this->username = $username;
        $this->password = $password;
        
        // Default PDO options
        $this->options = array_merge([
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_PERSISTENT => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
        ], $options);
    }

    /**
     * Get singleton instance of database connection
     * 
     * @param string $connectionId Unique identifier for connection
     * @return DatabaseDriver
     */
    public static function getInstance(string $connectionId = 'default'): DatabaseDriver
    {
        if (!isset(self::$instances[$connectionId])) {
            throw new \RuntimeException("Database connection '$connectionId' not initialized");
        }
        return self::$instances[$connectionId];
    }

    /**
     * Register a database connection instance
     * 
     * @param string $connectionId Unique identifier for connection
     * @param DatabaseDriver $driver
     * @return void
     */
    public static function registerInstance(string $connectionId, DatabaseDriver $driver): void
    {
        self::$instances[$connectionId] = $driver;
    }

    /**
     * Establish database connection
     * 
     * @return PDO
     * @throws PDOException
     */
    public function connect(): PDO
    {
        if ($this->connection !== null) {
            return $this->connection;
        }

        try {
            $dsn = sprintf(
                'mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4',
                $this->host,
                $this->port,
                $this->database
            );

            $this->connection = new PDO(
                $dsn,
                $this->username,
                $this->password,
                $this->options
            );

            return $this->connection;
        } catch (PDOException $e) {
            error_log("Database Connection Error: " . $e->getMessage());
            throw new PDOException(
                "Failed to connect to database: {$this->database}",
                (int)$e->getCode(),
                $e
            );
        }
    }

    /**
     * Get current connection
     * 
     * @return PDO
     */
    public function getConnection(): PDO
    {
        if ($this->connection === null) {
            $this->connect();
        }
        return $this->connection;
    }

    /**
     * Close database connection
     * 
     * @return void
     */
    public function disconnect(): void
    {
        $this->connection = null;
    }

    /**
     * Test connection
     * 
     * @return bool
     */
    public function testConnection(): bool
    {
        try {
            $pdo = $this->connect();
            $pdo->query('SELECT 1');
            return true;
        } catch (PDOException $e) {
            error_log("Connection test failed: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Get database info
     * 
     * @return array
     */
    public function getDatabaseInfo(): array
    {
        try {
            $pdo = $this->getConnection();
            $result = $pdo->query("SELECT DATABASE() as db, VERSION() as version, NOW() as time")->fetch();
            return [
                'database' => $result['db'],
                'version' => $result['version'],
                'timestamp' => $result['time'],
                'connection_status' => 'connected'
            ];
        } catch (PDOException $e) {
            return ['error' => $e->getMessage()];
        }
    }
}
