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 {

    diag Test::Variant;

    my $self = shift;

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

    my $shop_schema = $self->schema;

    lives_ok(
        sub {
            $product =
              $self->products->search( { canonical_sku => undef } )->first;
        },
        "find a canonical product"
    );

    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"
    );

    lives_ok(
        sub {
            $ret = $self->attributes->create(
                { name => 'color', title => 'Color', type => 'variant' } );
        },
        "Add color attribute a second time"
    );

    throws_ok(
        sub {
            $product->add_variants(
                { color => 'pink', sku => 'G0001-YELLOW-S' } );
        },
        qr/Ambigious variant attribute 'color' for SKU G0001-YELLOW-S/,
        "Fail add_variant now that color attributes exists twice"
    );

    lives_ok( sub { $ret->delete }, "Delete duplicate color attribute" );

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

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

    $ret = $product->find_variant(
        {
            color => 'pink',
            size  => 'medium',
        }
    );

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

    ok( $ret->sku eq 'G0001-PINK-M',
        'Check find_variant result for pink/medium' )
      || diag "Result: ", $ret->sku;

    # find_variant against variant should go via canonical

    lives_ok(
        sub {
            $ret = $ret->find_variant( { color => 'pink', size => 'medium', } );
        },
        "find_variant on a variant"
    );

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

    cmp_ok( $ret->sku, 'eq', 'G0001-PINK-M', 'got same variant back' );

    # call find_variant without input
    my %match_info;

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

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

    my $expected = {
        'G0001-PINK-M' => {
            'color' => 0,
            'size'  => 0
        },
        'G0001-YELLOW-S' => {
            'size'  => 0,
            'color' => 0
        },
        'G0001-PINK-L' => {
            'size'  => 0,
            'color' => 0
        },
        'G0001-PINK-S' => {
            'color' => 0,
            'size'  => 0
        },
        'G0001-YELLOW-L' => {
            'color' => 0,
            'size'  => 0
        }
    };

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

    # find missing variant
    $ret = $product->find_variant(
        {
            color => 'yellow',
            size  => 'medium',
        },
        \%match_info
    );

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

    # check contents of match info
    $expected = {
        'G0001-PINK-L' => {
            'color' => 0,
            'size'  => 0
        },
        'G0001-YELLOW-L' => {
            'color' => 1,
            'size'  => 0
        },
        'G0001-YELLOW-S' => {
            'size'  => 0,
            'color' => 1
        },
        'G0001-PINK-S' => {
            'color' => 0,
            'size'  => 0
        },
        'G0001-PINK-M' => {
            'size'  => 1,
            'color' => 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 'color',
        "Color is first record in attribute_iterator"
    ) || diag "Name in first record: ", $ret->[0]->{name};

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

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

    ok( ref($colors_record) eq 'ARRAY' && @$colors_record == 2,
        "Number of records in colors iterator" );

    ok(
        $colors_record->[0]->{value} eq 'pink'
          && $colors_record->[1]->{value} eq 'yellow',
        "Order of records in colors iterator"
    );

    ok( ref($sizes_record) eq 'ARRAY' && @$sizes_record == 3,
        "Number of records in sizes iterator" );

    ok(
        $sizes_record->[0]->{value} eq 'small'
          && $sizes_record->[1]->{value} eq 'medium'
          && $sizes_record->[2]->{value} eq 'large',
        "Order of records in sizes iterator"
    );

    # attribute_iterator with hashref => 1

    lives_ok( sub { $ret = $product->attribute_iterator( hashref => 1 ) },
        "attribute_iterator with hashref => 1" );

    cmp_ok( ref($ret), 'eq', 'HASH', "Got a hashref" );
    cmp_deeply(
        $ret,
        {
            color => {
                name             => 'color',
                title            => 'Color',
                priority         => 2,
                attribute_values => bag(
                    {
                        value    => 'pink',
                        title    => 'Pink',
                        selected => 0,
                        priority => 2,
                    },
                    {
                        value    => 'yellow',
                        title    => 'Yellow',
                        selected => 0,
                        priority => 1,
                    },
                ),
            },
            size => {
                name             => 'size',
                title            => 'Size',
                priority         => 1,
                attribute_values => bag(
                    {
                        value    => 'small',
                        title    => 'Small',
                        selected => 0,
                        priority => 2,
                    },
                    {
                        value    => 'medium',
                        title    => 'Medium',
                        selected => 0,
                        priority => 1,
                    },
                    {
                        value    => 'large',
                        title    => 'Large',
                        selected => 0,
                        priority => 0,
                    },
                ),
            },
        },
        "Deep comparison is good"
    );

    # test selected
    my $variant = $product->find_variant(
        {
            color => 'yellow',
            size  => 'large',
        }
    );

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

    ok(
        $variant->sku eq 'G0001-YELLOW-L',
        'Check find_variant result for pink/medium'
    ) || diag "Result: ", $ret->sku;

    $ret           = $variant->attribute_iterator;
    $colors_record = $ret->[0]->{attribute_values};
    $sizes_record  = $ret->[1]->{attribute_values};

    ok( ref($colors_record) eq 'ARRAY' && @$colors_record == 2,
        "Number of records in colors iterator" );

    ok(
        $colors_record->[0]->{value} eq 'pink'
          && $colors_record->[1]->{value} eq 'yellow',
        "Order of records in colors iterator"
    );

    ok(
        $colors_record->[0]->{selected} eq '0'
          && $colors_record->[1]->{selected} eq '1',
        "Value of selected in colors iterator"
    );

    ok( ref($sizes_record) eq 'ARRAY' && @$sizes_record == 3,
        "Number of records in sizes iterator" );

    ok(
        $sizes_record->[0]->{value} eq 'small'
          && $sizes_record->[1]->{value} eq 'medium'
          && $sizes_record->[2]->{value} eq 'large',
        "Order of records in sizes iterator"
    );

    ok(
        $sizes_record->[0]->{selected} eq '0'
          && $sizes_record->[1]->{selected} eq '0'
          && $sizes_record->[2]->{selected} eq '1',
        "Value of selected in sizes iterator"
    );

    # 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. ...

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

    $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" );

    cmp_ok( $self->products->count, '==', 7, "7 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" );

    cmp_ok( $self->products->count, '==', 8, "8 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;