The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Test::Variant;

use Test::Deep;
use Test::Exception;
use Test::More;
use Test::Roo::Role;

test 'variant tests' => sub {

    my $self = shift;

    my ( $product, $data, $ret, $rset, $result, $expected );

    my $schema = $self->ic6s_schema;
    my $rset_attribute = $schema->resultset('Attribute');

    lives_ok(
        sub {
            $product = $self->products->find( { sku => 'os28066' } );
        },
        "find product os28066"
    );

    throws_ok(
        sub { $product->add_variants( { color => 'red' } ) },
        qr/SKU missing in input for add_variants./,
        "Fail add_variants with missing sku"
    );

    throws_ok(
        sub { $product->add_variants( { color => 'red', sku => undef } ) },
        qr/SKU missing in input for add_variants./,
        "Fail add_variants with undef sku"
    );

    throws_ok(
        sub {
            $product->add_variants(
                { fruit => 'banana', sku => 'G0001-YELLOW-S' } );
        },
        qr/Missing variant attribute 'fruit' for SKU G0001-YELLOW-S/,
        "Fail to add with bad attribute fruit"
    );

    throws_ok(
        sub {
            $product->add_variants(
                { color => 'orange', sku => 'G0001-YELLOW-S' } );
        },
        qr/Missing variant attribute value 'orange' for attribute 'color'/,
        "Fail to add with bad color value orange"
    );

    throws_ok(
        sub {
            $ret = $self->attributes->create(
                { name => 'color', title => 'Color', type => 'variant' } );
        },
        qr/unique|duplicate/i,
        "Add color attribute a second time fails"
    );

    lives_ok(
        sub {
            $rset_attribute->create(
                {
                    name             => 'weight',
                    title            => 'Weight',
                    type             => 'variant',
                    attribute_values => [ { value => '6', title => '6' }, ]
                }
            );
        },
        "add weight attribute"
    );

    lives_ok(
        sub { $product->add_variants(
                {
                    sku => 'G0001-weight6-white',
                    uri => 'G0001-weight6-white',
                    attributes => {
                        weight => 6,
                        color => 'white',
                    }
                }
            )}, "add G0001-weight6-white' using explicit syntax"
    );

    lives_ok( sub { $ret = $self->products->find('G0001-weight6-white') },
        "find G0001-weight6-white" );

    isa_ok( $ret, 'Interchange6::Schema::Result::Product' );

    cmp_ok( $ret->weight, '==', 1, "weight column == 1" );

    lives_ok(
        sub { $ret = $product->attribute_iterator( hashref => 1 ) },
        "get hashred attribute_iterator for os28066"
    );

    $expected = {
        blade => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "Steel",
                    value    => "steel"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Plastic",
                    value    => "plastic"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Titanium",
                    value    => "titanium"
                }
            ),
            name     => "blade",
            priority => 1,
            title    => "Blade",
        },
        color => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "White",
                    value    => "white"
                }
            ),
            name     => "color",
            priority => 1,
            title    => "Color"
        },
        handle => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "Ebony",
                    value    => "ebony"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Wood",
                    value    => "wood"
                }
            ),
            name     => "handle",
            priority => 2,
            title    => "Handle",
        },
        weight => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => 6,
                    value    => 6
                }
            ),
            name     => "weight",
            priority => 0,
            title    => "Weight"
        }
    };
    cmp_deeply( $ret, $expected, "hashref iterator is as expected" );

    lives_ok(
        sub {
            $ret = $product->attribute_iterator(
                order_by => [
                    { -desc => 'attribute.name' },
                    { -desc => 'attribute_value.value' },
                ]
            );
        },
        "get arrayref attribute_iterator for os28066 with 'order_by' arg"
    );

    $expected = [
        {
            attribute_values => [
                {
                    priority => 0,
                    selected => 0,
                    title    => 6,
                    value    => 6
                }
            ],
            name     => "weight",
            priority => 0,
            title    => "Weight"
        },
        {
            attribute_values => [
                {
                    priority => 0,
                    selected => 0,
                    title    => "Wood",
                    value    => "wood"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Ebony",
                    value    => "ebony"
                },
            ],
            name     => "handle",
            priority => 2,
            title    => "Handle",
        },
        {
            attribute_values => [
                {
                    priority => 0,
                    selected => 0,
                    title    => "White",
                    value    => "white"
                },
            ],
            name     => "color",
            priority => 1,
            title    => "Color"
        },
        {
            attribute_values => [
                {
                    priority => 0,
                    selected => 0,
                    title    => "Titanium",
                    value    => "titanium"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Steel",
                    value    => "steel"
                },   
                {
                    priority => 0,
                    selected => 0,
                    title    => "Plastic",
                    value    => "plastic"
                },
            ],
            name     => "blade",
            priority => 1,
            title    => "Blade",
        },
    ];
    cmp_deeply( $ret, $expected, "hashref iterator is as expected" );

    lives_ok(
        sub {
            $ret = $product->attribute_iterator(
                hashref => 1,
                cond    => { 'attribute.name' => 'weight' }
            );
        },
        "get hashred attribute_iterator for os28066 with 'cond' arg"
    );

    $expected = {
        weight => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => 6,
                    value    => 6
                }
            ),
            name     => "weight",
            priority => 0,
            title    => "Weight"
        }
    };
    cmp_deeply( $ret, $expected, "hashref iterator is as expected" );

    lives_ok(
        sub { $ret = $self->products->find('G0001-weight6-white')->delete },
        "delete G0001-weight6-white" );


    $ret = $product->variants->count;

    ok( $ret == 6, 'Number of variants' ) || diag "count: $ret.";

    $ret = $product->find_variant(
        {
            handle => 'ebony',
            blade  => 'plastic',
        }
    );

    isa_ok( $ret, 'Interchange6::Schema::Result::Product' );

    ok( $ret->sku eq 'os28066-E-P',
        'Check find_variant result for ebony/plastic' )
      || diag "Result: ", $ret->sku;

    # find_variant against variant should go via canonical

    lives_ok(
        sub {
            $ret =
              $ret->find_variant( { handle => 'wood', blade => 'steel', } );
        },
        "find_variant on a variant"
    );

    isa_ok( $ret, 'Interchange6::Schema::Result::Product' );

    cmp_ok( $ret->sku, 'eq', 'os28066-W-S', 'got same variant back' );

    # call find_variant without input
    my %match_info;

    $ret = $product->find_variant(
        {
            handle => undef,
            blade  => undef,
        },
        \%match_info
    );

    ok( !defined $ret, 'Check find_variant result without input.' );

    $expected = {
        'os28066-E-P' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-E-S' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-E-T' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-W-P' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-W-S' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-W-T' => {
            handle => 0,
            blade  => 0,
        },
    };

    is_deeply( \%match_info, $expected, "Check match information" );

    # find missing variant
    $ret = $product->find_variant(
        {
            handle => 'banana',
            blade  => 'steel',
        },
        \%match_info
    );

    ok( !defined $ret,
        'Check find_variant result for missing variant banana/steel' )
      || diag "Result: ", $ret;

    $expected = {
        'os28066-E-P' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-E-S' => {
            handle => 0,
            blade  => 1,
        },
        'os28066-E-T' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-W-P' => {
            handle => 0,
            blade  => 0,
        },
        'os28066-W-S' => {
            handle => 0,
            blade  => 1,
        },
        'os28066-W-T' => {
            handle => 0,
            blade  => 0,
        },
    };

    is_deeply( \%match_info, $expected, "Check match information" );

    $ret = $product->attribute_iterator;

    # expecting two records, first Color and second Size
    ok(
        ref($ret) eq 'ARRAY' && @$ret == 2,
        "Number of records in attribute iterator"
    ) || diag "Results: $ret";

    ok(
        ref( $ret->[0] ) eq 'HASH' && $ret->[0]->{name} eq 'handle',
        "handle is first record in attribute_iterator"
    ) || diag "Name in first record: ", $ret->[0]->{name};

    ok(
        ref( $ret->[1] ) eq 'HASH' && $ret->[1]->{name} eq 'blade',
        "blade is second record in attribute_iterator"
    ) || diag "Name in second record: ", $ret->[1]->{name};

    # checking Color and Size records
    my $handles_record = $ret->[0]->{attribute_values};
    my $blades_record  = $ret->[1]->{attribute_values};

    ok( ref($handles_record) eq 'ARRAY' && @$handles_record == 2,
        "Number of records in handles iterator" ) or diag scalar @$handles_record;

    ok( ref($blades_record) eq 'ARRAY' && @$blades_record == 3,
        "Number of records in blades iterator" ) or diag scalar @$blades_record;

    # attribute_iterator with hashref => 1

    # first of all a canonical product with just hashref => 1 arg so we check
    # that "selected" in return is always 0

    lives_ok(
        sub { $ret = $product->attribute_iterator( hashref => 1 ) },
        "get hashred attribute_iterator for os28066"
    );

    $expected = {
        blade => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "Steel",
                    value    => "steel"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Plastic",
                    value    => "plastic"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Titanium",
                    value    => "titanium"
                }
            ),
            name     => "blade",
            priority => 1,
            title    => "Blade",
        },
        handle => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "Ebony",
                    value    => "ebony"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Wood",
                    value    => "wood"
                }
            ),
            name     => "handle",
            priority => 2,
            title    => "Handle",
        }
    };
    cmp_deeply( $ret, $expected, "hashref iterator is as expected" );

    # now canonical with hashref => 1 and sku => os28066-E-P to check
    # that "selected" in return is 1 in correct places

    lives_ok(
        sub {
            $ret = $product->attribute_iterator(
                hashref  => 1,
                selected => 'os28066-E-P'
            );
        },
        "get hashred attribute_iterator for os28066 selected => os28066-E-P"
    );

    $expected = {
        blade => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "Steel",
                    value    => "steel"
                },
                {
                    priority => 0,
                    selected => 1,
                    title    => "Plastic",
                    value    => "plastic"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Titanium",
                    value    => "titanium"
                }
            ),
            name     => "blade",
            priority => 1,
            title    => "Blade",
        },
        handle => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 1,
                    title    => "Ebony",
                    value    => "ebony"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Wood",
                    value    => "wood"
                }
            ),
            name     => "handle",
            priority => 2,
            title    => "Handle",
        }
    };
    cmp_deeply( $ret, $expected, "hashref iterator is as expected" );

    # now a variant with just hashref => 1 arg so we check
    # that "selected" in return is 1 in correct places

    lives_ok( sub { $product = $self->products->find('os28066-E-P') },
        "find product os28066-E-P" );

    lives_ok(
        sub { $ret = $product->attribute_iterator( hashref => 1 ) },
        "get hashred attribute_iterator for os28066-E-P"
    );

    $expected = {
        blade => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 0,
                    title    => "Steel",
                    value    => "steel"
                },
                {
                    priority => 0,
                    selected => 1,
                    title    => "Plastic",
                    value    => "plastic"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Titanium",
                    value    => "titanium"
                }
            ),
            name     => "blade",
            priority => 1,
            title    => "Blade",
        },
        handle => {
            attribute_values => bag(
                {
                    priority => 0,
                    selected => 1,
                    title    => "Ebony",
                    value    => "ebony"
                },
                {
                    priority => 0,
                    selected => 0,
                    title    => "Wood",
                    value    => "wood"
                }
            ),
            name     => "handle",
            priority => 2,
            title    => "Handle",
        }
    };
    cmp_deeply( $ret, $expected, "hashref iterator is as expected" );

    # this is a pure code-coverage test trying to do something unexpected
    # using find_variant when variant attribute has no values
    $data = {
        sku         => "sku-654",
        name        => "sku 654",
        description => "product sku-654",
        price       => 1,
        variants    => [
            {
                sku                => "sku-654-variant",
                name               => "sku 654 variant",
                description        => "product sku-654-variant",
                price              => 1,
                product_attributes => [
                    {
                        attribute => {
                            name => "sku-654_attribute",
                            type => "variant",
                        },
                    },
                ],
            },
        ],
    };
    lives_ok(
        sub { $product = $self->products->create($data); },
        "Create product with attribute but no attribute values"
    );

    lives_ok {
        $ret = $product->find_variant(
            {
                handle => 'ebony',
                blade  => 'plastic',
            }
          )
    }
    "find_variant";

    ok !defined $ret, "nothing found as expected";

    # TODO:
    # jeff_b comment on GH#86
    # Similarly, I found a need for something that would retrieve all product
    # attribute values for a multi-valued attribute. ...

    my $count = $self->products->count;

    $data = {
        sku                => "922",
        name               => "sku 922",
        description        => "product with sku 922",
        product_attributes => [
            {
                attribute => {
                    name             => "video_url",
                    attribute_values => [
                        {
                            value =>
                              'http://www.youtube.com/watch?v=q57Kgb-oyfQ',
                            priority => 1,
                        },
                        {
                            value =>
                              'http://www.youtube.com/watch?v=binmsZ0eQEg',
                            priority => 0,
                        },
                    ],
                },
            },
        ],
    };

    lives_ok( sub { $product = $self->products->create($data); },
        "Create product with sku 922" );

    $count++;
    cmp_ok( $self->products->count, '==', $count, "$count Products" );

    $data = {
        sku                => "123",
        name               => "sku 123",
        description        => "product with sku 123",
        product_attributes => [
            {
                attribute => {
                    name             => "video_url",
                    attribute_values => [
                        {
                            value =>
                              'http://www.youtube.com/watch?v=q57Kgb-111',
                            priority => 2,
                        },
                        {
                            value =>
                              'http://www.youtube.com/watch?v=binmsZ0-222',
                            priority => 3,
                        },
                    ],
                },
            },
        ],
    };

    lives_ok( sub { $self->products->create($data); },
        "Create product with sku 123" );

    $count++;
    cmp_ok( $self->products->count, '==', $count, "$count Products" );

    lives_ok(
        sub {
            $rset =
              $product->product_attributes->search_related(
                "attribute",
                { name => "video_url" },
              )->first->search_related(
                "attribute_values",
                undef,
                { order_by => { -asc => "priority" } }
              )
        },
        "fetch attribute values for product attribute name video_url"
    );

    cmp_ok( $rset->count, '==', 2, "2 results found" );

    lives_ok( sub { $result = $rset->next }, "get 1st result" );

    cmp_ok( $result->value, 'eq', 'http://www.youtube.com/watch?v=binmsZ0eQEg',
        "value of 1st result is correct" );

    cmp_ok( $result->priority, '==', 0, "priority of 1st result is correct" );

    lives_ok( sub { $result = $rset->next }, "get 2nd result" );

    cmp_ok( $result->value, 'eq', 'http://www.youtube.com/watch?v=q57Kgb-oyfQ',
        "value of 2nd result is correct" );

    cmp_ok( $result->priority, '==', 1, "priority of 2nd result is correct" );

    # cleanup
    lives_ok( sub { $self->clear_products },   "clear_products" );
    lives_ok( sub { $self->clear_attributes }, "clear_attributes" );
};

1;