#!/usr/bin/perl

# TODO remove all use statements before deployment.
#use strict;
#use warnings;
#use Data::Dumper;

my $cf_workdir = '/var/cfengine/delta_reporting/log';
my $classes = "$cf_workdir/classes";
my $promises = "$cf_workdir/promises";
my $final_log = "$cf_workdir/final";
my $timestamp_regex = qr/\ABEGIN (\d{4}-\d{2}-\d{2}\S*)/;
my $outcome = qr/((not)?kept)|repaired/;

my ( $timestamp, $class, $promise_handle, $promiser, $promise_outcome, $promisee );
format FINAL =
@* ;; @* ;; @* ;; @* ;; @* ;; @*
$timestamp, $class, $promise_handle, $promiser, $promise_outcome, $promisee
.

sub readfile {
   my $ts;
	my $file = shift;
	my ( @file, %remove_duplicates);

	open( my $fh, "<", $file) || die "Cannot open $file, $!";
	while (<$fh>){
		chomp;
      if ( m/$timestamp_regex/ ){
         $ts = $1;
         next;
      }
		$remove_duplicates{$_} = '';
	}
	close $fh;

	@file = keys %remove_duplicates;
   unshift( @file, $ts);
	return @file
}

#
# Main matter
# 

open ( FINAL, ">>", $final_log) || die "Cannot open $final_log, $!";

my ( $fh, %record );
open( $fh, "<", $classes ) or die "Cannot open [$classes], [$!]";
while (<$fh>)
{
   chomp;
   if ( m/$timestamp_regex/ )
   {
      $record{timestamp} = $1;
      next;
   }
   elsif ( m/_handle_(\w+)_($outcome)\Z/ )
   {
      $record{$_}{promise_handle}  = $1;
      $record{$_}{promise_outcome} = $2;
   }
   elsif ( m/\A\w+\Z/ )
   {
      $timestamp       = $record{timestamp};
      $class           = $_;
      $promise_handle  = 'empty';
      $promiser        = 'empty';
      $promise_outcome = 'empty';
      $promisee        = 'empty';
      write FINAL;
   }
}
close $fh;

my @promises = readfile( $promises );
my $promises_timestamp = shift @promises;
if ( $record{timestamp} ne $promises_timestamp )
{
   die "Timestamps in [$classes] and [$promises] are not equal";
}

for my $p ( @promises )
{
   # A clean record each time
   undef $timestamp;
   undef $class;
   undef $promise_handle;
   undef $promiser;
   undef $promise_outcome;
   undef $promisee;

   ( $promise_handle, $promiser, $promisee ) = split /\s+;;\s+/, $p;

   my $canonized_promiser;
   ( $canonized_promiser = $promiser ) =~ s/[\W\s]/_/g;

   for my $k ( keys %record )
   {
      if ( $k =~ m/\A${canonized_promiser}_handle_${promise_handle}_$outcome\Z/ )
      {
         $timestamp       = $record{timestamp};
         $class           = $k;
         $promise_outcome = $record{$k}{promise_outcome};
         write FINAL;

         # Remove this record to shorten this loop for the next $p.
         delete $record{$k};
      }
   }
}
close FINAL;

=pod

=head1 SYNOPSIS

The program reads the raw output that CFEngine and EFL create and builds a
final log ready for download and input by Delta Reporting. This is called by
the delta_reporting.cf policy file.

=head1 DEV NOTES

Requirements

1. classes log will be in the format of:

BEGIN 2014-03-17T22:03:56-0400
_var_cache_cfengine__etc_ssh_sshd_config_handle_efl_service_svc_cache_kept
_etc_rsyslog_conf_handle_efl_service_files_config_kept

2. promises log will be in the format of:

BEGIN 2014-03-17T22:03:56-0400
efl_command_commands ;; /bin/echo =lsb_release=$(lsb_release -cs) ;; environment
efl_delete_files_files_isdir ;; /var/tmp ;; Neil Watson

3. Both class and promise files are new on each run. The timestamps should be the same.

4. The final log is appended. CFEngine deletes it.

=cut

