#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Long ();
use File::Temp ();

my $init = 0;
my $status = 0;
my $config_file;
my $default_config_file = '/etc/conghist.conf';
my $rpm_qa_dir;
my @dirs;

my $result = Getopt::Long::GetOptions(
	'config=s' => \$config_file,
	'Config=s' => \$config_file,
	'init' => \$init,
	'status' => \$status,
	'dir=s' => \@dirs,
	'rpm-qa-dir=s' => \$rpm_qa_dir,
);

my %config = ();
while (1) {
	local *CONF;
	if (defined $config_file and not $config_file =~ /^\s*$/) {
		open CONF, '<:bytes', $config_file
			or die "Error reading config file [$config_file]: $!.\n";
	} else {
		open CONF, '<:bytes', $default_config_file
			or last;
	}

	my %valid_directives = (
		dir => 'multiple',
		'rpm-qa-dir' => 'single',
	);

	while (<CONF>) {
		if (/^\s*#/) {
			next;
		}
		if (/^\s*$/) {
			next;
		}
		chomp;
		my ($key, $value) = /^\s*(\S+?)\s*=\s*(.*?)\s*$/;
		if (not defined $key) {
			close CONF;
			die "Error config line [$_] at [$config_file:$.]\n";
		}
		if (not defined $valid_directives{$key}) {
			close CONF;
			die "Unknown directive [$key] at [$config_file:$.]\n";
		}
		if (not defined $value) {
			next;
		}
		if ($valid_directives{$key} eq 'multiple') {
			push @{ $config{$key} }, $value;
		} else {
			$config{$key} = $value;
		}
	}

	close CONF;
	last;
}

if (not @dirs) {
	if (not defined $config{dir}) {
		die "No directories to work on specified.\n";
	}
	@dirs = @{ $config{dir} };
}
if (not defined $rpm_qa_dir) {
	$rpm_qa_dir = $config{'rpm-qa-dir'};
}
if (defined $rpm_qa_dir) {
	$rpm_qa_dir = glob($rpm_qa_dir);
}
for (my $i = 0; $i < @dirs; $i++) {
	if (not $dirs[$i] =~ m!^/!) {
		warn "Absolute directory path needed, got [$dirs[$i]]\n";
		splice @dirs, $i--, 1;
		next;
	}
	my @d = glob($dirs[$i]);
	if (not @d) {
		warn "Directory [$dirs[$i]] not found\n";
	}
	splice @dirs, $i--, 1;
	for my $dd (@d) {
		$dd =~ s!(.)/+$!$1!;
		if (not -d $dd) {
			warn "It seems [$dd] is not directory\n";
			next;
		}
		splice @dirs, ++$i, 0, $dd;
	}
}

if (not @dirs) {
	die "No directories to work on specified.\n";
}

# print "$0: working on [@dirs]\n";

for my $d (@dirs) {
	chdir $d or do {
		warn "$d: failed to chdir to [$d]: $!\n";
		next;
	};
	my $msg = 'Automatic commit.';
	if (not -d "$d/.git") {
		if ($status) {
			print "$d: not initialized\n";
			next;
		} elsif (not $init) {
			warn "$d: not initialized\n";
			next;
		}
		my $output = `git init 2>&1`;
		if ($? != 0) {
			warn "$d: initializing repository failed:\n$output";
			next;
		}
		$msg = 'Initial commit.';
	}
	# Force the directory to be only accessible by the owner,
	# to prevent information leak.
	chmod 0700, '.git';
	if (defined $rpm_qa_dir
		and length($rpm_qa_dir) > 0
		and substr($rpm_qa_dir . '/', 0, length($d) + 1)
			eq ($d . '/')) {
		if (! -d $rpm_qa_dir) {
			mkdir $rpm_qa_dir or do {
				warn "$d: failed to create rpm-qa directory [$rpm_qa_dir]: $!\n";
				next;
			};
		}
		my $rpm_qa_file = "$rpm_qa_dir/rpm-qa";
		my $rpm_qa_old_content;
		if (-f $rpm_qa_file) {
			local *FILE;
			if (open FILE, '<:bytes', $rpm_qa_file) {
				local $/ = undef;
				$rpm_qa_old_content = <FILE>;
				close FILE;
			}
		}
		my $rpm_qa_content = join '',
			sort `rpm -qa --qf '%{name}-%{version}-%{release}.%{arch}\n'`;
		if ($? != 0) {
			warn "$d: failed to run rpm -qa for [$rpm_qa_file]\n";
			next;
		}
		if (not defined $rpm_qa_old_content
			or $rpm_qa_old_content ne $rpm_qa_content) {
			if ($status) {
				print "$rpm_qa_dir: rpm -qa changed\n";
			} else {
				my $tmp = new File::Temp(
					DIR => $rpm_qa_dir,
					TEMPLATE => ".rpm-qa.$$.XXXXX",
					UNLINK => 1,
				) or do {
					warn "$d: failed to create temporary file in [$rpm_qa_dir] for rpm -qa output\n";
					next;
				};
				binmode $tmp, ':bytes';
				print $tmp $rpm_qa_content;
				close $tmp;
				rename $tmp->filename, $rpm_qa_file
					or do {
					warn "$d: failed to rename [@{[ $tmp->filename ]}] to [$rpm_qa_file]: $!\n";
					next;
				};
			}
		}
	}
	system 'git rm -f -r --cached . > /dev/null 2>&1 ; git add -v -f . > /dev/null';
	my $output = `git status`;
	if ($status) {
		if ($? == 0) {
			print "$d: changes exist\n";
		}
		next;
	}
	if ($? == 0) {
		$output = `git commit -m '$msg' 2>&1`;
		if ($? != 0) {
			warn "$d: failed to commit changed:\n$output";
		}
	} elsif ($? >> 8 == 1) {
		# nothing changed
	} else {
		warn "$d: [$?]: failed to gather changes:\n$output";
	}
}

