#!env perl6
use lib "lib";
use Red;
use Red::Utils;
use Red::AST::CreateColumn;
use Red::AST::ChangeColumn;
use Red::AST::DropColumn;

my %*SUB-MAIN-OPTS =
  :named-anywhere,
;

#| List tables from database schema
multi MAIN(
    "list-tables",
    Str  :$driver!,
    *%pars
) {
    my $*RED-DB = database($driver, |%pars);

    my $schema-reader = $*RED-DB.schema-reader;

    .say for $schema-reader.tables-names
}

sub gen-stub(:@includes, :@models, :$driver, :%pars) {
    say 'use Red \<red-do>;';
    for @includes.unique {
        say "use $_;"
    }
    say "\nred-defaults \"{ $driver }\", { %pars.map(*.perl) };";
    say "";
    for @models {
        say ".say for { $_ }.^all;"
    }
}

#| Generate models' code from database schema
multi MAIN(
    "print-stub",
    Str  :$schema-class,
    Str  :$driver!,
    *%pars
) {

    my $*RED-DB = database($driver, |%pars);

    my $schema-reader = $*RED-DB.schema-reader;

    my @includes;
    my @models;
    for $schema-reader.tables-names -> $table-name {
        my $model-name = snake-to-camel-case $table-name;
        @models.push: $model-name;
        with $schema-class {
            @includes.push: $schema-class;
        } else {
            @includes.push: $model-name;
        }
    }

    gen-stub :@includes, :@models, :$driver, :%pars
}


#| Generate models' code from database schema
multi MAIN(
    "migration-plan",
    Str :$model,
    Str :$require = $model,
    Str :$driver!,
    *%pars
) {
    my $*RED-DB = database($driver, |%pars);
    my %steps;
    require ::($require);
    for $*RED-DB.diff-to-ast: ::($model).^diff-from-db -> @data {
        say "Step ", ++$, ":";
        #say @data.join("\n").indent: 4
#        $*RED-DB.translate($_).key.indent(4).say for Red::AST::ChangeColumn.optimize: @data
        $*RED-DB.translate($_).key.indent(4).say for @data
    }
}

#| Generate models' code from database schema
multi MAIN(
    "generate-code",
    Str  :$path     where { not .defined or .IO.d or $_ eq "-" or fail "Path $_ does not exist." },
    Str  :$from-sql where { not .defined or .IO.f or $_ eq "-" or fail "SQL $_ do not exist." },
    Str  :$schema-class,
    Bool :$print-stub       = False,
    Bool :$no-relationships = False,
    #Bool :$stub-only,
    Str  :$driver!,
    *%pars
) {
    my $*RED-DB = database($driver, |%pars);

    my $schema-reader = $*RED-DB.schema-reader;

    my $schema = do if $path eq "-" {
        $*OUT
    } else {
        $path.IO.add("$_.pm6").open: :!bin, :w with $schema-class
    }

    my $sql = $from-sql eq "-" ?? $*IN !! .IO.open with $from-sql;

    my Bool $no-use = False;
    my @includes;
    my @models;
    for |(
        $sql
            ?? $sql.slurp
            !! $schema-reader.tables-names
    ) -> $name-or-sql {
        for |(
            $name-or-sql.contains(" ")
                ?? $schema-reader.table-definition-from-create-table($name-or-sql)
                !! $schema-reader.table-definition($name-or-sql)
        ) -> $table-definition {
            my $table-name = $table-definition.name;
            my $model-name = $table-definition.model-name;
            @models.push: $model-name;
            my $fh = do with $schema {
                @includes.push: $schema-class if $schema-class;
                $_
            } else {
                @includes.push: $model-name;
                $path.IO.add("{ $model-name }.pm6").open: :!bin, :w
            }
            $fh.say: "use Red;\n" unless $no-use;
            $fh.say: "#| Table: $table-name";
            $fh.say: $table-definition.to-code:
                    :$no-relationships,
                    |(:$schema-class with $schema-class)
            ;
            with $schema {
                $no-use++ if $schema-class
            } else {
                $fh.close unless $path eq "-";
            }
        }
    }
    $schema.close if $schema.defined and $path ne "-";
    gen-stub :@includes, :@models, :$driver, :%pars if $print-stub
}
