<?php

namespace App\Core;

class Database
{
    private static ?Database $instance = null;
    private \PDO $connection;

    private function __construct()
    {
        $config = require dirname(__DIR__, 2) . '/config/database.php';
        
        try {
            $dsn = "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
            $this->connection = new \PDO($dsn, $config['username'], $config['password']);
            $this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
            $this->connection->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
            $this->connection->exec("SET NAMES 'utf8'");
        } catch (\PDOException $e) {
            throw new \Exception("Ошибка подключения к базе данных: " . $e->getMessage());
        }
    }

    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

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

    public function query(string $sql, array $params = []): array
    {
        $stmt = $this->connection->prepare($sql);
        $this->bindParams($stmt, $params);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    public function queryOne(string $sql, array $params = []): ?array
    {
        $stmt = $this->connection->prepare($sql);
        $this->bindParams($stmt, $params);
        $stmt->execute();
        $result = $stmt->fetch();
        return $result ?: null;
    }
    
    private function bindParams(\PDOStatement $stmt, array $params): void
    {
        foreach ($params as $index => $param) {
            $paramNum = $index + 1;
            if (is_bool($param)) {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_BOOL);
            } elseif (is_int($param)) {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_INT);
            } elseif (is_null($param)) {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_NULL);
            } else {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_STR);
            }
        }
    }

    public function execute(string $sql, array $params = []): bool
    {
        $stmt = $this->connection->prepare($sql);
        $this->bindParams($stmt, $params);
        return $stmt->execute();
    }

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

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

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

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

    private function __clone() {}
    
    public function __wakeup()
    {
        throw new \Exception("Десериализация Singleton запрещена");
    }
}
