#!/usr/bin/env perl
use strict;
use warnings;
use SMT::Agent::Utils;
use XML::XPath;

sub jobhandler
{
  my ($jobtype, $jobid, $args, $verbose) =  @_;

  SMT::Agent::Utils::logger ("jobhandler for patchstatus called", $jobid);
  SMT::Agent::Utils::logger ("patchstatus runs jobid \"$jobid\"", $jobid);

  # check whether this handler can handle requested jobtype
  SMT::Agent::Utils::error ("wrong job handler: \"patchstatus\" cannot handle \"$jobtype\"", $jobid) if ( $jobtype ne "patchstatus" );

  #==  run zypper ==

  my $command = "/usr/bin/zypper";
  my @cmdArgs;
  push (@cmdArgs, "--no-cd");
  push (@cmdArgs, "--non-interactive");			
  push (@cmdArgs, "--xmlout");			
  push (@cmdArgs, "list-patches");
  push (@cmdArgs, "--all");

  ## perform a refresh of repos and services prior to a patchstatus job
  my @REFcmdArgs = ();
  push (@REFcmdArgs, '--no-cd');
  push (@REFcmdArgs, '--non-interactive');
  push (@REFcmdArgs, 'refresh');
  push (@REFcmdArgs, '--service');

  (my $REFret, my $REFstdout, my $REFstderr) = SMT::Agent::Utils::executeCommand ( $command, undef, @REFcmdArgs );

  return (
    stdout => ((defined $REFstdout) && $verbose) ? $REFstdout : '',
    stderr => ((defined $REFstderr) && $verbose) ? $REFstderr : '',
    exitcode => $REFret,
    success => 'false',
    message => 'Refreshing the repositories and services failed. Patch status can not be computed.'
  ) if ( $REFret != 0 );
  # only if refresh succeeds: run zypper with list-updates and parse the output
  (my $retval, my $stdout, my $stderr) = SMT::Agent::Utils::executeCommand($command, undef, @cmdArgs);
  $retval = 'undefined' unless (defined $retval);
  SMT::Agent::Utils::error("Zypper did return with exit code: $retval", $jobid) if ($retval != 0);
  SMT::Agent::Utils::error("Could not get patch information from zypper", $jobid) unless $stdout;

  # parse xml
  my $xpQuery = XML::XPath->new(xml => $stdout);
  SMT::Agent::Utils::error("Could not load XML parser", $jobid) unless $xpQuery;

  my $upSet;
  eval { $upSet = $xpQuery->find('/stream[1]/update-status[1]/update-list[1]/update') };
  SMT::Agent::Utils::error("Zypper xml data is not parsable", $jobid) if ($@);

  my %patches;

  foreach my $node ($upSet->get_nodelist()) {
      my $pname       = $node->getAttribute('name')       || '';
      my $pedition    = $node->getAttribute('edition')    || '';
      my $pstatus     = $node->getAttribute('status')     || '';
      my $pcategory   = $node->getAttribute('category')   || '';
      my $ppkgmanager = $node->getAttribute('pkgmanager') || '';
      if ( (not exists $patches{$pname}) || (not defined $patches{$pname}{edition}) || ($pedition > $patches{$pname}{edition}) )
      {
          $patches{$pname} = { name => $pname,
                               edition => $pedition,
                               status => $pstatus,
                               category => $pcategory,
                               pkgmanager => $ppkgmanager
                             };
      }
  }

  my %PATCHES = ( security => 0, recommended => 0, optional => 0, yast   => 0,
                  document => 0, enhancement => 0, feature  => 0, bugfix => 0, _other => 0 );
  my $pkgMgrCount = 0;

  foreach my $pkey (keys %patches) {
    next unless defined $patches{$pkey}{status};
    next unless defined $patches{$pkey}{category};
    # only count needed patches
    next unless ($patches{$pkey}{status} =~ /^needed$/);

    # count patches by category, as categories are dynamic all unknown ones are "_other"
    my $cat = $patches{$pkey}{category};
    $PATCHES{ (exists $PATCHES{$cat} && defined $PATCHES{$cat}) ? $cat : '_other' }++;
    $pkgMgrCount++ if ((defined $patches{$pkey}{pkgmanager}) && ($patches{$pkey}{pkgmanager} =~ /^true$/));

  }

  # group categories
  my $recommendedCount = $PATCHES{recommended} + $PATCHES{bugfix};
  my $securityCount = $PATCHES{security};
  my $optionalCount = $PATCHES{optional} + $PATCHES{enhancement} + $PATCHES{feature} + $PATCHES{document} + $PATCHES{_other};
  my $msgComment = "PackageManager=$pkgMgrCount Security=$securityCount Recommended=$recommendedCount (Bugfix=".$PATCHES{bugfix}.") Optional=$optionalCount (Enhancement=".$PATCHES{enhancement}." Feature=".$PATCHES{feature}." Document=".$PATCHES{document}." Other=".$PATCHES{_other}.")";
  my $customStdout = "Patch Information\n".
                     "------------------\n".
                     "PackageManager: $pkgMgrCount\n".
                     "Security:       $securityCount\n".
                     "Recommended:    $recommendedCount\n".
                     "- Bugfix:       ".$PATCHES{bugfix}."\n".
                     "Optional:       $optionalCount\n".
                     "- Enhancement:  ".$PATCHES{enhancement}."\n".
                     "- Feature:      ".$PATCHES{feature}."\n".
                     "- Document:     ".$PATCHES{document}."\n".
                     "- Other:        ".$PATCHES{_other}."\n";

  return (
    stdout => ((defined $customStdout) && $verbose ) ? $customStdout : '',
    stderr => ((defined $stderr) && $verbose ) ? $stderr : '',
    exitcode => $retval,
    success => "true",
    message => "$pkgMgrCount:$securityCount:$recommendedCount:$optionalCount # ".$msgComment
  );

}

SMT::Agent::Utils::logger ("successfully loaded handler for jobtype \"patchstatus\"");

1;

