#!/usr/bin/perl -w

use strict;
use warnings;

use Test2::Bundle::Extended;
use Test2::Tools::Explain;
use Test2::Plugin::NoWarnings;

use File::Temp qw/tempfile tempdir/;
use File::Basename;

use Errno qw/ENOENT EBADF ENOTDIR/;

use Test::MockFile qw< nostrict >;    # Everything below this can have its open overridden.

my $temp_dir = tempdir( CLEANUP => 1 );
my ( undef, $filename )    = tempfile( DIR => $temp_dir );
my ( undef, $temp_notdir ) = tempfile();

note "-------------- REAL MODE --------------";
is( -d $temp_dir,                     1, "Temp is created on disk." );
is( opendir( my $dir_fh, $temp_dir ), 1, "$temp_dir can be read" );
my @dir_files;
push @dir_files, scalar readdir($dir_fh);
push @dir_files, scalar readdir($dir_fh);
push @dir_files, scalar readdir($dir_fh);
my $base = basename $filename;
is( [ sort @dir_files ],     [ sort( qw/. .. /, $base ) ], "We read 3 entries in some order. Not predictable, but sort fixes that!" );
is( scalar readdir($dir_fh), undef,                        "undef when nothing left from readdir." );
is( closedir($dir_fh),       1,                            "close the fake dir handle" );

like( warning { readdir($dir_fh) }, qr/^readdir\(\) attempted on invalid dirhandle \S+ /, "warn on readdir when file handle is closed." );

is( opendir( my $bad_fh, "/not/a/valid/path/kdshjfkjd" ), undef,  "opendir on a bad path returns false" );
is( $! + 0,                                               ENOENT, '$! numeric is right.' );

is( opendir( my $notdir_fh, $temp_notdir ), undef,   "opendir on a file returns false" );
is( $! + 0,                                 ENOTDIR, '$! numeric is right.' );

my ( $real_fh, $f3 ) = tempfile( DIR => $temp_dir );
like( warning { readdir($real_fh) }, qr/^readdir\(\) attempted on invalid dirhandle \$fh/, "We only warn if the file handle or glob is invalid." );

note "-------------- MOCK MODE --------------";
my $abc = Test::MockFile->file( "$temp_dir/abc", 'hello' );
my $def = Test::MockFile->file( "$temp_dir/def", 'hello' );
my $bar = Test::MockFile->dir($temp_dir);
my $baz = Test::MockFile->file( $temp_notdir, '' );

is( opendir( $dir_fh, $temp_dir ), 1,     "Mocked temp dir opens and returns true" );
is( scalar readdir($dir_fh),       ".",   "Read .  from fake readdir" );
is( scalar readdir($dir_fh),       "..",  "Read .. from fake readdir" );
is( telldir($dir_fh),              2,     "tell dir in the middle of fake readdir is right." );
is( scalar readdir($dir_fh),       "abc", "Read abc from fake readdir" );
is( scalar readdir($dir_fh),       "def", "Read def from fake readdir" );
is( telldir($dir_fh),              4,     "tell dir at the end of fake readdir is right." );
is( scalar readdir($dir_fh),       undef, "Read from fake readdir but no more in the list." );
is( scalar readdir($dir_fh),       undef, "Read from fake readdir but no more in the list." );
is( scalar readdir($dir_fh),       undef, "Read from fake readdir but no more in the list." );
is( scalar readdir($dir_fh),       undef, "Read from fake readdir but no more in the list." );

is( rewinddir($dir_fh),    1,                  "rewinddir returns true." );
is( telldir($dir_fh),      0,                  "telldir afer rewinddir is right." );
is( [ readdir($dir_fh) ],  [qw/. .. abc def/], "Read the whole dir from fake readdir after rewinddir" );
is( telldir($dir_fh),      4,                  "tell dir at the end of fake readdir is right." );
is( seekdir( $dir_fh, 1 ), 1,                  "seekdir returns where it sought." );
is( [ readdir($dir_fh) ],  [qw/.. abc def/],   "Read the whole dir from fake readdir after seekdir" );
closedir($dir_fh);

is( opendir( my $still_notdir_fh, $temp_notdir ), undef,   "opendir on a mocked file returns false" );
is( $! + 0,                                       ENOTDIR, '$! numeric is right.' );

# Check symlinks appear in readdir
my $dir_for_symlink = Test::MockFile->dir('/foo');
my $dir_in_dir      = Test::MockFile->dir('/foo/infoo');
my $symlink_dest    = Test::MockFile->file( '/foo/dest', '' );
my $symlink         = Test::MockFile->symlink( '/foo/dest', '/foo/source' );

opendir my $sdh, '/foo' or die $!;
my @contents = readdir $sdh;
closedir $sdh or die $!;
is(
    [ sort @contents ],
    [qw< . .. dest infoo source >],
    'Symlink and directories appears in directory content'
);

{
    my $d1 = Test::MockFile->dir('/foo2/bar');
    my $d2 = Test::MockFile->dir('/foo2');
    mkdir $d1->path();
    mkdir $d2->path();

    my $f = Test::MockFile->file( '/foo2/bar/baz', '' );

    opendir my $dh, '/foo2' or die $!;
    my @content = readdir $dh;
    closedir $dh or die $!;
    is( \@content, [qw< . .. bar >], 'Did not get confused by internal files' );
}

done_testing();
exit;
