unit class Red::Cli;
use Red::Database;
use Red::Do;
use Red::Schema;
use Red::Utils;
use Red::AST::CreateColumn;
use Red::AST::ChangeColumn;
use Red::AST::DropColumn;


#| Lists tables from database schema
multi list-tables(
        Str  :$driver!,
        *%pars
) is export {
    my $schema-reader = $*RED-DB.schema-reader;

    $schema-reader.tables-names.do-it
}

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

#| Generates stub code to access models from database schema
multi gen-stub-code(
        Str  :$schema-class,
        Str  :$driver!,
        *%pars
) is export {
    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).join: "\n"
}


#| Generates migration plan to upgrade database schema
multi migration-plan(
        Str :$model!,
        Str :$require = $model,
        Str :$driver!,
        *%pars
) is export {
    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
    }
}

#| Generates models' code from database schema
multi 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
) is export {
    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
}

#| Prepare database
multi prepare-database(
        Bool :$populate,
        Str  :$models!,
        Str  :$driver!,
        *%pars
) is export {
    my @m = schema($models.split: ",").create.models.values;
    @m.map: { .^populate } if $populate
}