#!/usr/bin/perl

use lib qw(t/lib);
use strict;
use warnings;

use Test::OnlineDDL;

############################################################

my $CHUNK_SIZE = $CDTEST_MASS_POPULATE ? 5000 : 3;
my $dbms_name  = CDTest->dbms_name;

############################################################

onlineddl_test 'No-op copy' => 'Track' => sub {
    my $cd_schema  = shift;
    my $track_rsrc = $cd_schema->source('Track');
    my $dbh        = $cd_schema->storage->dbh;

    # Constructor
    my $online_ddl = DBIx::OnlineDDL->new(
        rsrc => $track_rsrc,
        # purposely not adding any coderef_hooks

        copy_opts => {
            chunk_size => $CHUNK_SIZE,
        },
    );

    is $online_ddl->table_name,     'track',      'Figured out table_name';
    is $online_ddl->new_table_name, '_track_new', 'Figured out new_table_name';

    my $helper = $online_ddl->_helper;

    my $orig_table_track_sql  = $helper->create_table_sql('track');
    my $orig_table_lyrics_sql = $helper->create_table_sql('lyrics');  # has FK pointing to track

    try_ok { $online_ddl->execute } 'Execute works';

    is $online_ddl->copy_opts->{id_name}, 'trackid', 'Figured out PK';

    my $new_table_track_sql  = $helper->create_table_sql('track');
    my $new_table_lyrics_sql = $helper->create_table_sql('lyrics');

    # Remove AUTO_INCREMENT information
    $orig_table_track_sql  =~ s/ AUTO_INCREMENT=\K\d+/###/;
    $orig_table_lyrics_sql =~ s/ AUTO_INCREMENT=\K\d+/###/;
    $new_table_track_sql   =~ s/ AUTO_INCREMENT=\K\d+/###/;
    $new_table_lyrics_sql  =~ s/ AUTO_INCREMENT=\K\d+/###/;

    is $new_table_track_sql,  $orig_table_track_sql,  'New table SQL for `track` matches the old one';
    is $new_table_lyrics_sql, $orig_table_lyrics_sql, 'New table SQL for `lyrics` matches the old one';
};

onlineddl_test 'Add column' => 'Track' => sub {
    my $cd_schema  = shift;
    my $track_rsrc = $cd_schema->source('Track');

    # Constructor
    my $online_ddl = DBIx::OnlineDDL->new(
        rsrc => $track_rsrc,
        coderef_hooks => {
            before_triggers => sub {
                my $oddl = shift;
                my $dbh  = $oddl->dbh;
                my $name = $oddl->new_table_name;

                my $qname = $dbh->quote_identifier($name);
                my $qcol  = $dbh->quote_identifier('test_column');

                $oddl->dbh_runner_do("ALTER TABLE $qname ADD COLUMN $qcol VARCHAR(100) NULL");
            },
        },

        copy_opts => {
            chunk_size => $CHUNK_SIZE,
        },
    );

    try_ok { $online_ddl->execute } 'Execute works';

    # Verify the column exists
    my $dbh     = $cd_schema->storage->dbh;
    my $vars    = $online_ddl->_vars;
    my $catalog = $vars->{catalog};
    my $schema  = $vars->{schema};

    my %cols = %{ $dbh->column_info( $catalog, $schema, 'track', '%' )->fetchall_hashref('COLUMN_NAME') };
    like(
        $cols{test_column},
        {
            TABLE_CAT        => $catalog,
            TABLE_SCHEM      => $schema,
            TABLE_NAME       => 'track',
            COLUMN_NAME      => 'test_column',
            COLUMN_SIZE      => 100,
            TYPE_NAME        => 'VARCHAR',
            IS_NULLABLE      => 'YES',
            NULLABLE         => 1,
            ORDINAL_POSITION => 7,
        },
        'New column exists in table',
    );
};

