<?php

declare(strict_types=1);

namespace ApiModel\Infrastructure\Persistence;

use PDO;
use PDOException;

final class Database
{
    public static function connect(): PDO
    {
        $dsn = $_ENV['DB_DSN'] ?? '';
        $appEnv = strtolower(trim((string) ($_ENV['APP_ENV'] ?? 'production')));

        if ($dsn === '' && $appEnv === 'production') {
            throw new \RuntimeException('DB_DSN es obligatorio cuando APP_ENV=production');
        }

        if ($dsn === '') {
            $host = $_ENV['DB_HOST'] ?? '';
            $port = $_ENV['DB_PORT'] ?? '5432';
            $name = $_ENV['DB_NAME'] ?? '';
            if ($host !== '' && $name !== '') {
                $dsn = "pgsql:host={$host};port={$port};dbname={$name}";
            } else {
                $dsn = 'sqlite:' . dirname(__DIR__, 3) . '/data/app.db';
            }
        }
        if (str_starts_with($dsn, 'sqlite:') && ! str_contains($dsn, '/')) {
            $path = substr($dsn, 7);
            if ($path !== ':memory:' && $path[0] !== '/') {
                $dsn = 'sqlite:' . dirname(__DIR__, 3) . '/' . $path;
            }
        }

        $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC];

        try {
            if (str_starts_with($dsn, 'sqlite:')) {
                $dir = dirname(substr($dsn, 7));
                if ($dir !== '' && $dir !== '.' && ! is_dir($dir)) {
                    mkdir($dir, 0755, true);
                }
                return new PDO($dsn, null, null, $opts);
            }
            $user = $_ENV['DB_USERNAME'] ?? $_ENV['DB_USER'] ?? 'app';
            $pass = $_ENV['DB_PASSWORD'] ?? $_ENV['DB_PASS'] ?? 'app';
            return new PDO($dsn, $user, $pass, $opts);
        } catch (PDOException $e) {
            throw new \RuntimeException('No se pudo conectar a la base de datos: ' . $e->getMessage(), 0, $e);
        }
    }

    public static function uuid(): string
    {
        $b = random_bytes(16);
        $b[6] = chr(ord($b[6]) & 0x0f | 0x40);
        $b[8] = chr(ord($b[8]) & 0x3f | 0x80);

        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($b), 4));
    }

    /** Valor para columnas BOOLEAN en PostgreSQL vía PDO (evita que false se envíe como ''). */
    public static function bool(bool $value): string
    {
        return $value ? 'true' : 'false';
    }

    /** Interpreta un valor booleano leído de la BD (PostgreSQL puede devolver 't'/'f'). */
    public static function fromBool(mixed $value): bool
    {
        if (is_bool($value)) {
            return $value;
        }

        if (is_string($value)) {
            $normalized = strtolower($value);

            return $normalized === 't' || $normalized === 'true' || $normalized === '1';
        }

        return (bool) $value;
    }
}
