package Common::DB::Deploy::QuerySplitter;

use strict;
use warnings;

my $RE_TOKEN = qr{
    (
        (?:--|\#)[\ \t\S]*      # single line comments
        |
        (?:<>|<=>|>=|<=|==|=|!=|!|<<|>>|<|>|\|\||\||&&|&|-|\+|\*(?!/)|/(?!\*)|\%|~|\^|\?)
                                # operators and tests
        |
        [\[\]\(\),;.]            # punctuation (parenthesis, comma)
        |
        \'\'(?!\')              # empty single quoted string
        |
        \"\"(?!\"")             # empty double quoted string
        |
        "(?>(?:(?>[^"\\]+)|""|\\.)*)+"
                                # anything inside double quotes, ungreedy
        |
        `(?>(?:(?>[^`\\]+)|``|\\.)*)+`
                                # anything inside backticks quotes, ungreedy
        |
        '(?>(?:(?>[^'\\]+)|''|\\.)*)+'
                                # anything inside single quotes, ungreedy.
        |
        /\*[\ \t\r\n\S]*?\*/      # C style comments
        |
        (?:[\w:@]+(?:\.(?:\w+|\*)?)*)
                                # words, standard named placeholders, db.table.*, db.*
        |
        (?: \$_\$ | \$\d+ | \${1,2} )
                                # dollar expressions - eg $_$ $3 $$
        |
        \n                      # newline
        |
        [\t\ ]+                 # any kind of white spaces
    )
}smx;

use constant SEMICOLON    => ';';
use constant RE_COMMENT   => qr/^\/\*/;
use constant RE_TITLE     => qr/^--\s*(.*)\s*$/;
use constant RE_DELIMITER => qr/^DELIMITER/i;

sub new {
    my ($class, %args) = @_;

    return bless {}, $class;
}

sub split {
    my ($self, $query) = @_;

    my (@statements, @sql, @title);
    foreach my $token ($query =~ m{$RE_TOKEN}smxg) {
        if ($token eq SEMICOLON) {
            if ( scalar(@sql) ) {
                push @statements, {
                    sql   => join("", map { $_ =~ /^[\(\),:=]$/ ? $_ : " $_" } @sql),
                    title => join("\n", @title),
                };
            }
            undef @sql;
            undef @title;
            next;
        }

        $token =~ s/^\s+|\s+$//g;

        # skip empty
        next if $token eq '';

        # skip comments
        next if $token =~ RE_COMMENT;

        # skip DELIMITER
        next if $token =~ RE_DELIMITER;

        if ($token =~ RE_TITLE) {
            push @title, $1 if $1;
            next;
        }

        push @sql, $token;
    }

    if ( scalar(@sql) ) {
        push @statements, {
            sql   => join("", map { $_ =~ /^[\(\),:=]$/ ? $_ : " $_" } @sql),
            title => join("\n", @title),
        };
    }

    return wantarray ? @statements : \@statements;
}


1;