<?php

declare(strict_types=1);

if (!class_exists('PDO') || !(new ReflectionClass('PDO'))->hasConstant('EXTRA_DECIMAL')) {
    fwrite(STDERR, "Candidate PDO_DM with EXTRA_DECIMAL required\n");
    exit(2);
}

class CandidateProbeStatement extends PDOStatement
{
    protected function __construct()
    {
    }

    public function candidateMarker(): string
    {
        return 'candidate-statement';
    }
}

function connectRole(string $role): PDO
{
    $prefix = $role === 'ai' ? 'POC_AI_' : 'POC_';
    foreach (['HOST', 'PORT', 'SCHEMA', 'USER', 'PASSWORD'] as $part) {
        if ((string) getenv($prefix . $part) === '') {
            throw new RuntimeException('Approved non-production runtime environment unavailable');
        }
    }
    $dsn = 'dm:host=' . getenv($prefix . 'HOST') . ';port=' . getenv($prefix . 'PORT')
        . ';schema=' . getenv($prefix . 'SCHEMA');
    return new PDO($dsn, (string) getenv($prefix . 'USER'),
        (string) getenv($prefix . 'PASSWORD'), [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_STRINGIFY_FETCHES => true,
            PDO::EXTRA_DECIMAL => 1,
        ]);
}

function metadata(PDO $pdo, string $sql): array
{
    $statement = $pdo->query($sql);
    $columns = [];
    for ($index = 0; $index < $statement->columnCount(); $index++) {
        try {
            $columns[] = $statement->getColumnMeta($index);
        } catch (Throwable $error) {
            $columns[] = ['error_class' => get_class($error), 'error_code' => $error->getCode()];
        }
    }
    return ['column_count' => $statement->columnCount(), 'columns' => $columns,
        'row_present' => $statement->fetch(PDO::FETCH_NUM) !== false];
}

function statementClass(PDO $pdo): array
{
    $result = ['set_attribute' => null, 'cases' => []];
    try {
        $result['set_attribute'] = $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS,
            [CandidateProbeStatement::class, []]);
    } catch (Throwable $error) {
        $result['set_attribute'] = ['error_class' => get_class($error), 'error_code' => $error->getCode()];
        return $result;
    }
    $sql = "SELECT CAST('9007199254740993' AS DECIMAL(20,0)) AS V FROM DUAL";
    foreach (['fetch', 'fetchAll', 'fetchColumn'] as $method) {
        try {
            $statement = $pdo->query($sql);
            $value = $statement->{$method}();
            $result['cases'][$method] = [
                'class' => get_class($statement),
                'marker' => method_exists($statement, 'candidateMarker')
                    ? $statement->candidateMarker() : null,
                'value' => $value,
                'value_type' => gettype($value),
            ];
        } catch (Throwable $error) {
            $result['cases'][$method] = [
                'error_class' => get_class($error), 'error_code' => $error->getCode(),
            ];
        }
    }
    return $result;
}

$result = ['php_version' => PHP_VERSION, 'pdo_dm_version' => phpversion('PDO_DM'),
    'extra_decimal' => PDO::EXTRA_DECIMAL, 'roles' => []];
foreach (['store', 'ai'] as $role) {
    try {
        $pdo = connectRole($role);
        $queries = $role === 'store'
            ? [
                'base_and_alias' => 'SELECT ID,ACCOUNT_SETTLEMENT_ID,'
                    . ' ACCOUNT_SETTLEMENT_ID AS linked_id'
                    . ' FROM YDY_MEAL_ORDER WHERE 1=0',
                'constant' => "SELECT CAST('18446744073709551615' AS DECIMAL(20,0))"
                    . ' AS ACCOUNT_SETTLEMENT_ID FROM DUAL WHERE 1=0',
            ]
            : [
                'base_and_alias' => 'SELECT BIZ_ORDER_ID,BIZ_ORDER_ID AS linked_id'
                    . ' FROM YOSHOP_USER_GIFT_LOG WHERE 1=0',
                'constant' => "SELECT CAST('18446744073709551615' AS DECIMAL(20,0))"
                    . ' AS BIZ_ORDER_ID FROM DUAL WHERE 1=0',
            ];
        $roleResult = ['metadata' => []];
        foreach ($queries as $name => $sql) {
            try {
                $roleResult['metadata'][$name] = metadata($pdo, $sql);
            } catch (Throwable $error) {
                $roleResult['metadata'][$name] = [
                    'error_class' => get_class($error), 'error_code' => $error->getCode(),
                ];
            }
        }
        $roleResult['statement_class'] = statementClass($pdo);
        $result['roles'][$role] = $roleResult;
    } catch (Throwable $error) {
        // Never log exception messages because they can include connection data.
        $result['roles'][$role] = [
            'connection_error_class' => get_class($error),
            'connection_error_code' => $error->getCode(),
        ];
    }
}
echo json_encode($result, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . PHP_EOL;
