use strict;
use warnings;

use Test::More;

use lib 't/lib';

use Fey::ORM::Test;
use Fey::Literal::String;
use Fey::Placeholder;
use Fey::Test;


Fey::ORM::Test::define_live_classes();
Fey::ORM::Test::insert_user_data();

plan tests => 9;


{
    package User;

    use Fey::ORM::Table;

    has_many messages => ( table => Schema->Schema()->table('Message') );

    package Message;

    use Fey::ORM::Table;

    has_many 'child_messages' =>
        ( table => Schema->Schema()->table('Message') );
}

{
    my $parent =
        Message->insert( message_id => 1,
                         message    => 'parent body',
                         user_id    => 1,
                       );

    for my $i ( 1 .. 3 )
    {
        Message->insert( message_id        => $i * 3,
                         message           => 'child body',
                         parent_message_id => $parent->message_id(),
                         user_id           => ( $i % 2 ? 1 : 42 ),
                       );
    }

    my $user = User->new( user_id => 1 );

    my $messages = $user->messages();

    is_deeply( [ sort map { $_->message_id() } $messages->all() ],
               [ 1, 3, 9 ],
               'messages() method returns iterator with expected message data' );

    $messages = $parent->child_messages();

    is_deeply( [ sort map { $_->message_id() } $messages->all() ],
               [ 3, 6, 9 ],
               'child_messages() method returns iterator with expected message data' );
}

{
    package User;

    __PACKAGE__->meta()->remove_method('message' );

    has_many messages =>
        ( table    => Schema->Schema()->table('Message'),
          order_by => [ Schema->Schema()->table('Message')->column('message_id'), 'DESC' ],
        );
}

{
    my $user = User->new( user_id => 1 );

    my $messages = $user->messages();

    is_deeply( [ map { $_->message_id() } $messages->all() ],
               [ 9, 3, 1 ],
               'messages() method returns iterator with expected message data, respecting order_by' );
}

{
    package User;

    __PACKAGE__->meta()->remove_method('message' );

    has_many messages =>
        ( table => Schema->Schema()->table('Message'),
          cache => 1,
        );
}

{
    my $user = User->new( user_id => 1 );

    isa_ok( $user->messages(), 'Fey::Object::Iterator::Caching' );
}

{
    my $schema = Schema->Schema();

    $schema->remove_foreign_key($_)
        for $schema->foreign_keys_between_tables( 'Message', 'User' );

    $schema->remove_foreign_key($_)
        for $schema->foreign_keys_between_tables( 'Message', 'Message' );

    # These definitions invert the source/target labeling of the
    # corresponding FKs in Fey::Test. The goal is to test that
    # has_many figures out the proper "direction" of the FK.
    my $fk1 =
        Fey::FK->new
            ( source_columns => [ $schema->table('User')->column('user_id') ],
              target_columns => [ $schema->table('Message')->column('user_id') ],
            );

    my $fk2 =
        Fey::FK->new
            ( source_columns => [ $schema->table('Message')->column('message_id') ],
              target_columns => [ $schema->table('Message')->column('parent_message_id') ],
            );

    $schema->add_foreign_key($_) for $fk1, $fk2;

    package User;

    __PACKAGE__->meta()->remove_method('messages');

    has_many messages => ( table => Schema->Schema()->table('Message') );

    package Message;

    __PACKAGE__->meta()->remove_attribute('child_messages');

    has_many 'child_messages' =>
        ( table => Schema->Schema()->table('Message') );

}

{
    my $user = User->new( user_id => 1 );

    my $messages = $user->messages();

    is_deeply( [ sort map { $_->message_id() } $messages->all() ],
               [ 1, 3, 9 ],
               'messages() method returns iterator with expected message data' );

    my $parent = Message->new( message_id => 1 );

    $messages = $parent->child_messages();

    is_deeply( [ sort map { $_->message_id() } $messages->all() ],
               [ 3, 6, 9 ],
               'messages() method returns iterator with expected message data' );
}

{
    my $user = User->new( user_id => 1 );

    my $messages = $user->messages();

    $messages->next();
    $messages->next();

    $messages = $user->messages();

    is_deeply( [ sort map { $_->message_id() } $messages->all() ],
               [ 1, 3, 9 ],
               'messages() method resets iterator with each call' );
}

{
    package Message;

    my $schema = Schema->Schema();

    my $select =
        Schema->SQLFactoryClass()->new_select()
              ->select( $schema->table('User') )
              ->from( $schema->table('User'), $schema->table('Message') )
              ->where( $schema->table('Message')->column('parent_message_id'),
                       '=', Fey::Placeholder->new() )
              ->order_by( $schema->table('Message')->column('message_id') );

    has_many 'child_message_users' =>
        ( table       => $schema->table('User'),
          select      => $select,
          bind_params => sub { $_[0]->message_id() },
        );
}

{
    my $message = Message->new( message_id => 1 );

    my @users = $message->child_message_users()->all();

    is( scalar @users, 3,
        'found two users from child_message_user()' );

    is_deeply( [ map { $_->user_id() } @users ],
               [ 1, 42, 1 ],
               'users are returned in expected order' );
}
