use strict;
use warnings;

use Test::More;
use Test::Differences;
use Test::Exception;

use autobox::Core;

use lib "lib";
use autobox::Transform;

use lib "t/lib";
use Literature;

my $literature = Literature::literature();
my $authors    = $literature->{authors};
my $books      = $literature->{books};

my $expected_titles_str = [
    "Caliban's War",
    "Leviathan Wakes",
    "The Name of the Wind",
    "The Tree-Body Problem",
];

# order by last char
my $expected_titles_regex = [
    "The Name of the Wind",
    "The Tree-Body Problem",
    "Caliban's War",
    "Leviathan Wakes",
];

my $expected_prices_asc = [ 5, 6, 6, 11 ];


subtest order_by_simple => sub {
    eq_or_diff(
        $books->order_by("title")->map_by("title")->to_ref,
        $expected_titles_str,
        "order_by default everything, scalar context",
    );
    eq_or_diff(
        [ $books->order_by("title")->map_by("title") ],
        $expected_titles_str,
        "order_by default everything, list context",
    );
};


subtest order_by_num_str => sub {
    eq_or_diff(
        $books->order_by(price => "num")->map_by("price")->to_ref,
        $expected_prices_asc,
        "order_by num",
    );
    eq_or_diff(
        $books->order_by(title => "str")->map_by("title")->to_ref,
        $expected_titles_str,
        "order_by str",
    );
};


subtest order_by_asc_desc => sub {
    eq_or_diff(
        $books->order_by(title => "asc")->map_by("title")->to_ref,
        $expected_titles_str,
        "order_by str asc",
    );
    eq_or_diff(
        $books->order_by(title => "desc")->map_by("title")->to_ref,
        $expected_titles_str->reverse->to_ref,
        "order_by str desc",
    );
};

subtest order_by_method_accessor => sub {
    # Call method with dummy arg
    eq_or_diff(
        $books->order_by([ title_uc => "dummy" ])->map_by("title")->to_ref,
        $expected_titles_str,
        "order_by method call without comparison",
    );
    eq_or_diff(
        $books->order_by([ title_uc => "dummy" ] => "desc")->map_by("title")->to_ref,
        $expected_titles_str->reverse->to_ref,
        "order_by method call with comparison",
    );
};


subtest order_by_sub => sub {
    eq_or_diff(
        $books->order_by(price => [ "num", sub { 0 - $_ } ])->map_by("price")->to_ref,
        $expected_prices_asc->reverse->to_ref,
        "order_by num, subref",
    );
};


subtest order_by_regex => sub {
    eq_or_diff(
        $books->order_by(title => qr/(.)$/)->map_by("title")->to_ref,
        $expected_titles_regex,
        "order_by regex",
    );
};


subtest order_by_multiple_options__num_desc => sub {
    eq_or_diff(
        $books->order_by(price => [ "num", "desc" ])->map_by("price")->to_ref,
        $expected_prices_asc->reverse->to_ref,
        "order_by num, desc",
    );
};

subtest accessor_missing => sub {
    throws_ok(
        sub { $books->order_by() },
        qr/\Q->order_by() missing argument:\E \$accessor/,
        "Missing arg dies ok",
    );
};

subtest comparison_args_validation => sub {
    throws_ok(
        sub { $books->order_by(title => "blah")->to_ref },
        qr/\Q->order_by(): Invalid comparison option (blah)/,
        "Invalid arg dies ok",
    );
    throws_ok(
        sub { [1]->order_by(title => [ "asc", "desc" ])->to_ref },
        qr/\Q->order_by(): Conflicting comparison options: (asc) and (desc)/,
        "Invalid arg dies ok",
    );
    # Ignore ugly subref vs regex for now
};

subtest order_by_multiple_comparisons => sub {
    my $words = [
        "abc",
        "def",
        "ABC",
        "DEF",
        "Abc",
    ]->map(sub { { word => $_ }});
    # ASCII order is UPPER before lower
    my $expected_words = [
        "DEF",
        "def",
        "ABC",
        "Abc",
        "abc",
    ];
    eq_or_diff(
        $words->order_by(
            word => [ desc => sub { uc($_) } ],
            word => qr/(.+)/,
        )->map_by("word")->to_ref,
        $expected_words,
        "First reverse uc, then whole match cmp",
    );
};

subtest examples => sub {
    my $titles = [
        "Leviathan Wakes",
        "The Name of the Wind",
        "The Tree-Body Problem",
        "Caliban's War",
        "The Butcher of Anderson Station",
    ];
    my $expected_titles = [
        "The Butcher of Anderson Station",
        "Caliban's War",
        "Leviathan Wakes",
        "The Name of the Wind",
        "The Tree-Body Problem",
    ];
    eq_or_diff(
        $titles->order( qr/^ (?: The \s+ )? (.+) /x )->to_ref,
        $expected_titles,
        "regex to remove leading article",
    );
};



done_testing();