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

my $dsn = $ENV{DBIX_FAST_TEST_MARIADB}
  or plan skip_all => 'DBIX_FAST_TEST_MARIADB env var not set '
                    . '(e.g. mariadb://root:test123@127.0.0.1:3307/audit)';

eval "use DBD::MariaDB 1.23";
plan skip_all => 'DBD::MariaDB 1.23 required' if $@;
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 MariaDB ($dsn): $@" if $@ or !$db;

$db->exec(q{DROP TABLE IF EXISTS products});
$db->exec(q{
    CREATE TABLE products (
        sku   VARCHAR(32) PRIMARY KEY,
        name  VARCHAR(64),
        stock INT,
        price DECIMAL(10,2)
    )
});

# --- upsert: insert + update path -------------------------------------------

subtest 'MariaDB: upsert inserts on no conflict' => sub {
    $db->upsert('products',
        { sku => 'A', name => 'Apple', stock => 10, price => 1.50 },
        ['sku'],
    );

    like($db->last_sql, qr/ON DUPLICATE KEY UPDATE/,
         'SQL uses ON DUPLICATE KEY UPDATE (not ON CONFLICT)');
    like($db->last_sql, qr/`stock` = VALUES\(`stock`\)/,
         'uses VALUES() reference, backtick-quoted');
    unlike($db->last_sql, qr/ON CONFLICT/,
           'does NOT use ON CONFLICT syntax');

    my $row = $db->hash('SELECT * FROM products WHERE sku = ?', 'A');
    is($row->{name},  'Apple', 'name inserted');
    is($row->{stock}, 10,      'stock inserted');
};

subtest 'MariaDB: upsert updates on PK conflict' => sub {
    $db->upsert('products',
        { sku => 'A', name => 'Apple v2', stock => 20, price => 2.00 },
        ['sku'],
    );

    my $row = $db->hash('SELECT * FROM products WHERE sku = ?', 'A');
    is($row->{name},  'Apple v2', 'name updated');
    is($row->{stock}, 20,         'stock updated');
    is($db->val('SELECT COUNT(*) FROM products'), 1,
       'still one row (was UPDATE, not duplicate INSERT)');
};

subtest 'MariaDB: upsert with explicit update_cols' => sub {
    $db->upsert('products',
        { sku => 'A', name => 'IGNORED', stock => 99, price => 9.99 },
        ['sku'],
        ['stock'],
    );

    like($db->last_sql, qr/`stock` = VALUES\(`stock`\)/,
         'stock present in update set');
    unlike($db->last_sql, qr/`name` = VALUES/,
           'name NOT in update set (excluded from update_cols)');

    my $row = $db->hash('SELECT * FROM products WHERE sku = ?', 'A');
    is($row->{name},  'Apple v2', 'name preserved');
    is($row->{stock}, 99,         'stock updated');
};

# --- bulk -------------------------------------------------------------------

subtest 'MariaDB: insert_many bulk inserts in one statement' => sub {
    $db->exec('DELETE FROM products');
    my $n = $db->insert_many('products', [
        { sku => 'B', name => 'Banana', stock => 5, price => 0.50 },
        { sku => 'C', name => 'Cherry', stock => 8, price => 1.20 },
        { sku => 'D', name => 'Date',   stock => 3, price => 2.00 },
    ]);

    my @tuples = $db->last_sql =~ /\(\?, \?, \?, \?\)/g;
    is(scalar @tuples, 3, 'single INSERT with 3 row-tuples');
    like($db->last_sql, qr/`products`/, 'backtick-quoted table');

    is($n, 3, 'returns row count');
    is($db->val('SELECT COUNT(*) FROM products'), 3, 'all rows persisted');
};

subtest 'MariaDB: upsert_many mixes inserts and updates' => sub {
    my $n = $db->upsert_many('products',
        [
            { sku => 'B', name => 'Banana v2', stock => 50, price => 0.55 },
            { sku => 'E', name => 'Elderberry', stock => 7,  price => 3.00 },
        ],
        ['sku'],
    );
    is($n, 2, 'returns batch size');

    like($db->last_sql, qr/ON DUPLICATE KEY UPDATE/,
         'uses MariaDB syntax');

    is($db->val('SELECT stock FROM products WHERE sku=?', 'B'), 50,
       'B updated in place');
    is($db->val('SELECT name FROM products WHERE sku=?', 'E'),
       'Elderberry', 'E inserted');
};

# --- Security regression ----------------------------------------------------

subtest 'MariaDB: upsert rejects identifier injection' => sub {
    throws_ok {
        $db->upsert('products',
            { sku => 'Z', q{name = (SELECT password FROM users), stock} => 1 },
            ['sku'],
        );
    } qr/Invalid identifier/,
        'malicious row key rejected';

    throws_ok {
        $db->upsert_many('products',
            [ { sku => 'Z', name => 'Z', stock => 1, price => 1 } ],
            [ q{sku, name = (SELECT secret FROM x)} ],
        );
    } qr/Invalid identifier/,
        'malicious conflict_keys rejected';
};

# Cleanup
$db->exec('DROP TABLE IF EXISTS products');

done_testing;
