package Hydra::Plugin::PathInput;

use strict;
use warnings;
use parent 'Hydra::Plugin';
use POSIX qw(strftime);
use Hydra::Helper::Nix;
use IPC::Run3;

sub supportedInputTypes {
    my ($self, $inputTypes) = @_;
    $inputTypes->{'path'} = 'Local path or URL';
}

sub fetchInput {
    my ($self, $type, $name, $value) = @_;

    return undef if $type ne "path";

    my $uri = $value;

    my $timestamp = time;
    my $sha256;
    my $storePath;

    my $timeout = $self->{config}->{path_input_cache_validity_seconds} // 30;

    # Some simple caching: don't check a path more than once every N seconds.
    (my $cachedInput) = $self->{db}->resultset('CachedPathInputs')->search(
        {srcpath => $uri, lastseen => {">", $timestamp - $timeout}},
        {rows => 1, order_by => "lastseen DESC"});

    if (defined $cachedInput && $MACHINE_LOCAL_STORE->isValidPath($cachedInput->storepath)) {
        $storePath = $cachedInput->storepath;
        $sha256 = $cachedInput->sha256hash;
        $timestamp = $cachedInput->timestamp;
    } else {

        print STDERR "copying input ", $name, " from $uri\n";
        if ( $uri =~ /^\// ) {
            $storePath = addToStore($uri);
        } else {
            # Run nix-prefetch-url with PRINT_PATH=1
            my ($stdout, $stderr);
            local $ENV{PRINT_PATH} = 1;
            run3(['nix-prefetch-url', $uri], \undef, \$stdout, \$stderr);
            die "cannot fetch $uri to the Nix store: $stderr\n" if $? != 0;
            # Get the last line (which is the store path)
            my @output_lines = split /\n/, $stdout;
            $storePath = $output_lines[-1] if @output_lines;
        }
        chomp $storePath;

        $sha256 = ($MACHINE_LOCAL_STORE->queryPathInfo($storePath, 0))[1] or die;

        ($cachedInput) = $self->{db}->resultset('CachedPathInputs')->search(
            {srcpath => $uri, sha256hash => $sha256});

        # Path inputs don't have a natural notion of a "revision", so
        # we simulate it by using the timestamp that we first saw this
        # path have this SHA-256 hash.  So if the contents of the path
        # changes, we get a new "revision", but if it doesn't change
        # (or changes back), we don't get a new "revision".
        if (!defined $cachedInput) {
            $self->{db}->txn_do(sub {
                $self->{db}->resultset('CachedPathInputs')->update_or_create(
                    { srcpath => $uri
                    , timestamp => $timestamp
                    , lastseen => $timestamp
                    , sha256hash => $sha256
                    , storepath => $storePath
                    });
                });
        } else {
            $timestamp = $cachedInput->timestamp;
            $self->{db}->txn_do(sub {
                $cachedInput->update({lastseen => time});
            });
        }
    }

    return
        { uri => $uri
        , storePath => $storePath
        , sha256hash => $sha256
        , revision => (strftime "%Y%m%d%H%M%S", gmtime($timestamp))  . ':' .  $sha256
        };
}

1;
