### ----------------------------------------------------
### -- Red
### -- Licenses: Artistic-2.0
### -- Authors: Fernando Correa de Oliveira
### -- File: lib/Red/Driver/SQLite/SchemaReader.rakumod
### ----------------------------------------------------
use Red::SchemaReader;
use Red::Driver::SQLite::SQLiteMaster;

#| An internal class to read SQLite schemes.
unit class Red::Driver::SQLite::SchemaReader;
also does Red::SchemaReader;

method sqlite-master { Red::Driver::SQLite::SQLiteMaster }

#use Grammar::Tracer::Compact;
grammar SQL::CreateTable {
    rule  TOP                      { :i <create-table>+ %% ";" }
    rule  create-table             { :i CREATE TABLE <table-name=.name> '(' ~ ')' <column>+ %% [ "," ] }
    token name                     { :i \w+ }
    rule  type                     { :i <name>["(" ~ ")" \d+]? }
    rule  column                   { :i <column-name=.name> <type> <modifier>? <index-mod>? <auto-increment>? }
    rule  auto-increment           { :i "AUTOINCREMENT" }
    proto rule modifier            {*}
    multi rule modifier:<null>     { :i NULL }
    multi rule modifier:<not-null> { :i NOT NULL }
    proto rule index-mod           {*}
    multi rule index-mod:<pk>      { :i PRIMARY KEY }
    multi rule index-mod:<fk>      { :i REFERENCES <table-name=.name> '(' ~ ')' <column-name=.name> }
    multi rule index-mod:<unique>  { :i UNIQUE }
    proto rule index               {*}
    multi rule index:<pk>          { :i PRIMARY KEY '(' ~ ')' <column-name=.name>+ % "," }
    multi rule index:<fk>          { :i FOREIGN KEY <local-column-name=.name> REFERENCES '(' ~ ')' <column-name=.name>+ % "," }
    multi rule index:<unique>      { :i UNIQUE '(' ~ ')' <column-name=.name>+ % "," }
}

class SQL::CreateTable::Action {
    use Red::Cli::Table;
    use Red::Cli::Column;
    method TOP($/)                 { make $<create-table>».made }
    method create-table($/)        { make Red::Cli::Table.new: name => $<table-name>.made, columns => $<column>».made }
    method name($/)                { make ~$/ }
    method type($/)                { make $/.Str.trim}
    method column($/)              {
        make Red::Cli::Column.new(
            $<column-name>.made,
            $<type>.made,
            ($<modifier>.made // True),
            |$<index-mod>.made<pk unique references>
        )
    }
    method auto-increment          { make ( :auto-increment ) }
    method modifier:<null>($/)     { make ( :nullable ) }
    method modifier:<not-null>($/) { make ( :!nullable ) }
    method index-mod:<pk>($/)      { make ( :pk ) }
    method index-mod:<fk>($/)      { make ( :references( %( table => $<table-name>.made, column => $<column-name>.made ) ) ) }
    method index-mod:<unique>($/)  { make ( :unique ) }
    method index:<pk>($/)          { ... }
    method index:<fk>($/)          { ... }
    method index:<unique>($/)      { ... }
}

method tables-names       { self.sqlite-master.tables.map: *.name }
method indexes-of($table) { self.sqlite-master.find-table($table).indexes }
method table-definition($table) {
    my $sql = self.sqlite-master.find-table($table).sql;
    my $list = self.table-definition-from-create-table($sql);
    return unless $list;
    $list.head
}
multi method table-definition-from-create-table(Str:D $sql) {
    SQL::CreateTable.parse($sql, :actions(SQL::CreateTable::Action)).made;
}

multi method table-definition-from-create-table(Any:U) {
    Red::Cli::Table.new
}


