#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
use Data::Dumper;

# Set the target timestamp
#my $target_date_str = $ARGV[0];
my $target_date_str = shift or die "Usage: $0 <'%Y-%m-%d %H:%M:%S' time stamp>";
my $target_time = Time::Piece->strptime($target_date_str, '%Y-%m-%d %H:%M:%S');
my $CONFIG_FILE = '/etc/grommunio-pkg-bits/setup-deps.conf';

sub get_rpms_updated () {
    local $ENV{LC_TIME} = 'POSIX';
    my @packages = ();

    # Open rpm -qa --last to get packages sorted by installation time [2]
    open(my $rpm_fh, "-|", "rpm -qa --qf '%{NAME} %{INSTALLTIME:date}\\n' --last") or die "$0: Cannot run rpm command: $!\n";

    while (my $line = <$rpm_fh>) {
        chomp($line);
      
        # Example format: name-version-release Day Mon DD HH:MM:SS YYYY
        if ($line =~ /^(.*?)\s+(\w{3}\s+\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})$/) {

            my $pkg  = $1;
            my $date = $2;
           
            # Parse the installation date
            my $pkg_time = Time::Piece->strptime($date, '%a %b %d %H:%M:%S %Y');
        
            if ($pkg_time >= $target_time) {
                # Print if the package was installed after the target time
                if ($pkg =~ /^(.*?)-[^-]+?-[^-]+?\.[^.]+?$/) {
                    my $short_name = $1;
                    push @packages, $short_name;
                }
            }
        }
    }
    close($rpm_fh);

    return @packages;
}

sub get_deps_config () {
    my %config_deps = ();

    open(my $fh, '<', $CONFIG_FILE) or die "Could not open '$CONFIG_FILE': $!\n";
    while (my $line = <$fh>) {
        chomp($line);
        $line =~ s/#.*//;
        $line =~ s/^\s+//;
        $line =~ s/\s+$//;
        next unless length($line);
        my ($key, $value) = split(/\s*:\s*/, $line, 2);
        my @value = split ' ', $value;
        #push @value, $key;
        $config_deps{$key} = \@value;
    }
    return \%config_deps;
}

### Core Resolution Logic - generated by Google Gemini using this problem
# definition:
#
# perl programming: given a hash of dependent packages and their immediate
# dependent packages, and an unordered list of installed packages, expand the
# list of dependencies, generating a penultimate array of installed package
# configuration ordered by need of resolution.
#
#
sub generate_config_order {
    my ($deps_hash, $config_list) = @_;

    # Quick-lookup map for packages that actually need processing
    my %needs_config = map { $_ => 1 } @$config_list;

    my @final_order;   # Accumulator for our final configuration order
    my %visited;       # 0 = unvisited, 1 = visiting, 2 = resolved

    # Recursive Depth-First Search (DFS) worker
    my $visit;
    $visit = sub {
        my ($node) = @_;

        # Rule Check: If it doesn't need configuration, treat it as already resolved
        return unless $needs_config{$node};

        # Circular dependency protection
        if (($visited{$node} // 0) == 1) {
            die "Fatal Error: Circular dependency detected at package '$node'!\n";
        }

        # Skip if this package branch was already handled in a previous loop
        return if (($visited{$node} // 0) == 2);

        # Mark as actively processing
        $visited{$node} = 1;

        # Recursively process immediate dependencies
        if (exists $deps_hash->{$node}) {
            foreach my $dep (@{ $deps_hash->{$node} }) {
                $visit->($dep);
            }
        }

        # Mark as completely resolved
        $visited{$node} = 2;

        # Safely add to our final sequential execution order
        push @final_order, $node;
    };

    # Force traversal starting specifically from our target unconfigured packages
    foreach my $pkg (@$config_list) {
        $visit->($pkg);
    }

    return \@final_order;
}

sub reduce_pkg_updated ($$) {
    my ($config_deps, $pkg_updated) = @_;
    my @pkg_list = ();
    my @deps_keys = keys %$config_deps;
    my %deps_keys = map{ $_ => 1 } @deps_keys;

    FILTER: foreach my $pkg (@$pkg_updated) {
        next FILTER if not $deps_keys{$pkg};
        push @pkg_list, $pkg;
    }

    return generate_config_order($config_deps, \@pkg_list);
}

my $config_deps = get_deps_config();

my @pkg_updated = get_rpms_updated();

my $reduced_pkgs = reduce_pkg_updated($config_deps, \@pkg_updated);

foreach my $pkg ( @$reduced_pkgs ) {
    print "$pkg\n";
}

