use strict;
use warnings;
use Test::More;

# Live PostgreSQL diagnostics: exercises DBIx::Fast::Profile::Pg against a
# real server. Gated on:
#   DBIX_FAST_TEST_PG='postgres://postgres:test123@127.0.0.1:5433/dbix_test'

my $dsn = $ENV{DBIX_FAST_TEST_PG}
  or plan skip_all => 'DBIX_FAST_TEST_PG env var not set '
                    . '(e.g. postgres://postgres:pass@127.0.0.1:5433/dbix_test)';

eval { require DBD::Pg; 1 }    or plan skip_all => 'DBD::Pg required';
eval { require DBIx::Fast; 1 } or plan skip_all => 'DBIx::Fast required';
DBIx::Fast->import;

my $db = eval { DBIx::Fast->new(dsn => $dsn, RaiseError => 1, PrintError => 0) };
plan skip_all => "Cannot connect to PostgreSQL ($dsn): $@" if $@ or !$db;

# Suppress NOTICE chatter (e.g. "table ... does not exist, skipping" from
# DROP TABLE IF EXISTS) so the test output stays clean.
$db->exec(q{SET client_min_messages TO WARNING});

$db->exec('DROP TABLE IF EXISTS profile_probe');
$db->exec(q{
    CREATE TABLE profile_probe (
        id     SERIAL PRIMARY KEY,
        name   TEXT NOT NULL,
        status SMALLINT DEFAULT 1
    )
});
$db->exec('CREATE INDEX idx_profile_probe_status ON profile_probe (status)');
$db->insert('profile_probe', { name => "probe $_", status => $_ % 2 }) for 1..5;

subtest 'profiler() resolves to the native Pg profile (no Base fallback)' => sub {
    my @warnings;
    local $SIG{__WARN__} = sub { push @warnings, @_ };
    my $p = $db->profiler;
    isa_ok($p, 'DBIx::Fast::Profile::Pg');
    is_deeply(\@warnings, [], 'no "using base profile" warning emitted');
};

subtest 'explain_query returns a plan and validates its input' => sub {
    my $p = $db->profiler;

    my $plan = $p->explain_query(
        'SELECT * FROM profile_probe WHERE status = ? AND id > ?', 1, 0);
    ok($plan && @$plan, 'got plan rows');
    like($plan->[0]{'QUERY PLAN'}, qr/\w/, 'plan text present');

    ok(!$p->explain_query('DROP TABLE profile_probe'),      'non-DML rejected');
    ok(!$p->explain_query('SELECT 1; DROP TABLE profile_probe'), 'stacked statement rejected');
    ok(!$p->explain_query('SELECT pg_read_file(\'/etc/passwd\') FROM profile_probe'),
       'pg_read_file rejected');
    ok(!$p->explain_query('SELECT pg_sleep(30)'),           'pg_sleep rejected');
    ok(!$p->explain_query('SELECT $tag$ hidden $tag$'),     'dollar-quoting rejected');
    ok( $p->explain_query(q{SELECT name FROM profile_probe WHERE name = 'pg_sleep x'}),
       'benign literal mentioning a blocked token is allowed');
};

subtest 'table and index stats' => sub {
    my $ts = $db->profiler->get_table_stats('profile_probe');
    is(scalar @$ts, 1, 'one row for the table');
    is($ts->[0]{table_name}, 'profile_probe', 'table_name matches');
    ok(defined $ts->[0]{data_length},  'data_length present');
    ok(defined $ts->[0]{index_length}, 'index_length present');

    my $is = $db->profiler->get_index_stats('profile_probe');
    my %by_index = map { $_->{index_name} => $_ } @$is;
    ok($by_index{idx_profile_probe_status}, 'secondary index listed');
    is($by_index{idx_profile_probe_status}{column_name}, 'status',
       'index column resolved');
    ok($by_index{profile_probe_pkey}, 'primary key index listed');
};

subtest 'server stats and connections' => sub {
    my $stats = $db->profiler->get_stats;
    ok(defined $stats->{global}{xact_commit}, 'global xact_commit present');
    ok(exists $stats->{session}, 'session block present');

    my $conns = $db->profiler->get_current_connections;
    ok($conns->{total_connections} >= 1, 'at least our own connection counted');

    my $procs = $db->profiler->get_processes;
    ok(@$procs >= 1, 'process list includes our backend');

    my $slow = $db->profiler->get_slow_queries;
    is(ref $slow, 'ARRAY',
       'get_slow_queries returns arrayref even without pg_stat_statements');
};

$db->exec('DROP TABLE IF EXISTS profile_probe');

done_testing;
