#!/usr/bin/perl
#  $Id: peed 20359 2010-10-06 12:49:16Z atsibulnik $
# -----------------------------------------------------------------------------
#  Event Integrity Maintain
#
#  Sript performs event integrity check (if ran with no options)
#  or (if option "-e" is provided) deletes from tables "event", "eventproperties"
#  and "eventwitness" all records that violate following logical constraints:
#    - objid in "event" and "eventwitness" references to valid existing object;
#    - workflow/state in "event" table references to valid pair workflow/eventstate
#      in "eventstate" table
#    - eventid in "eventwitness" and "eventproperty" references to valid event.
#
# -----------------------------------------------------------------------------
#  Author: Alex Titov
#  QA by:  Christopher C Gettings
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------

use strict;
use Getopt::Std;
use Time::HiRes;
use SKM::DB;
use Log::Log4perl "get_logger";
require "$ENV{APL}/common/bin/logger.engine";



my $log = get_logger('NEXTCAM');

my $dbh=DBMaster({PrintError=>1, 'RaiseError'=>1, 'FetchHashKeyName'=>'NAME_uc'},'ShowErrorStatement'=>1) ||
 $log->logdie($DBI::errstr);



sub check
{
	my $checkResult = 0; # OK

	eval {
		my $sth = $dbh->prepare('SELECT e.eventid FROM event e
 WHERE e.objid NOT IN (SELECT o.obj FROM _objs o)
    OR (e.workflow, e.state) NOT IN (SELECT s.workflow, s.eventstate FROM eventstate s)
 LIMIT 1;');
		$sth->execute;
		my $hash_ref = $sth->fetchrow_hashref;
		if (defined($hash_ref)) {
			$checkResult = 1;
		}
	};
	if ($@) {
		$log->error($@);
		$checkResult = 2;
	}

	return $checkResult;
}


sub maintenance
{
	my $startPeed = [Time::HiRes::gettimeofday()];
	$log->info('starting event integrity maintenance');

	my @stmtSchedule = (
		{ desc => "Creating temp table",
		  stmt => "CREATE TEMPORARY TABLE tmp_eventid_tbl (eventid INTEGER); CREATE UNIQUE INDEX tmp_eventid_tbl_pkey ON tmp_eventid_tbl (eventid);" },
	    
		{ desc => "Start transaction",
		  stmt => "START TRANSACTION;" },
	    
		{ desc => "Look for invalid objids in the Event table",
		  stmt => "INSERT INTO tmp_eventid_tbl
 SELECT e.eventid
   FROM event e
  WHERE e.objid NOT IN (SELECT o.obj FROM _objs o)
     OR (e.state, e.workflow) NOT IN (SELECT s.eventstate, s.workflow FROM eventstate s);" },
	    
		{ desc => "Remove events with invalid objid",
		  stmt => "DELETE FROM event USING tmp_eventid_tbl t WHERE event.eventid = t.eventid;" },
	    
		{ desc => "Clean temporary table",
		  stmt => "TRUNCATE tmp_eventid_tbl;" },
	    
		{ desc => "Look for invalid event properties",
		  stmt => "INSERT INTO tmp_eventid_tbl SELECT DISTINCT p.eventid FROM eventproperty p LEFT JOIN event e ON e.eventid=p.eventid WHERE e.eventid IS NULL;" },
	    
		{ desc => "Remove invalid event properties",
		  stmt => "DELETE FROM eventproperty USING tmp_eventid_tbl t WHERE eventproperty.eventid = t.eventid;" },
	    
		{ desc => "Clean temporary table",
		  stmt => "TRUNCATE tmp_eventid_tbl;" },
	    
		{ desc => "Look for invalid event witnesses",
		  stmt => "INSERT INTO tmp_eventid_tbl SELECT DISTINCT w.eventid FROM eventwitness w LEFT JOIN event e ON e.eventid=w.eventid WHERE e.eventid IS NULL;" },
	    
		{ desc => "Remove invalid witnesses",
		  stmt => "DELETE FROM eventwitness USING tmp_eventid_tbl t WHERE eventwitness.eventid = t.eventid;" },
	    
		{ desc => "Remove witnesses with invalid objid",
		  stmt => "DELETE FROM eventwitness WHERE objid NOT IN (SELECT DISTINCT o.obj FROM _objs o);" },
	    
		{ desc => "Complete transaction",
		  stmt => "COMMIT;" },
	);


	my $elapsed;
	foreach my $i (0 .. $#stmtSchedule ) {
		$log->info("Phase $i of $#stmtSchedule: $stmtSchedule[$i]{desc}");
		$log->debug("Stmt=$stmtSchedule[$i]{stmt}");

		my $start = [Time::HiRes::gettimeofday()];
		eval {
			my $result = $dbh->do($stmtSchedule[$i]{stmt});

			$elapsed = Time::HiRes::tv_interval($start);
			my $msg = sprintf "Phase %d completed. Return: %s, elapsed time: %.3f sec",
				$i, ($result eq '0E0') ? 'OK' : $result ,$elapsed;
			$log->info($msg);
		};
		if ($@) {
			$log->error($@);
			last;
		}
	}
	$elapsed = Time::HiRes::tv_interval($startPeed);
	$log->info(sprintf("Maintenace completed. Total elapsed time: %.3f sec", $elapsed));
}


# Parse cmd line
my %args = ();
getopts("he", \%args);

if (defined($args{h}))
{
 	$log->info('Usage: event_integrity_check [-h] [-e]');
 	$log->info(' -h         : this help message');
 	$log->info(' -e         : run integrity mantenance');
 	$log->info(' no option  : check event integrity');

} else {
	if (defined($args{e}))
	{
		maintenance();
		$log->info("Strart integrity check");
	}

	my $checkResult = check();
	my $checkResultTxt = 'UNKNOWN';

	if ($checkResult == 0) {
		$checkResultTxt = 'CONSISTENT';
	 	$log->info($checkResultTxt);
    } elsif ($checkResult == 1) {
		$checkResultTxt = 'INCONSISTENT';
	 	$log->warn('Event integrity is broken. Run this script with option "-e"');
	}

	# Write result to the file
	if (open(my $fh, ">$ENV{APL}/var/elog/event_integrity")) {
		print $fh "EVENT_INTEGRITY=$checkResultTxt\n"; 
	    close ($fh);
	} else {
	 	$log->error("Failed to write intergity status into $ENV{APL}/var/elog/event_integrity : $!");
	}

	exit $checkResult;
}

