use strict;
use warnings;
use Test::More 0.96;
use Test::MockObject 1.09 ();
use FindBin qw($Bin); # core
use Symbol; # core

my $mod = 'DBIx::TableLoader::CSV';
eval "require $mod" or die $@;

our $csv_class;
our %csv_classes = (map { my $e = eval "require $_"; $_ => $e } qw(Text::CSV Text::CSV_XS Text::CSV_PP));

sub test_with_all_classes ($$);

# get_raw_row()
test_with_all_classes get_raw_row => sub
{
	my $loader = new_ok($mod, [io => new_io(), default_column_type => 'foo', csv_class => $csv_class]);
	# columns determined from first row
	is_deeply($loader->columns, [[qw(fld1 foo)], [qw(fld2 foo)]], 'columns');
	is_deeply($loader->column_names, [qw(fld1 fld2)], 'column names');
	# get_row calls get_raw_row
	is_deeply($loader->get_raw_row, [qw(row1 col2)],     'raw row 1');
	is_deeply($loader->get_row,     [qw(row2 col2)],     'row 2');
	is_deeply($loader->get_raw_row, [qw(row3), "col 2"], 'raw row 3');
};

# default_name()
test_with_all_classes default_name => sub
{
	# we're basically testing File::Basename isn't necessary
	foreach my $test (
		['/tmp/goober.csv' => 'goober'],
		['har de har har.fudge.csv' => 'har de har har.fudge'],
		['ahoy.tab' => 'ahoy'],
		['' => 'csv'],
	){
		my ($file, $exp) = @$test;
		is(new_ok($mod, [io => new_io(), file => $file, csv_class => $csv_class])->default_name, $exp, 'default_name');
	}
};

# prepare_data() options
test_with_all_classes 'prepare_data() options' => sub
{
	my $loader;
	my $mock = Test::MockObject->new();
	$mock->fake_module('Fake_CSV',
		new => sub { bless( ($_[1]||{}), $_[0] ) },
		getline => sub { [1] }
	);

	# csv
	$loader = new_ok($mod, [io => new_io(), csv_class => $csv_class]);
	isa_ok($loader->{csv}, $csv_class);
	my $csv = Fake_CSV->new({goo => 'ber'});
	$loader = new_ok($mod, [io => new_io(), csv => $csv]);
	is($loader->{csv}, $csv, 'csv option');
	is($loader->{csv}->{goo}, 'ber', 'csv option');

	# csv_class
	$loader = new_ok($mod, [io => new_io(), csv_class => $csv_class]);
	isa_ok($loader->{csv}, $csv_class);
	$loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV']);
	isa_ok($loader->{csv}, 'Fake_CSV');

	# csv_opts, csv_defaults
	$loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV']);
	is($loader->{csv}->{binary}, 1, 'csv_defaults');
	$loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV', csv_opts => {binary => 12}]);
	is($loader->{csv}->{binary}, 12, 'csv_defaults overridden with csv_opts');
	$loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV', csv_defaults => {}]);
	ok(!exists($loader->{csv}->{binary}), 'csv_defaults emptied');

	# file (no io)
	is(eval { $mod->new(file => '') }, undef, 'die w/o file');
	like($@, qr/Cannot proceed without/, 'no file error');
	is(eval { $mod->new(file => "$Bin/t/file that does/not.exist") }, undef, 'die w/o file');
	like($@, qr/Failed to open/, 'cannot find file');

	$loader = eval { $mod->new(file => "$Bin/example.csv") };
	is($@, '', 'no error');
	isa_ok($loader, $mod, 'file exists');

	# no_header
	$loader = new_ok($mod, [io => new_io()]);
	is_deeply($loader->column_names, [qw(fld1 fld2)], 'csv 1st row column names');
	is_deeply($loader->get_raw_row, [qw(row1 col2)],  'csv 2nd row is 1st row of data');

	$loader = new_ok($mod, [io => new_io(), columns => [qw(goo ber)]]);
	is_deeply($loader->column_names, [qw(goo ber)],   'provided column names');
	is_deeply($loader->get_raw_row, [qw(row1 col2)],  'csv 2nd row is 1st row of data');

	$loader = new_ok($mod, [io => new_io(), columns => [qw(goo ber)], no_header => 1]);
	is_deeply($loader->column_names, [qw(goo ber)],   'provided column names');
	is_deeply($loader->get_raw_row, [qw(fld1 fld2)],  'no_header option returns 1st row of csv in 2st row of data');
};

done_testing;

sub new_io {
	# use fake io to avoid opening files
	my $data = ["fld1,fld2\n", "row1,col2\n", "row2,col2\n", qq[row3,"col 2"\n]];
	return Test::MockObject->new(
		# Text::CSV_PP calls eof() which requires a Glob reference
		Symbol::gensym()
	)->mock(getline => sub { shift @$data });
}

sub test_with_all_classes ($$) {
	my ($name, $subtest) = @_;
	foreach my $class ( keys %csv_classes ){
		local $csv_class = $class;
		subtest "$name with $csv_class" => sub {
			plan skip_all => "$csv_class required for testing with it"
				if ! $csv_classes{$csv_class};

			$subtest->();
		};
	}
}
