### ----------------------------------------------------
### -- Collection
### -- Licenses: Artistic-2.0
### -- Authors: Richard Hainsworth, aka finanalyst
### -- File: lib/Collection.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
 ~ "\n" ~ $!.backtrace
            }
            # so a plugin should not write directly
            my @asset-files;
            try {
                @asset-files = indir($path, { &closure.($with, %options) });
            }
            if $! {
                note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n" ~ $!.message ~ "\n" ~ $!.backtrace
            }
            move-files(@asset-files, $mode, $mile, $plug, $path, %config<destination>, $with)
        }
        $with.add-plugin($plug,
            :$path,
            :template-raku(%plugin-conf<template-raku>:delete),
            :custom-raku(%plugin-conf<custom-raku>:delete),
            :config(%plugin-conf),
            :!protect-name
        );
    }
    @valids
}
multi sub manage-plugins(Str:D $mile where *eq 'report', :$with,
                         :%config, :$mode,
                         :$collection-info,
                         :$no-status
                         --> Array ) {
    my @valids = plugin-confs(:$mile, :%config, :$mode, :$collection-info);
    my %options = %( :$collection-info, :$no-status, :$mode);
    mkdir "$mode/%config<report-path>" unless "$mode/%config<report-path>".IO.d;
    for @valids -> (:key($plug), :value(%plugin-conf)) {
        my $path = "$mode/plugins/$plug".IO.absolute;
        my $callable = "$path/{ %plugin-conf{$mile} }".IO.absolute;
        my &closure;
        try {
            &closure = indir($path, { EVALFILE $callable });
        }
        if $! {
            note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n" ~ $!.message ~ "\n" ~ $!.backtrace
        }
        # a plugin should only affect the report directly
        # so a plugin should not write directly
        # a plugin may offer more than one file for a report, $resp is a list of pairs
        my $resp;
        try {
            $resp = indir($path, { &closure.(|$with, %options) });
        }
        if $! {
            note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n" ~ $!.message ~ "\n" ~ $!.backtrace
        }
        for $resp.list {
            "$mode/{ %config<report-path> }/{ .key }".IO.spurt( .value )
        }
    }
    @valids
}
multi sub manage-plugins(Str:D $mile where *~~ any(< transfer compilation >),
         :$with,
         :%config, :$mode,
         :$collection-info,
         :$no-status
        --> Array ) {
    my @valids = plugin-confs(:$mile, :%config, :$mode, :$collection-info);
    my %options = %( :$collection-info, :$no-status, :$mode);
    for @valids -> (:key($plug), :value(%plugin-conf)) {
        my $path = "$mode/plugins/$plug".IO.absolute;
        my $callable = "$path/{ %plugin-conf{$mile} }".IO.absolute;
        my &closure;
        try {
            &closure = indir($path, { EVALFILE $callable })
        }
        if $! {
            note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n" ~ $!.message ~ "\n" ~ $!.backtrace
        }
        # so a plugin should not write directly
        my $rv;
        try {
            $rv = indir($path, { &closure.(|$with, %options) });
        }
        if $! {
            note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n" ~ $!.message ~ "\n" ~ $!.backtrace
        }
        move-files($rv, $mode, $mile, $plug, $path, %config<destination>, $with[0])
            if $rv ~~ Positional and $rv[0] ~~ Positional and $rv[0].elems == 3
    }
    @valids
}
sub move-files( @asset-files, $mode, $mile, $plug, $path, $destination, $pp ) {
    for @asset-files -> ($to, $other-plug, $file) {
        # copy the files returned - the use case for this is css and script files to be
        # served with html files. The sub-directory paths are needed local to the output files
        # they will be named in the templates provided by the plugins
        # the simplest case is when a plugin asks for a plugin from its own
        # directory. But there is also the case of moving files from other
        # directories. How to do this securely? We can allow transfers from a plugin directory
        # so the plugin-data space will contain a path for each registered plugin.
        # consequently, we have a three element copy
        my $from;
        if $other-plug eq MYSELF {
            $from = "$path/$file";
        }
        else {
            my $config = $pp.get-data($other-plug);
            # returns Nil if no data
            unless $config {
                note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n"
                    ~ "｢$other-plug｣ is not registered as a plugin in ProcessedPod instance"
                    ~ "\nProcessedPod data keys are: " ~ $pp.plugin-datakeys.join(', ');
                next
                }
            $from = $config<path> ~ '/' ~ $file
        }
        my $to-path = "$mode/$destination/$to".IO;
        mkdir($to-path.dirname) unless $to-path.dirname.IO.d;
        unless $from.IO ~~ :e & :f {
            note "ERROR caught in ｢$plug｣ at milestone ｢$mile｣:\n"
                ~ "｢$from｣ is not a valid file. Skipping.";
            next
            }
        $from.IO.copy($to-path);
    }
}

sub milestone($mile, :$with, :@dump-at = (), :$collection-info, :$no-status,
              :%config = {}, :$mode = '',
              :@plugins-used,
              Bool :$call-plugins
              --> Array ) {
    if $mile ~~ /:i Mode/ {
        @plugins-used.append: %( $mile => [:message('No plugins defined'), ] );
    }
    elsif $call-plugins {
        @plugins-used.append:
        %( $mile => manage-plugins($mile.lc, :$with, :%config, :$mode, :$collection-info, :$no-status))
    }
    else {
        @plugins-used.append: %( $mile => [ :message('Plugin calls cancelled'), ] )
    }
    if $mile.lc ~~ any(|@dump-at) {
        my $rv = '';
        for $with.list -> $ds {
            $rv ~= ($ds.raku ~ "\n\n")
        }
        $rv ~= @plugins-used.raku;
        "dumped-{ $mode ?? $mode !! 'mode-unknown' }-at-{ $mile.lc }\.txt".IO.spurt($rv);
    }
    say "Starting ｢$mile｣ milestone" if $collection-info;
    [ $with, @plugins-used ];
}


