<?php

/**
 * Фасад для роботи з базою даних
 *
 * @package Flowaxy\Support\Facades
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Support\Facades;

use Flowaxy\Infrastructure\Persistence\Database\Database as DatabaseInstance;
use Flowaxy\Infrastructure\Persistence\Database\QueryBuilder;

final class Database extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return DatabaseInstance::class;
    }

    /**
     * Отримати підключення
     *
     * @return \PDO
     */
    public static function connection(): \PDO
    {
        return DatabaseInstance::getInstance()->getConnection();
    }

    /**
     * Почати запит з таблиці
     *
     * @param string $table
     * @return QueryBuilder
     */
    public static function table(string $table): QueryBuilder
    {
        return new QueryBuilder(DatabaseInstance::getInstance());
    }

    /**
     * Виконати SELECT запит
     *
     * @param string $query
     * @param array<int, mixed> $params
     * @return array<int, array<string, mixed>>
     */
    public static function select(string $query, array $params = []): array
    {
        return DatabaseInstance::getInstance()->getAll($query, $params);
    }

    /**
     * Виконати INSERT запит
     *
     * @param string $table
     * @param array<string, mixed> $values
     * @return bool
     */
    public static function insert(string $table, array $values): bool
    {
        $columns = array_keys($values);
        $placeholders = array_map(fn($col) => ":{$col}", $columns);
        $sql = "INSERT INTO {$table} (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")";

        try {
            $stmt = DatabaseInstance::getInstance()->getConnection()->prepare($sql);
            foreach ($values as $key => $value) {
                $stmt->bindValue(":{$key}", $value);
            }

            return $stmt->execute();
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Виконати UPDATE запит
     *
     * @param string $table
     * @param array<string, mixed> $values
     * @param array<string, mixed> $where
     * @return bool
     */
    public static function update(string $table, array $values, array $where): bool
    {
        $set = [];
        foreach (array_keys($values) as $column) {
            $set[] = "{$column} = :set_{$column}";
        }

        $whereClause = [];
        foreach (array_keys($where) as $column) {
            $whereClause[] = "{$column} = :where_{$column}";
        }

        $sql = "UPDATE {$table} SET " . implode(', ', $set) . " WHERE " . implode(' AND ', $whereClause);

        try {
            $stmt = DatabaseInstance::getInstance()->getConnection()->prepare($sql);
            foreach ($values as $key => $value) {
                $stmt->bindValue(":set_{$key}", $value);
            }
            foreach ($where as $key => $value) {
                $stmt->bindValue(":where_{$key}", $value);
            }

            return $stmt->execute();
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Виконати DELETE запит
     *
     * @param string $table
     * @param array<string, mixed> $where
     * @return bool
     */
    public static function delete(string $table, array $where): bool
    {
        $whereClause = [];
        foreach (array_keys($where) as $column) {
            $whereClause[] = "{$column} = :{$column}";
        }

        $sql = "DELETE FROM {$table} WHERE " . implode(' AND ', $whereClause);

        try {
            $stmt = DatabaseInstance::getInstance()->getConnection()->prepare($sql);
            foreach ($where as $key => $value) {
                $stmt->bindValue(":{$key}", $value);
            }

            return $stmt->execute();
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Виконати запит
     *
     * @param string $query
     * @param array<int, mixed> $params
     * @return \PDOStatement
     */
    public static function query(string $query, array $params = []): \PDOStatement
    {
        return DatabaseInstance::getInstance()->query($query, $params);
    }

    /**
     * Почати транзакцію
     *
     * @return bool
     */
    public static function beginTransaction(): bool
    {
        return DatabaseInstance::getInstance()->getConnection()->beginTransaction();
    }

    /**
     * Підтвердити транзакцію
     *
     * @return bool
     */
    public static function commit(): bool
    {
        return DatabaseInstance::getInstance()->getConnection()->commit();
    }

    /**
     * Відкотити транзакцію
     *
     * @return bool
     */
    public static function rollback(): bool
    {
        return DatabaseInstance::getInstance()->getConnection()->rollBack();
    }

    /**
     * Виконати код в транзакції
     *
     * @param callable $callback
     * @return mixed
     * @throws \Exception
     */
    public static function transaction(callable $callback): mixed
    {
        self::beginTransaction();

        try {
            $result = $callback();
            self::commit();

            return $result;
        } catch (\Exception $e) {
            self::rollback();
            throw $e;
        }
    }
}
