The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;

use File::Basename;
use lib File::Basename::dirname(__FILE__)."/../../../lib";
use lib File::Basename::dirname(__FILE__)."/../..";
use URT;

use Test::More tests => 50;
use URT::DataSource::SomeSQLite;

&setup_classes_and_db();

#UR::DBI->monitor_sql(1);

my $query_count;
ok(URT::DataSource::SomeSQLite->create_subscription(
                                   method => 'query',
                                   callback => sub { $query_count++ }),
    'Created subscription to count queries');
#$DB::single = 1;
$query_count = 0;
my $thing = URT::Thing->get(name => 'Bob', -hints => [ 'attribs' ]);
ok($thing, 'get() returned an object');
is($thing->name, 'Bob', 'object name is correct');
is($thing->thing_id, 1, 'ID is correct');
is($query_count, 1, 'Correctly made 1 query');

$query_count = 0;
my @attribs = URT::Attrib->is_loaded();
is(scalar(@attribs), 2, 'The last get() also loaded 2 attribs');
@attribs = sort { $a->attrib_id <=> $b->attrib_id } @attribs;  # Just in case, but they should already be in this order...
is($query_count, 0, 'Correctly made no queries');


is($attribs[0]->name, 'alignment', 'First attrib name is correct');
is($attribs[0]->value, 'good', 'First attrib value is correct');
is($attribs[1]->name, 'job', 'Second attrib name is correct');
is($attribs[1]->value, 'cook', 'Second attrib value is correct');


$query_count = 0;
@attribs = $thing->attribs();
is(scalar(@attribs), 2, 'accessing attribs through the delegated property returned 2 things');
is($query_count, 0, 'Correctly made no queries');

is($attribs[0]->name, 'alignment', 'First attrib name is correct');
is($attribs[0]->value, 'good', 'First attrib value is correct');
is($attribs[1]->name, 'job', 'Second attrib name is correct');
is($attribs[1]->value, 'cook', 'Second attrib value is correct');


$query_count = 0;
my $person = URT::Person->get(name => 'Frank', -hints => ['params']);
ok($person, 'get() returned an object');
is($person->name, 'Frank', 'object name is correct');
is($person->person_id, 2, 'ID is correct');
is($query_count, 1, 'Correctly made 1 query');

my @bridges = URT::Bridge->is_loaded();
is(scalar(@bridges), 3, '3 bridges were loaded from the above query');

my @params = URT::Param->is_loaded();
is(scalar(@params), 3, '3 params were loaded from the above query');


$query_count = 0;
@bridges = $person->bridges();
is(scalar(@bridges), 3, 'got 3 bridges through the delegated accessor');
is($query_count, 0, 'Correctly made no queries');

$query_count = 0;
@params = $person->params();
is(scalar(@params), 3, 'got 3 params through the delegated accessor');
is($query_count, 0, 'Correctly made no queries');




sub setup_classes_and_db {
    my $dbh = URT::DataSource::SomeSQLite->get_default_handle;

    ok($dbh, 'Got DB handle');

    # attribs belong to one thing
    ok( $dbh->do("create table thing (thing_id integer, name varchar)"),
       'Created thing table');
    ok( $dbh->do("create table attrib (attrib_id integer, name varchar, value varchar, thing_id integer REFERENCES thing(thing_id))"),
       'Created attrib table');

    my $insert = $dbh->prepare("insert into thing (thing_id, name) values (?,?)");
    foreach my $row ( ( [1, 'Bob'], [2, 'Christine']) ) {
        ok( $insert->execute(@$row), 'Inserted a thing');
    }
    $insert->finish;

    $insert = $dbh->prepare("insert into attrib (attrib_id, name, value, thing_id) values (?,?,?,?)");
    foreach my $row ( ( [1, 'alignment', 'good', 1],
                        [2, 'job', 'cook', 1], 
                        [3, 'alignment', 'evil', 2],
                        [4, 'color', 'red', 2] ) ) {
        ok($insert->execute(@$row), 'Inserted an attrib');
    }
    $insert->finish;

    # params are many-to-many with people
    ok( $dbh->do("create table person (person_id integer, name varchar)"), 
        'created table foo');
    ok( $dbh->do("create table param (param_id integer, name varchar, value varchar)"),
        'created param table');
    ok( $dbh->do("create table person_param_bridge (person_id integer REFERENCES person(person_id), param_id integer REFERENCES param(param_id), PRIMARY KEY (person_id, param_id))"
),
        'created bridge table');

 
    $insert = $dbh->prepare("insert into person (person_id, name) values (?,?)");
    foreach my $row ( ( [ 1, 'Joe'],
                        [ 2, 'Frank'] )) {
        ok($insert->execute(@$row), 'inserted a person');
    }
    $insert->finish;
            
    $insert = $dbh->prepare("insert into param (param_id, name, value) values (?,?,?)");
    foreach my $row ( ( [ 1, 'rank', 'cog' ],
                        [ 2, 'status', 'single' ],
                        [ 3, 'title', 'capn' ],
                        [ 4, 'tag', 'xyzzy' ] )) {
        ok($insert->execute(@$row), 'inserted a param');
    }
    $insert->finish;

    $insert = $dbh->prepare("insert into person_param_bridge (person_id, param_id) values (?,?)");
    foreach my $row ( ( [ 1, 1 ],
                        [ 2, 1 ],
                        [ 2, 2 ],
                        [ 2, 4 ] )) {
        ok($insert->execute(@$row), 'inserted a bridge');
    }
    $insert->finish;

    
    ok($dbh->commit(), 'DB commit');
           
 
    UR::Object::Type->define(
        class_name => 'URT::Thing',
        id_by => 'thing_id',
        has => [
            name => { is => 'String' },
            attribs => { is => 'URT::Attrib', reverse_as => 'thing', is_many => 1 },
        ],
        data_source => 'URT::DataSource::SomeSQLite',
        table_name => 'thing',
    );

    UR::Object::Type->define(
        class_name => 'URT::Attrib',
        id_by => 'attrib_id', 
        has => [
            name => { is => 'String' },
            value => { is => 'String' },
            thing => { is => 'URT::Thing', id_by => 'thing_id' },
        ],
        data_source => 'URT::DataSource::SomeSQLite',
        table_name => 'attrib',
    );

    UR::Object::Type->define(
        class_name => 'URT::Person',
        id_by => 'person_id',
        has => 'name',
        has_many_optional => [
           bridges => { is => 'URT::Bridge', reverse_as => 'persons' },
           params => { via => 'bridges', to => 'params' },
        ],
        data_source => 'URT::DataSource::SomeSQLite',
        table_name => 'person',
    );
    UR::Object::Type->define(
        class_name => 'URT::Param',
        id_by => 'param_id',
        has => ['name','value'],
        data_source => 'URT::DataSource::SomeSQLite',
        table_name => 'param',
    );
    UR::Object::Type->define(
        class_name => 'URT::Bridge',
        id_by => [ 'person_id', 'param_id' ],
        has => [
            persons => { is => 'URT::Person', id_by => 'person_id' },
            params  => { is => 'URT::Param', id_by => 'param_id' },
        ],
        data_source => 'URT::DataSource::SomeSQLite',
        table_name => 'person_param_bridge',
    );

        
           
}