<?php

namespace Core;

use PDO;
use PDOException;
use PDOStatement;

class Db
{

    private static PDO $db;
    private static self $instance;

    /**
     * DB constructor.
     * @throws PDOException
     */
    public function __construct()
    {
        try {
            self::$db = Database::pdoinit();
        } catch (PDOException $e) {
            throw new PDOException($e->getMessage());
        }
    }

    /**
     * @param string $query
     * @return int
     */
    public static function exec(string $query): int
    {
        self::init();
        return self::$db->exec($query);
    }

    private static function init(): void
    {
        self::$instance ??= new self();
    }

    /**
     * @param ?string $name [optional] Name of the sequence object from which the ID should be returned.
     * @return string
     */
    public static function lastInsertId(?string $name = null): string
    {
        self::init();
        return self::$db->lastInsertId($name);
    }

    /**
     * @param string $query
     * @param array $args
     * @return array
     */
    public static function getRows(string $query, array $args = []): array
    {
        return self::run($query, $args)->fetchAll();
    }

    /**
     * @param string $query
     * @param array $args
     * @return PDOStatement
     */
    public static function run(string $query, array $args = []): PDOStatement
    {
        try {
            if (!$args) {
                return self::query($query);
            }
            $stmt = self::prepare($query);
            $stmt->execute($args);
            return $stmt;
        } catch (PDOException $e) {
            throw new PDOException($e->getMessage());
        }
    }

    /**
     * @param string $stmt
     * @return PDOStatement
     */
    private static function query(string $stmt): PDOStatement
    {
        self::init();
        return self::$db->query($stmt);
    }

    /**
     * @param string $stmt
     * @return PDOStatement
     */
    public static function prepare(string $stmt): PDOStatement
    {
        self::init();
        return self::$db->prepare($stmt);
    }

    /**
     * @param string $query
     * @param array $args
     * @return mixed
     */
    public static function getValue(string $query, array $args = [])
    {
        $result = self::getRow($query, $args);
        if (!empty($result)) {
            $result = array_shift($result);
        }
        return $result;
    }

    /**
     * @param string $query
     * @param array $args
     * @return mixed
     */
    public static function getRow(string $query, array $args = [])
    {
        return self::run($query, $args)->fetch();
    }

    /**
     * @param string $query
     * @param array $args
     * @return array
     */
    public static function getColumn(string $query, array $args = []): array
    {
        return self::run($query, $args)->fetchAll(PDO::FETCH_COLUMN);
    }

    /**
     * @param string $query
     * @param array $args
     */
    public static function sql(string $query, array $args = [])
    {
        self::run($query, $args);
    }
}