package System::Introspector::Probe::Packages::Apt;
use Moo;
use File::Basename;

use System::Introspector::Util qw(
    handle_from_command
    transform_exceptions
    output_from_file
    files_from_dir
);

has apt_lists_dir => (is => 'ro', builder => 1);
has apt_update_after => (is => 'ro', default => sub { 86400 });
has apt_update => (is => 'ro');

has apt_sources => (is => 'ro', builder => 1);
has apt_sources_dir => (is => 'ro', builder => 1);

sub _build_apt_lists_dir   { '/var/lib/apt/lists' }
sub _build_apt_sources     { '/etc/apt/sources.list' }
sub _build_apt_sources_dir { '/etc/apt/sources.list.d' }

sub gather {
    my ($self) = @_;
    return {
        update => {
            last => $self->_last_apt_update,
            run => transform_exceptions {
                return { result => $self->_check_apt_state };
            },
        },
        installed => transform_exceptions {
            return { packages => $self->_gather_installed };
        },
        upgradable => transform_exceptions {
            return { actions => $self->_gather_upgradable };
        },
        sources => transform_exceptions {
            return { config => $self->_gather_sources };
        },
    };
}

sub _last_apt_update {
    my ($self) = @_;
    return scalar( (stat($self->apt_lists_dir))[9] );
}

sub _check_apt_state {
    my ($self) = @_;
    return 'disabled' unless $self->apt_update;
    my $threshold   = $self->apt_update_after;
    my $last_change = $self->_last_apt_update;
    return 'no'if ($last_change + $threshold) > time;
    handle_from_command 'apt-get update';
    return 'yes';
}

sub _open_dpkg_query_pipe {
    my ($self) = @_;
    return handle_from_command 'dpkg-query --show';
}

sub _open_apt_get_upgrade_simulation_pipe {
    my ($self) = @_;
    return handle_from_command 'apt-get -s upgrade';
}

sub _gather_sources {
    my ($self) = @_;
    my $sources_dir = $self->apt_sources_dir;
    return {
        'sources_list' => $self->_fetch_source_list($self->apt_sources),
        'sources_list_dir' => (-e $sources_dir) ? transform_exceptions {
            return +{ files => +{ map {
                ($_, $self->_fetch_source_list("$sources_dir/$_"));
            } files_from_dir $sources_dir } };
        } : {},
    };
}

sub _fetch_source_list {
    my ($self, $file) = @_;
    return transform_exceptions {
        return {
            file_name => $file,
            body => scalar(output_from_file $file),
        };
    };
}

sub _gather_upgradable {
    my ($self) = @_;
    my $pipe = $self->_open_apt_get_upgrade_simulation_pipe;
    my %action;
    while (defined( my $line = <$pipe> )) {
        chomp $line;
        if ($line =~ m{^(inst|remv)\s+(\S+)\s+(.+)$}i) {
            $action{ lc($1) }{ $2 } = $3;
        }
    }
    return \%action;
}

sub _gather_installed {
    my ($self) = @_;
    my $pipe = $self->_open_dpkg_query_pipe;
    my %package;
    while (defined( my $line = <$pipe> )) {
        chomp $line;
        my ($package, $version) = split m{\s+}, $line;
        $package{ $package } = {
            version => $version,
        };
    }
    return \%package;
}

1;

__END__

=head1 NAME

System::Introspector::Packages::Apt - Gather APT package status

=head1 DESCRIPTION

Uses C<dpkg-query> to list all installed packages.

=head1 SEE ALSO

=over

=item L<System::Introspector>

=back

=cut
