<?php
namespace Nibiru\Module\Ai\Plugins\Tools;

use Nibiru\Module\Ai\Plugins\Tool;
use Nibiru\Pdo;

/**
 * Read-only SQL query tool. Lets an agent ask the database questions like
 * "how many active users?" without giving it write access.
 *
 *   $agent->withTools([new \Nibiru\Module\Ai\Plugins\Tools\PdoQuery()])
 *         ->run('How many users registered last week?');
 *
 * Safety: rejects anything that looks like INSERT/UPDATE/DELETE/DROP/TRUNCATE/ALTER.
 * If you need write access, write a more privileged subclass with an audit trail.
 */
class PdoQuery extends Tool
{
    public function name(): string { return 'pdo_query'; }

    public function description(): string
    {
        return 'Run a single read-only SQL SELECT against the application database. '
             . 'Use for counts, aggregates, lookups. Returns rows as JSON.';
    }

    public function schema(): array
    {
        return [
            'sql' => [
                'type' => 'string',
                'description' => 'A single SELECT statement. Use placeholders (:name) for dynamic values.',
                'required' => true,
            ],
            'params' => [
                'type' => 'object',
                'description' => 'Optional parameter bindings, e.g. {":id": 42}.',
                'required' => false,
            ],
        ];
    }

    public function execute(array $args): mixed
    {
        $sql = trim((string) ($args['sql'] ?? ''));
        if ($sql === '') return 'ERROR: empty SQL';
        if (!preg_match('/^\s*SELECT\s/i', $sql)) {
            return 'ERROR: only SELECT is permitted by pdo_query';
        }
        if (preg_match('/;\s*\S/', $sql)) {
            return 'ERROR: only a single statement is permitted';
        }
        if (preg_match('/\b(INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|GRANT|REVOKE)\b/i', $sql)) {
            return 'ERROR: write/DDL operations are blocked';
        }

        try {
            $params = is_array($args['params'] ?? null) ? $args['params'] : [];
            $rows = Pdo::fetchAll($sql, $params);
            // Cap the response so the agent doesn't choke on huge results.
            $rows = array_slice($rows, 0, 50);
            return json_encode($rows, JSON_UNESCAPED_UNICODE);
        } catch (\Throwable $e) {
            return 'ERROR: ' . $e->getMessage();
        }
    }
}
