use strict;
use warnings;
use Test::More;
use Test::Exception;
use DBIx::Fast;

# DBIx::Fast::Result iterator API (query()) and the prepare_cached contract
# of the read methods. Extracted from t/security.t, where it was hiding.

eval "use DBD::SQLite 1.74";
plan skip_all => "DBD::SQLite 1.74 required" if $@;

my $db = DBIx::Fast->new(
    dsn        => 'dbi:SQLite:dbname=:memory:',
    driver     => 'SQLite',
    RaiseError => 1,
    PrintError => 0,
);

$db->exec(q{
    CREATE TABLE users (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        email TEXT,
        status INTEGER DEFAULT 1
    )
});
$db->exec(q{ INSERT INTO users (name, email) VALUES ('Alice', 'alice@test.com') });
$db->exec(q{ INSERT INTO users (name, email) VALUES ('Bob', 'bob@test.com') });
$db->exec(q{ INSERT INTO users (name, email) VALUES ('Charlie', 'charlie@test.com') });

# --- query() iterator ---

subtest 'query() returns Result iterator' => sub {
    require DBIx::Fast::Result;

    my $rs = $db->query('SELECT id, name FROM users ORDER BY id');
    isa_ok($rs, 'DBIx::Fast::Result', 'query() returns Result object');

    my $row1 = $rs->hash;
    is($row1->{name}, 'Alice', 'First row is Alice');
    is($rs->count, 1, 'Count is 1 after first fetch');

    my $row2 = $rs->hash;
    is($row2->{name}, 'Bob', 'Second row is Bob');
    is($rs->count, 2, 'Count is 2 after second fetch');

    my $row3 = $rs->hash;
    is($row3->{name}, 'Charlie', 'Third row is Charlie');

    my $row4 = $rs->hash;
    is($row4, undef, 'No more rows returns undef');
    is($rs->count, 3, 'Final count is 3');
};

subtest 'query() with bind params' => sub {
    my $rs = $db->query('SELECT name FROM users WHERE id = ?', 2);
    my $row = $rs->hash;
    is($row->{name}, 'Bob', 'query() with bind param works');
};

subtest 'query() array method' => sub {
    my $rs = $db->query('SELECT id, name FROM users WHERE id = ?', 1);
    my $row = $rs->array;
    is(ref $row, 'ARRAY', 'array() returns arrayref');
    is($row->[0], 1, 'First element is id');
    is($row->[1], 'Alice', 'Second element is name');

    my $empty = $rs->array;
    is($empty, undef, 'array() returns undef when exhausted');
};

subtest 'query() all method' => sub {
    my $rs = $db->query('SELECT name FROM users ORDER BY id');
    my $all = $rs->all;
    is(ref $all, 'ARRAY', 'all() returns arrayref');
    is(scalar @$all, 3, 'all() returns all rows');
    is($all->[0]{name}, 'Alice', 'First row correct');
    is($all->[2]{name}, 'Charlie', 'Last row correct');
    is($rs->count, 3, 'count tracks all() rows');
};

subtest 'query() flat method' => sub {
    my $rs = $db->query('SELECT name FROM users ORDER BY id');
    my $names = $rs->flat;
    is(ref $names, 'ARRAY', 'flat() returns arrayref');
    is_deeply($names, ['Alice', 'Bob', 'Charlie'], 'flat() returns first column values');
};

subtest 'query() finish method' => sub {
    my $rs = $db->query('SELECT * FROM users');
    $rs->hash;  # fetch one
    lives_ok { $rs->finish } 'finish() works mid-iteration';
};



# Read methods should use prepare_cached so repeated queries skip re-parsing
subtest 'read methods use prepare_cached' => sub {
    my $dbh = $db->db->dbh;
    my $cache = $dbh->{CachedKids} // {};
    my $initial_keys = scalar keys %$cache;

    # Run a variety of read methods with identical SQL twice
    my $q = 'SELECT id, name FROM users WHERE id = ?';
    $db->all($q, 1);  $db->all($q, 2);
    $db->hash($q, 1); $db->hash($q, 2);
    $db->val('SELECT name FROM users WHERE id = ?', 1);
    $db->val('SELECT name FROM users WHERE id = ?', 2);
    $db->array('SELECT id FROM users WHERE id < ?', 99);
    $db->array('SELECT id FROM users WHERE id < ?', 100);
    $db->flat('SELECT name FROM users WHERE id < ?', 99);
    $db->flat('SELECT name FROM users WHERE id < ?', 100);

    my $after = $dbh->{CachedKids} // {};
    cmp_ok(scalar keys %$after, '>', $initial_keys,
           'CachedKids grows after read methods (prepare_cached is in use)');

    # Each distinct SQL should appear exactly once in the cache
    my @hits = grep { /SELECT id, name FROM users WHERE id = \?/ } keys %$after;
    is(scalar @hits, 1,
       'identical all()/hash() SQL is cached once, not duplicated per call');
};

done_testing;
