use strict;
use warnings;
use Test::More;
use File::Temp qw(tmpnam);
use Data::Pool::Shared;

# A creator killed between ftruncate() and header init leaves a full-size,
# all-zero (magic==0) file.  new() must recover it instead of bricking the
# path, but must never clobber a valid or foreign file.

my $path = tmpnam() . '.shm';
END { unlink $path if $path && -f $path }

my ($capacity, $elem_size) = (10, 32);

# Learn the on-disk size for this geometry.
{ my $p = Data::Pool::Shared->new($path, $capacity, $elem_size); }
my $total = -s $path;
unlink $path;

# 1. Recovery: an abandoned all-zero file of exactly $total bytes is re-initialized.
{
    open my $f, '>', $path or die $!; truncate $f, $total or die $!; close $f;
    is(-s $path, $total, "abandoned file is $total bytes (a killed creator's ftruncate)");
    my $p = eval { Data::Pool::Shared->new($path, $capacity, $elem_size) };
    ok($p, "new() recovers an abandoned mid-init file instead of bricking") or diag $@;
  SKIP: {
        skip "no handle", 4 unless $p;
        is $p->used, 0, "recovered pool starts empty (used==0)";
        is $p->available, $capacity, "recovered pool starts empty (available==capacity)";
        my $idx = $p->alloc;
        ok(defined $idx, "recovered pool is usable (alloc works)");
        $p->set($idx, "hello");
        is substr($p->get($idx), 0, 5), "hello", "recovered pool is usable (set/get works)";
    }
    undef $p; unlink $path;
}

# 2. No clobber: a file with nonzero (foreign) magic still errors.
{
    open my $f, '>', $path or die $!; print $f "XXXX"; truncate $f, $total or die $!; close $f;
    my $p = eval { Data::Pool::Shared->new($path, $capacity, $elem_size) };
    ok(!$p, "new() refuses a foreign nonzero-magic file (no clobber)");
    like($@, qr/invalid/i, "  ... reporting an invalid file");
    undef $p; unlink $path;
}

# 3. No recovery for the wrong size: magic==0 but size != total still errors.
{
    open my $f, '>', $path or die $!; truncate $f, $total + 8 or die $!; close $f;
    my $p = eval { Data::Pool::Shared->new($path, $capacity, $elem_size) };
    ok(!$p, "new() refuses an uninitialized file of the wrong size");
    undef $p; unlink $path;
}

# 4. A valid file is attached, never re-initialized (its data survives).
{
    my $a = Data::Pool::Shared->new($path, $capacity, $elem_size);
    my $idx = $a->alloc;
    $a->set($idx, "keep!");
    undef $a;

    my $r = Data::Pool::Shared->new($path, $capacity, $elem_size);
    is $r->used, 1, "reopening a valid file preserves used count";
    is substr($r->get($idx), 0, 5), "keep!", "reopening a valid file preserves its data";
    undef $r; unlink $path;
}
# 5. A magic==0 file of the right size but with NON-zero data (not a fresh
#    ftruncate) is NOT recovered -- recovery only re-inits a provably-empty file.
{
    open my $zfh, '>', $path or die $!; truncate $zfh, $total or die $!; close $zfh;
    open $zfh, '+<', $path or die $!; seek $zfh, $total - 1, 0; print $zfh "\x01"; close $zfh;
    my $p = eval { Data::Pool::Shared->new($path, $capacity, $elem_size) };
    ok(!$p, "new() refuses a magic==0 file that is not all-zero (no clobber of real data)");
    undef $p; unlink $path;
}

done_testing;
