#!perl
BEGIN
{
	use strict;
	use warnings;
    use Test::More qw( no_plan );
    select(($|=1,select(STDERR),$|=1)[1]);
    use DateTime;
    use DateTime::TimeZone;
    use DateTime::Format::Strptime;
    use Module::Generic::File qw( file );
	our $DEBUG = exists( $ENV{AUTHOR_TESTING} ) ? $ENV{AUTHOR_TESTING} : 0;
};

SKIP:
{
    eval
    {
        require DBD::SQLite;
    };
    if( $@ )
    {
        skip( "DBD::SQLite is not installed", 63 );
    }
    else
    {
        use_ok( 'DB::Object::SQLite' );
        use_ok( "DB::Object::SQLite::Query" );
        use_ok( "DB::Object::SQLite::Statement" );
        use_ok( "DB::Object::SQLite::Tables" );
    }
    
    my( $test_file, $path, $suf ) = File::Basename::fileparse( __FILE__, qr/\.[^\.]+$/ );
    
    our $test_db = 'db_object_test.sqlite';
    unlink( "$path/$test_db" ) if( -e( "$path/$test_db" ) );
    our $con_uri = $ENV{DB_CON_URI} = "file:${path}/$test_db";
    my $sql = DB::Object::SQLite->new;
    # $sql->debug( 3 );
    ok( $sql, "Getting DB::Object::SQLite object" );
    isa_ok( $sql, 'DB::Object::SQLite', "Checking class of object" );
    my @db = $sql->databases;
    ok( @db, "Checking available databases" );
    diag( sprintf( "Found the databases: %s\n", join( ", ", @db ) ) );
    if( grep( /^main$/, @db ) )
    {
        pass( "Main test database found" );
    }
    else
    {
        fail( "Main test database not found" );
    }
    
    # diag( "Testing private functions" );
    is( $sql->_ceiling( 45.5 ), 46, "Checking ceil()" );
    is( $sql->_concat( 'Well, ', 'hello ', 'world' ), 'Well, hello world', "Checking concat()" );
    my $tz;
    eval
    {
        $tz = DateTime::TimeZone->new( name => 'local' );
    };
    if( $@ )
    {
        $tz = DateTime::TimeZone->new( name => 'UTC' );
    }
    diag( "Using time zone '", $tz->name, "'" ) if( $ENV{AUTOMATED_TESTING} );
    
    my $fmt = DateTime::Format::Strptime->new(
        locale => 'en_GB',
        time_zone => $tz->name,
        pattern => '%Y-%m-%dT%T%z',
    );
    my $dt = DateTime->from_epoch( epoch => time(), time_zone => $tz );
    $dt->set_formatter( $fmt );
    # diag( "Will be using this timestamp as a reference: $dt" );
    my $date = $dt->ymd( '-' );
    is( $sql->_curdate, "$date", "Checking curdate()" );
    my $time = $dt->hms( ':' );
    like( $sql->_curtime, qr/\d{2}\:\d{2}\:\d{2}/, "Checking curtime()" );
    is( $sql->_dayname( "$dt" ), $dt->day_name, "Checking dayname()" );
    is( $sql->_dayofmonth( "$dt" ), $dt->day, "Checking dayofmonth()" );
    is( $sql->_dayofweek( "$dt" ), $dt->day_of_week, "Checking dayofweek()" );
    is( $sql->_dayofyear( "$dt" ), $dt->day_of_year, "Checking dayofyear()" );
    my @coordinates = qw( 35.7132311 139.7174027 35.68117 139.7327573 );
    is( CORE::sprintf( "%.12f", $sql->_distance_miles( @coordinates ) ), 2.377034154241, "Checking distance_miles()" );
    my $check_t = DateTime->new(
        year => 1970,
        month => 3,
        day => 31,
        hour => 0,
        minute => 0,
        second => 0,
        time_zone => 'UTC',
        # time_zone => $tz,
    );
    $check_t->set_time_zone( $tz->name );
    $check_t->set_year(1970);
    $check_t->set_month(3);
    $check_t->set_day(31);
    # e.g. 1970-03-31T09:00:00
    is( $sql->_from_days(719617), "$check_t", "Checking from_days()" );
    is( $sql->_from_unixtime( $dt->epoch ), $dt->strftime( '%Y-%m-%d %T%z' ), "Checking from unix_time()" );
    is( $sql->_hour( "$dt" ), $dt->hour, "Checking hour()" );
    is( $sql->_lcase( "CamEl WRiTing" ), 'camel writing', "Checking lcase()" );
    is( $sql->_left( 'Awesome', 3 ), 'Awe', "Checking left()" );
    is( $sql->_locate( 'hello@deguest.jp', '@' ), 5, "Checking locate()" );
    is( $sql->_log10( 1000 ), 3, "Checking log10()" );
    is( $sql->_minute( "$dt" ), $dt->minute, "Checking minute()" );
    is( $sql->_month( "$dt" ), $dt->month, "Checking month()" );
    is( $sql->_monthname( "$dt" ), $dt->month_name, "Checking monthname()" );
    # is( $sql->_number_format( 1000, ',', '.', 2 ), '1,000.00', "Checking number_format()" );
    is( $sql->_power( 2, 3 ), 8, "Checking power()" );
    is( $sql->_quarter( "$dt" ), $dt->quarter, "Checking quarter()" );
    my $random_number = $sql->_rand;
    pass( "Random number generated: $random_number" );
    is( $sql->_replace( 'Hello John!', 'John', 'Jacques' ), 'Hello Jacques!', "Checking replace()" );
    is( $sql->_right( 'Wonderful', 3 ), 'ful', "Checking right()" );
    is( $sql->_second( "$dt" ), $dt->second, "Checking second()" );
    is( $sql->_space( 7 ), '       ', "Checking space()" );
    is( $sql->_sprintf( "There are '%d' petals to this flower", 3 ), "There are '3' petals to this flower", "Checking sprintf()" );
    is( $sql->_to_days( '2016-06-29' ), 736509, "Checking to_days()" );
    is( $sql->_ucase( "CamEl WRiTing" ), 'CAMEL WRITING', "Checking lcase()" );
    is( $sql->_unix_timestamp( "$dt" ), $dt->epoch, "Checking unix_timestamp()" );
    is( $sql->_week( "$dt" ), $dt->week_number, "Checking week()" );
    is( $sql->_weekday( "$dt" ), $dt->day_of_week, "Checking weekday()" );
    is( $sql->_year( "$dt" ), $dt->year, "Checking year()" );
    is( $sql->_regexp( '\@example\..*$', 'job@example.org' ), 1, "Checking regexp()" );

    my $dbh = DB::Object->connect(
    'uri'       => $con_uri,
    # 'host'       => 'localhost',
    'driver'    => 'SQLite',
    # 'login'      => 'n',
    # 'passwd' => '',
    'debug'       => $DEBUG,
    ) || die( $DB::Object::ERROR );
    $dbh->verbose( 0 );
    #$dbh->quiet( 1 );
    # $dbh->debug( 3 );
    ok( $dbh, "Testing connection" );
    isa_ok( $dbh, 'DB::Object::SQLite', "Checking object class ownership" );
    
    # Load schema
    my $schemaFile = file($0)->parent->child( 'sqlite.sql' );
    my $fh = $schemaFile->open( '<', { binmode => 'utf-8' } ) ||
        BAIL_OUT( "Unable to read the sqlite schema \"$schemaFile\": $1" );
    my $queries = [];
    my $def = {};
    my $sql = '';
    while( defined( my $l = $fh->getline ) )
    {
        if( $l =~ /^\-{2}[[:blank:]]+(\d+)[[:blank:]]+(.*?)$/ )
        {
            if( length( $sql ) )
            {
                push( @$queries, { id => $def->{id}, comment => $def->{comment}, query => $sql } );
            }
            @$def{qw(id comment)} = ( $1, $2 );
            $sql = '';
            next;
        }
        $sql .= $l;
    }
    $fh->close;
    $def->{query} = $sql;
    push( @$queries, $def ) if( length( $sql ) );
    $dbh->begin_work;
    foreach my $ref ( @$queries )
    {
        # diag( "Executing query \#$ref->{id} $ref->{query}" );
        my $rv = $dbh->do( $ref->{query} ) || do
        {
            $dbh->rollback;
            BAIL_OUT( "Unable to execute query \"$ref->{query}\": " . $dbh->error );
        };
        ok( $rv, $ref->{comment} );
    }
    $dbh->commit;
    
    is( scalar( @{$dbh->tables} ), 3, "Total number of tables expected (3)" );
    BAIL_OUT( sprintf( "Total nuber of tables set up is %d", scalar( @{$dbh->tables} ) ) ) if( scalar( @{$dbh->tables} ) != 3 );
    is( $dbh->table_exists( 'customers' ), 1, "Checking table_exists with table test" );
    my $tbl = $dbh->customers || fail( "Cannot get test object." );
    ok( $tbl, sprintf( "Got customers object: %s", ref( $tbl ) ) );
    isa_ok( $tbl, 'DB::Object::SQLite::Tables', "Getting test table object" );
    is( $tbl->name, 'customers', "Checking customers table name" );
    # diag( "Table object is '$tbl." );
    
    $tbl->where( email => qr/\@example\..*$/ );
    my $test_sql = $tbl->select->as_string;
    is( $test_sql, "SELECT id, first_name, last_name, email, created, modified, active, STRFTIME('%s','created') AS created_unixtime, STRFTIME('%s','modified') AS modified_unixtime, CONCAT(first_name, ' ', last_name) AS name FROM customers WHERE email REGEXP('\\\@example\\\..*\$')", "Checking regular expression" );
    
    # $tbl->debug( 3 );
    $tbl->where( 'email' => 'john@example.org' );
    my $sth = $tbl->delete;
    diag( "Error found: ", $tbl->error ) if( !$sth );
    ## diag( "Received the sth '$sth'." );
    my $str = $sth->as_string;
    #my $str = $tbl->delete->as_string;
    is( $str, "DELETE FROM customers WHERE email='john\@example.org'", "Checking DELETE query" );
    
    {
        local $SIG{__WARN__} = sub{};
        my $fake_tbl = $dbh->table( 'plop' );
        isa_ok( $fake_tbl => 'DB::Object::Tables' );
        ok( !$dbh->table_exists( 'plop' ), "checking fake table 'plop' does not exist" );
    }

    my $tbl_exists = $dbh->table_exists( 'customers' );
    if( !defined( $tbl_exists ) )
    {
        fail( "Error checking if table \"customers\" exists: " . $dbh->error );
    }
    else
    {
        is( $tbl_exists, 1, "Checking existence of table customers with table_exists()" );
    }
    $str = $tbl->insert(
        first_name => 'Paul',
        last_name => 'Goldman',
        email => 'paul@example.org',
        active => 0,
    )->as_string;
    my $expected = <<SQL;
INSERT INTO customers (first_name, last_name, email, active) VALUES('Paul', 'Goldman', 'paul\@example.org', '0')
SQL
    chomp( $expected );
    is( $str, $expected, "Checking INSERT statement" );
    
    ## Checking select query
    $tbl->reset;
    $tbl->where( email => 'john@example.org' );
    $tbl->order( 'last_name' );
    $tbl->group( 'email' );
    $tbl->having( email => qr/\@example/ );
    $tbl->limit( 10 );
    my $cust_sth_sel = $tbl->select || fail( "An error occurred while creating a query to select data frm table customers: " . $tbl->error );
    $result = $cust_sth_sel->as_string;
    $expected = <<SQL;
SELECT id, first_name, last_name, email, created, modified, active, STRFTIME('%s','created') AS created_unixtime, STRFTIME('%s','modified') AS modified_unixtime, CONCAT(first_name, ' ', last_name) AS name FROM customers WHERE email='john\@example.org' GROUP BY email HAVING email REGEXP('\\\@example') ORDER BY last_name LIMIT 10
SQL
    chomp( $expected );
    is( $result, $expected, "Checking SELECT query on customers table" );
    
    ## Checking update query
    $tbl->reset;
    $tbl->where( email => 'john@example.org' );
    my $cust_sth_upd = $tbl->update( active => 0 ) || fail( "An error has occurred while trying to create an update query for table customers: " . $tbl->error );
    $result = $cust_sth_upd->as_string;
    $expected = <<SQL;
UPDATE customers SET active='0' WHERE email='john\@example.org'
SQL
    chomp( $expected );
    is( $result, $expected, "Checking UPDATE query on customers table using ONLY clause" );
};

done_testing();

__END__
