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

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

use DBIx::Fast::Connector;

my $dsn   = 'dbi:SQLite:dbname=:memory:';
my $attrs = { RaiseError => 1, PrintError => 0, AutoCommit => 1 };

subtest 'basic constructor and dbh' => sub {
    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    ok($conn, 'Connector created');
    my $dbh = $conn->dbh;
    ok($dbh, 'Got dbh');
    isa_ok($dbh, 'DBI::db');
};

subtest 'dbh caching - same handle returned' => sub {
    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    my $dbh1 = $conn->dbh;
    my $dbh2 = $conn->dbh;
    is($dbh1, $dbh2, 'Same handle returned on second call');
};

subtest 'disconnect and reconnect' => sub {
    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    my $dbh1 = $conn->dbh;
    ok($dbh1, 'Got initial handle');

    $conn->disconnect;
    my $dbh2 = $conn->dbh;
    ok($dbh2, 'Got new handle after disconnect');
    isnt($dbh1, $dbh2, 'New handle is different');
};

subtest 'mode() compatibility method' => sub {
    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    lives_ok { $conn->mode() } 'mode() is callable';
    lives_ok { $conn->mode('fixup') } 'mode() accepts arguments';
    is($conn->mode(), undef, 'mode() returns undef');
};

subtest 'fork detection - child gets new handle' => sub {
    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    my $parent_dbh = $conn->dbh;
    ok($parent_dbh, 'Parent got handle');

    my $pid = fork();
    if (!defined $pid) {
        plan skip_all => "fork() not available";
        return;
    }

    if ($pid == 0) {
        # Child process
        my $child_dbh = $conn->dbh;
        # After fork, should get a NEW handle
        exit($child_dbh && $child_dbh ne $parent_dbh ? 0 : 1);
    }
    else {
        waitpid($pid, 0);
        is($? >> 8, 0, 'Child detected fork and reconnected');
    }
};

subtest 'stale handle detection' => sub {
    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    my $dbh1 = $conn->dbh;

    # Simulate stale handle by disconnecting the underlying DBI handle
    $dbh1->disconnect;

    # The connector should detect the stale handle and reconnect
    my $dbh2 = $conn->dbh;
    ok($dbh2, 'Got handle after stale detection');
    ok($dbh2->FETCH('Active'), 'New handle is active');
};

subtest 'ping reconnects when server-side connection is dead' => sub {
    # Force ping on every dbh() call so the test is deterministic.
    local $ENV{DBIX_FAST_PING_INTERVAL} = 0;

    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    my $dbh1 = $conn->dbh;
    ok($dbh1->FETCH('Active'), 'Initial handle is active');

    # Monkey-patch ping on this handle to simulate a dead server-side
    # connection that DBI still thinks is Active (wait_timeout, server
    # restart, etc.). This is the exact scenario that segfaults
    # DBD::MariaDB 1.24 when a query is executed on the handle.
    no warnings 'redefine';
    local *DBI::db::ping = sub { 0 };

    my $dbh2 = $conn->dbh;
    ok($dbh2, 'Got a handle after failed ping');
    isnt($dbh1, $dbh2, 'Connector replaced the stale handle');
    ok($dbh2->FETCH('Active'), 'Replacement handle is active');
};

subtest 'SQLite skips ping by default' => sub {
    # No env var set: SQLite DSN should auto-skip ping entirely.
    local %ENV = %ENV;
    delete $ENV{DBIX_FAST_PING_INTERVAL};

    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    $conn->dbh;  # establish connection

    my $ping_calls = 0;
    no warnings 'redefine';
    local *DBI::db::ping = sub { $ping_calls++; 1 };

    $conn->dbh for 1..5;
    is($ping_calls, 0, 'ping never called for SQLite without env override');
};

subtest 'ping throttling - no reconnect within interval' => sub {
    # 60s interval ensures ping is NOT called on immediate re-fetch
    local $ENV{DBIX_FAST_PING_INTERVAL} = 60;

    my $conn = DBIx::Fast::Connector->new(dsn => $dsn, attrs => $attrs);
    my $dbh1 = $conn->dbh;

    my $ping_calls = 0;
    no warnings 'redefine';
    local *DBI::db::ping = sub { $ping_calls++; 1 };

    $conn->dbh for 1..5;
    is($ping_calls, 0, 'ping throttled within interval window');
};

done_testing;
