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

# Regression test for encapsulation (ranks 17/27): the output/config fields
# Q / results / last_id / last_sql must be read-only so callers cannot clobber
# query outputs or replace the driver-aware SQL::Abstract instance. sql and p
# stay writable for the documented set-then-execute workflow.

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('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)');
$db->insert('t', { id => 1, name => 'a' });
$db->all('SELECT * FROM t ORDER BY id');

subtest 'Q/results/last_id/last_sql are read-only' => sub {
    can_ok($db, qw(Q results last_id last_sql));
    isa_ok($db->Q, 'SQL::Abstract', 'Q reader returns the SQL::Abstract instance');

    # A writer-style call must NOT mutate the field (no mutator generated). This
    # holds whether the reader ignores or rejects the extra argument.
    for my $f (qw(results last_id last_sql)) {
        my $before = $db->$f;
        eval { $db->$f('CLOBBERED') };
        is($db->$f, $before, "$f is read-only (writer-style call did not mutate)");
    }

    my $q = $db->Q;
    eval { $db->Q('CLOBBERED') };
    is($db->Q, $q, 'Q is read-only (driver-aware SQL::Abstract cannot be replaced)');
};

subtest 'sql and p remain writable for set-then-execute' => sub {
    $db->q('SELECT * FROM t WHERE id = ?', 1);
    is($db->sql, 'SELECT * FROM t WHERE id = ?', 'sql settable via q()');
    is_deeply($db->p, [1], 'p settable via q()');
    lives_ok { $db->sql('SELECT 1'); $db->p([]); } 'sql/p mutators still present';
};

done_testing;
