use Test::More;
use strict;
use Data::FormValidator;
use DateTime;
plan(tests => 85);

# 1
use_ok('Data::FormValidator::Constraints::DateTime');
Data::FormValidator::Constraints::DateTime->import();
my $format          = '%m-%d-%Y';
my $good_date       = '02-17-2005';
my $unreal_date     = '02-31-2005';
my $bad_date        = '0-312-005';
my $real_bad_date   = '2';
# these are relatvie to the above $good_date;
my $distant_future_date = '03-03-2110'; 
my $future_date         = '03-03-2010'; 
my $past_date           = '03-03-1979';
my $distant_past_date   = '03-03-1879';

# 2..13
# to_datetime
{
    my %profile = (
        validator_packages      => ['Data::FormValidator::Constraints::DateTime'], 
        required                => [qw(good bad realbad unreal)],
        untaint_all_constraints => 1,
    );
    my %data = (
        good    => $good_date,
        unreal  => $unreal_date,
        bad     => $bad_date,
        realbad => $real_bad_date,
    );
    foreach my $as_method (0..1) {
        $profile{constraints} = _make_constraints(
            routine     => 'to_datetime', 
            as_method   => $as_method, 
            params      => [\$format],
            inputs      => [keys %data ],
        );
        my $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->valid('good'), 'datetime expected valid');
        ok( $results->invalid('bad'), 'datetime expected invalid');
        ok( $results->invalid('realbad'), 'datetime expected invalid');
        ok( $results->invalid('unreal'), 'datetime expected invalid');
        my $date = $results->valid('good');
        isa_ok( $date, 'DateTime');
        is( "$date", $good_date, 'DateTime stringifies correctly');
    }
};


# 14..33
# ymd_to_datetime
{
    foreach my $as_method (0..1) {
        my %profile = (
            validator_packages      => [qw(Data::FormValidator::Constraints::DateTime)],
            required                => [qw(my_year)],
            untaint_all_constraints => 1,
        );
        my %data = (
            my_year     => 2005,
            my_month    => 2,
            my_day      => 17,
        );
        $profile{constraints} = _make_constraints(
            routine     => 'ymd_to_datetime',
            as_method   => $as_method,
            params      => ($as_method ? [qw(my_year my_month my_day)] : [qw(my_month my_day)]),
            inputs      => ['my_year'],
        );
        my $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->valid('my_year'), 'ymd_to_datetime: correct');
        isa_ok( $results->valid('my_year'), 'DateTime');
    
        # now with hms
        $profile{constraints} = _make_constraints(
            routine     => 'ymd_to_datetime',
            as_method   => $as_method,
            params      => ($as_method ? 
                            [qw(my_year my_month my_day my_hour my_min my_sec)] 
                            : [qw(my_month my_day my_hour my_min my_sec)]),
            inputs      => ['my_year'],
        );
        $data{my_hour} = 14;
        $data{my_min}  = 6;
        $data{my_sec}  = 14;
    
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->valid('my_year'), 'ymd_to_datetime: correct');
        isa_ok( $results->valid('my_year'), 'DateTime');
    
        # make sure it fails if the month is not a number
        $data{my_month} = "";
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->invalid('my_year'), 'ymd_to_datetime: invalid date');
        $data{my_month} = undef;
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->invalid('my_year'), 'ymd_to_datetime: invalid date');
        $data{my_month} = 2;    # reset the month

        # make sure it fails if the day is not a number
        $data{my_day} = "";
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->invalid('my_year'), 'ymd_to_datetime: invalid date');
        $data{my_day} = undef;
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->invalid('my_year'), 'ymd_to_datetime: invalid date');
        $data{my_day} = "17";   # reset the day

        # make sure it fails the year is not a number
        $profile{constraints} = {
            'my_month'  => {
                ($as_method ? 'constraint_method' : 'constraint') => 'ymd_to_datetime',
                params      => [qw(my_year my_month my_day my_hour my_min my_sec)],
            },
        };
        $profile{required} = ['my_month'];
        $data{my_year} = "";
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->invalid('my_month'), 'ymd_to_datetime: invalid date');
        $data{my_year} = undef;
        $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->invalid('my_month'), 'ymd_to_datetime: invalid date');
    }
}