onlineddl_test 'Add column + title change' => 'Track' => sub {
    my $cd_schema  = shift;
    my $track_rsrc = $cd_schema->source('Track');

    # Constructor
    my $online_ddl = DBIx::OnlineDDL->new(
        rsrc => $track_rsrc,
        coderef_hooks => {
            before_triggers => sub {
                my $oddl = shift;
                my $dbh  = $oddl->dbh;
                my $name = $oddl->new_table_name;

                my $qname = $dbh->quote_identifier($name);
                my $qcol  = $dbh->quote_identifier('test_column');
                my $qidx  = $dbh->quote_identifier('track_cd_title');

                $oddl->dbh_runner_do(
                    "ALTER TABLE $qname ADD COLUMN $qcol VARCHAR(100) NULL",

                    # SQLite can't DROP on an ALTER TABLE, but isn't bothered by the breaking of
                    # a unique index (for some reason)
                    ($dbms_name eq 'SQLite' ? () :
                        "ALTER TABLE $qname DROP INDEX $qidx"
                    )
                );
            },
            before_swap => sub {
                my $oddl = shift;
                my $dbh  = $oddl->dbh;
                my $name = $oddl->new_table_name;

                my $qname = $dbh->quote_identifier($name);

                DBIx::BatchChunker->construct_and_execute(
                    chunk_size => $CHUNK_SIZE,
                    process_past_max => 1,

                    dbic_storage => $oddl->rsrc->storage,
                    min_stmt => "SELECT MIN(trackid) FROM $qname",
                    max_stmt => "SELECT MAX(trackid) FROM $qname",
                    stmt     => join( ' ',
                        'UPDATE',
                        $dbh->quote_identifier($name),
                        'SET title =',
                        $dbh->quote('This is the song that never ends'),
                        'WHERE trackid BETWEEN ? AND ?',
                    ),
                );
            },
        },

        copy_opts => {
            chunk_size => $CHUNK_SIZE,
        },
    );

    try_ok { $online_ddl->execute } 'Execute works';

    # Verify the column exists
    my $dbh     = $cd_schema->storage->dbh;
    my $vars    = $online_ddl->_vars;
    my $catalog = $vars->{catalog};
    my $schema  = $vars->{schema};

    my %cols = %{ $dbh->column_info( $catalog, $schema, 'track', '%' )->fetchall_hashref('COLUMN_NAME') };
    like(
        $cols{test_column},
        {
            TABLE_CAT        => $catalog,
            TABLE_SCHEM      => $schema,
            TABLE_NAME       => 'track',
            COLUMN_NAME      => 'test_column',
            COLUMN_SIZE      => 100,
            TYPE_NAME        => 'VARCHAR',
            IS_NULLABLE      => 'YES',
            NULLABLE         => 1,
            ORDINAL_POSITION => 7,
        },
        'New column exists in table',
    );
};

onlineddl_test 'Drop column' => 'Track' => sub {
    plan skip_all => 'SQLite cannot drop columns' if $dbms_name eq 'SQLite';

    my $cd_schema  = shift;
    my $track_rsrc = $cd_schema->source('Track');

    # Constructor
    my $online_ddl = DBIx::OnlineDDL->new(
        rsrc => $track_rsrc,
        coderef_hooks => {
            before_triggers => sub {
                my $oddl = shift;
                my $dbh  = $oddl->dbh;
                my $name = $oddl->new_table_name;

                my $qname = $dbh->quote_identifier($name);
                my $qcol  = $dbh->quote_identifier('last_updated_at');

                $oddl->dbh_runner_do("ALTER TABLE $qname DROP COLUMN $qcol");
            },
        },

        copy_opts => {
            chunk_size => $CHUNK_SIZE,
        },
    );

    try_ok { $online_ddl->execute } 'Execute works';

    # Verify the column doesn't exist
    my $dbh     = $cd_schema->storage->dbh;
    my $vars    = $online_ddl->_vars;
    my $catalog = $vars->{catalog};
    my $schema  = $vars->{schema};

    my %cols = %{ $dbh->column_info( $catalog, $schema, 'track', '%' )->fetchall_hashref('COLUMN_NAME') };

    ok(!exists $cols{last_updated_at}, 'Column dropped in table', $cols{last_updated_at});
};

onlineddl_test 'Drop PK' => 'Track' => sub {
    plan skip_all => 'SQLite cannot drop columns' if $dbms_name eq 'SQLite';

    my $cd_schema  = shift;
    my $track_rsrc = $cd_schema->source('Track');

    # Constructor
    my $online_ddl = DBIx::OnlineDDL->new(
        rsrc => $track_rsrc,
        coderef_hooks => {
            before_triggers => sub {
                my $oddl = shift;
                my $dbh  = $oddl->dbh;
                my $name = $oddl->new_table_name;

                my $qname = $dbh->quote_identifier($name);
                my $qcol  = $dbh->quote_identifier('trackid');

                $oddl->dbh_runner_do("ALTER TABLE $qname DROP COLUMN $qcol");

                $oddl->dbh_runner(run => sub {
                    $dbh = $_;

                    # Need to also drop the FK on lyrics
                    my $fk_hash = $oddl->_fk_info_to_hash( $dbh->foreign_key_info(
                        $oddl->_vars->{catalog}, $oddl->_vars->{schema}, $oddl->table_name,
                        undef, undef, undef
                    ) );

                    $dbh->do(join ' ',
                        'ALTER TABLE',
                        $dbh->quote_identifier('lyrics'),
                        'DROP',
                        # MySQL uses 'FOREIGN KEY' on DROPs, and everybody else uses 'CONSTRAINT' on both
                        ($dbms_name eq 'MySQL' ? 'FOREIGN KEY' : 'CONSTRAINT'),
                        $dbh->quote_identifier( (values %$fk_hash)[0]->{fk_name} ),
                    );
                });
            },
        },

        copy_opts => {
            chunk_size => $CHUNK_SIZE,
        },
    );

    try_ok { $online_ddl->execute } 'Execute works';

    # Verify the column doesn't exist
    my $dbh     = $cd_schema->storage->dbh;
    my $vars    = $online_ddl->_vars;
    my $catalog = $vars->{catalog};
    my $schema  = $vars->{schema};

    my %cols = %{ $dbh->column_info( $catalog, $schema, 'track', '%' )->fetchall_hashref('COLUMN_NAME') };

    ok(!exists $cols{trackid}, 'PK column dropped in table', $cols{trackid});
};

############################################################

done_testing;
