use Red::AST;
use Red::Driver::CacheWithStrKey;
use Red::Driver::CacheInvalidateOnWrite;

unit class Red::Driver::Cache::Memory does Red::Driver::CacheWithStrKey does Red::Driver::CacheInvalidateOnWrite;

has %!cache;
has %!cache-to-table;
has %!expires;
has @!times;
has Int $.ttl = 10;

submethod TWEAK(|) {
    start react whenever Supply.interval: 1 {
        CATCH {
            default {
                .say
            }
        }
        my $time = self!time;
        while (@!times.head // Inf) <= $time { #>
            my $t = @!times.shift;
            for |(%!expires{$t}:delete) -> $key {
                next without $key;
                note "remove: $key" if $*RED-CACHE-DEBUG;
                %!cache{$key}:delete
            }
        }
    }
}

method !time { now.Int }

multi method get-from-cache(Str $key, :$ast)  {
    note "get-from-cache: $key" if $*RED-CACHE-DEBUG;
    %!cache{$key}
}

multi method set-on-cache(Str:D $key, @data, :$ast) {
    note "set-on-cache: $key" if $*RED-CACHE-DEBUG;
    %!cache{$key} = @data;
    for $ast.tables -> $table {
        note "save on $table.^table(): $key" if $*RED-CACHE-DEBUG;
        %!cache-to-table.push: $table.^table, $key
    }

    my $time = self!time + $!ttl;
    %!expires.push: $time => $key;
    @!times.push: $time;
}

multi method invalidate(Str :$table, *% where ! .elems) {
    with %!cache-to-table{ $table }:delete {
        for .<> // [] -> $key {
            note "invalidate $table: $key" if $*RED-CACHE-DEBUG;
            %!cache{ $key }:delete
        }
    }
}
#multi method invalidate(Str :$table, *%columns) { say "invalidate $table, { %columns.kv.map: { $^a => $^b.value } }" }