# 34..49
# before_datetime
{
    my %data = (
        good    => $good_date,
        unreal  => $unreal_date,
        bad     => $bad_date,
        realbad => $real_bad_date,
        future  => $distant_future_date,
    );
    my %profile = (
        validator_packages      => ['Data::FormValidator::Constraints::DateTime'],
        required                => [keys %data],
        untaint_all_constraints => 1,
    );
    foreach my $as_method (0..1) {
        $profile{constraints} = _make_constraints(
            routine     => 'before_datetime',
            as_method   => $as_method,
            params      => [\$format, \$future_date],
            inputs      => [keys %data],
        );
        my $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->valid('good'), 'datetime expected valid');
        ok( $results->invalid('bad'), 'datetime expected invalid');
        ok( $results->invalid('realbad'), 'datetime expected invalid');
        ok( $results->invalid('unreal'), 'datetime expected invalid');
        ok( $results->invalid('future'), 'datetime expected invalid');
        my $date = $results->valid('good');
        isa_ok( $date, 'DateTime');
        is( "$date", $good_date, 'DateTime stringifies correctly');

        # test an invalid future_date
        $profile{constraints} = _make_constraints(
            routine     => 'before_datetime',
            as_method   => $as_method,
            params      => [\$format, \$bad_date],
            inputs      => [keys %data],
        );
        $results = Data::FormValidator->check(\%data, \%profile);
        my @any_valid = $results->valid();
        is(scalar @any_valid, 0, 'no valid data');
    }
}

# 50..65
# after_datetime
{
    my %data = (
        good    => $good_date,
        unreal  => $unreal_date,
        bad     => $bad_date,
        realbad => $real_bad_date,
        past    => $distant_past_date,
    );
    my %profile = (
        validator_packages      => ['Data::FormValidator::Constraints::DateTime'],
        required                => [keys %data],
        untaint_all_constraints => 1,
    );
    foreach my $as_method (0..1) {
        $profile{constraints} = _make_constraints(
            routine     => 'after_datetime',
            as_method   => $as_method,
            params      => [\$format, \$past_date],
            inputs      => [keys %data],
        );
        my $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->valid('good'), 'datetime expected valid');
        ok( $results->invalid('bad'), 'datetime expected invalid');
        ok( $results->invalid('realbad'), 'datetime expected invalid');
        ok( $results->invalid('unreal'), 'datetime expected invalid');
        ok( $results->invalid('past'), 'datetime expected invalid');
        my $date = $results->valid('good');
        isa_ok( $date, 'DateTime');
        is( "$date", $good_date, 'DateTime stringifies correctly');

        # test an invalid past_date
        $profile{constraints} = _make_constraints(
            routine     => 'after_datetime',
            as_method   => $as_method,
            params      => [\$format, \$bad_date],
            inputs      => [keys %data],
        );
        $results = Data::FormValidator->check(\%data, \%profile);
        my @any_valid = $results->valid();
        is(scalar @any_valid, 0, 'no valid data');
    }
}


# 66..85
# between_datetimes
{
    my %data = (
        good            => $good_date,
        unreal          => $unreal_date,
        bad             => $bad_date,
        realbad         => $real_bad_date,
        outside_past    => $distant_past_date,
        outside_future  => $distant_future_date,
    );
    my %profile = (
        validator_packages      => ['Data::FormValidator::Constraints::DateTime'],
        required                => [keys %data],
        untaint_all_constraints => 1,
    );
    foreach my $as_method (0..1) {
        $profile{constraints} = _make_constraints(
            routine     => 'between_datetimes',
            as_method   => $as_method,
            params      => [\$format, \$past_date, \$future_date],
            inputs      => [keys %data],
        );
        my $results = Data::FormValidator->check(\%data, \%profile);
        ok( $results->valid('good'), 'datetime expected valid');
        ok( $results->invalid('bad'), 'datetime expected invalid');
        ok( $results->invalid('realbad'), 'datetime expected invalid');
        ok( $results->invalid('unreal'), 'datetime expected invalid');
        ok( $results->invalid('outside_past'), 'datetime expected invalid');
        ok( $results->invalid('outside_future'), 'datetime expected invalid');
        my $date = $results->valid('good');
        isa_ok( $date, 'DateTime');
        is( "$date", $good_date, 'DateTime stringifies correctly');

        # test an invalid past_date
        $profile{constraints} = _make_constraints(
            routine     => 'between_datetimes',
            as_method   => $as_method,
            params      => [\$format, \$bad_date, \$future_date],
            inputs      => [keys %data],
        );
        $results = Data::FormValidator->check(\%data, \%profile);
        my @any_valid = $results->valid();
        is(scalar @any_valid, 0, 'no valid data');

        # test an invalid future_date
        $profile{constraints} = _make_constraints(
            routine     => 'between_datetimes',
            as_method   => $as_method,
            params      => [\$format, \$past_date, \$bad_date],
            inputs      => [keys %data],
        );
        $results = Data::FormValidator->check(\%data, \%profile);
        @any_valid = $results->valid();
        is(scalar @any_valid, 0, 'no valid data');
    }
}


sub _make_constraints {
    my %args = @_;
    my ($method, $as_method, @params) = @_;
    my %constraints = ();

    foreach my $input (@{$args{inputs}}) {
        if( $args{as_method} ) {
            $constraints{$input} = {
                constraint_method   => $args{routine},
                params              => $args{params},
            };
        } else {
            my @tmp_params = @{$args{params}};
            $constraints{$input} = {
                constraint  => $args{routine},
                params      => [$input, @tmp_params],
            };
        }
    }
    return \%constraints;
};
