#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use JSON::XS;
use POSIX qw(strftime);

our $SERVICE_NAME = "insight_mu";
our $PROTOCOL_VERSION = "3.0.0";
my $SCRIPT_VERSION = "3.0.2";

perunServicesInit::init;
my $DIRECTORY = perunServicesInit::getDirectory;
# get only valid members
my $data = perunServicesInit::getHashedDataWithGroups;

# CONSTANTS
our $A_USER_ID;                  *A_USER_ID =                  \'urn:perun:user:attribute-def:core:id';
our $A_USER_FIRSTNAME;           *A_USER_FIRSTNAME =           \'urn:perun:user:attribute-def:core:firstName';
our $A_USER_LASTNAME;            *A_USER_LASTNAME =            \'urn:perun:user:attribute-def:core:lastName';
our $A_USER_UCO;                 *A_USER_UCO =                 \'urn:perun:user:attribute-def:def:login-namespace:mu';
our $A_USER_EMAIL;               *A_USER_EMAIL =               \'urn:perun:user:attribute-def:def:preferredMail';
our $A_GROUP_ID;                 *A_GROUP_ID =                 \'urn:perun:group:attribute-def:core:id';
our $A_GROUP_DESC;               *A_GROUP_DESC =               \'urn:perun:group:attribute-def:core:description';
our $A_GROUP_NAME;               *A_GROUP_NAME =               \'urn:perun:group:attribute-def:core:name';
our $A_GROUP_INETCISPR;          *A_GROUP_INETCISPR =          \'urn:perun:group:attribute-def:def:inetCispr';
our $A_GROUP_INETVAZPR;          *A_GROUP_INETVAZPR =          \'urn:perun:group:attribute-def:def:inetVazpr';
our $A_GROUP_O365_EMAIL_ADDRESS; *A_GROUP_O365_EMAIL_ADDRESS = \'urn:perun:group:attribute-def:def:o365EmailAddresses:o365mu';
our $A_GROUP_O365_NAME;          *A_GROUP_O365_NAME =          \'urn:perun:group:attribute-def:virt:adDisplayName:o365mu';
our $A_MEMBER_STATUS;            *A_MEMBER_STATUS =            \'urn:perun:member:attribute-def:core:status';

our $STATUS_VALID;               *STATUS_VALID =               \'VALID';
# import time
(my $ss, my $mm, my $hh, my $d, my $mon, my $yr) = gmtime();
my $ampm = "AM";
if ($hh > 12) {
	$ampm = "PM";
}
my $writeTime = strftime ("%d/%m/%Y %I:%M ", gmtime) . $ampm;

my $memberDataById;
my $groupDataById;
my $workplaceDataById;
foreach my $resourceId ($data->getResourceIds()) {
	foreach my $groupId ($data->getGroupIdsForResource(resource => $resourceId)) {
		my $groupName = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_NAME);
		my $groupDesc = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_DESC);
		my $groupCISPR = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_INETCISPR);
		my $groupVAZPR = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_INETVAZPR);
		my $emailsArray = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_O365_EMAIL_ADDRESS);
		my @o365emails = $emailsArray ? @$emailsArray : ();
		my $o365groupName = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_O365_NAME);
		my %members;
		for my $memberId ($data->getMemberIdsForResourceAndGroup(resource => $resourceId, group => $groupId)) {
			if ($data->getMemberAttributeValue(member => $memberId, attrName => $A_MEMBER_STATUS) ne $STATUS_VALID) {
				next;
			}
			my $uco = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_UCO );
			# in case one person (one uco) is a member multiple times
			unless (exists($members{$uco})){
				$members{$uco} = 1;
			}
			# no need to create new person object if user already processed
			next if ($memberDataById->{$uco});
			my $userId = $data->getUserIdForMember (member => $memberId);
			my $firstName = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_FIRSTNAME );
			my $lastName = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_LASTNAME );
			my $email = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_EMAIL );
			my $person = {
				Name                => $firstName . " " . $lastName . " (" . $uco . ")",
				UCO                 => $uco,
				ExternalObjectID    => $userId,
				FirstName           => $firstName,
				LastName            => $lastName,
				EmailAddress        => $email,
				Expired             => "FALSE",
				LastImportWriteTime => $writeTime
			};
			$memberDataById->{$uco} = $person;
		}
		my @membersList = sort { $a <=> $b } keys %members;
		# decide whether group or workplace depending on defined VAZPR attribute
		unless ( defined $groupVAZPR ) {
			my $group = {
				Name                => $groupName . " (" . $groupId . ")",
				Description         => $groupDesc,
				ExternalObjectID    => $groupId,
				EmailAddress        => "",
				Expired             => "FALSE",
				LastImportWriteTime => $writeTime,
				Members             => \@membersList
			};
			$groupDataById->{$groupId} = $group;
		} else {
			if ( defined $o365groupName && ! $o365groupName eq '' ) {
				$groupName = $o365groupName;
			}
			my $group = {
				Name                => $groupName . " (" . $groupCISPR . "/" . $groupId . ")",
				Description         => $groupDesc,
				InetVAZPR           => $groupVAZPR,
				InetCISPR           => $groupCISPR,
				ExternalObjectID    => $groupId,
				EmailAddress        => $o365emails[0],
				Expired             => "FALSE",
				LastImportWriteTime => $writeTime,
				Members             => \@membersList
			};
			$workplaceDataById->{$groupId} = $group;
		}
	}
}

my @groupValues = values(%$groupDataById);
my @workplaceValues = values(%$workplaceDataById);
my @personValues = values(%$memberDataById);

# sort persons by UCO
@personValues = sort { $a->{'UCO'} <=> $b->{'UCO'} } @personValues;
# sort groups by externalObjectId = Group ID
@groupValues = sort { $a->{'ExternalObjectID'} <=> $b->{'ExternalObjectID'} } @groupValues;
# sort workplaces by externalObjectId = Group ID
@workplaceValues = sort { $a->{'ExternalObjectID'} <=> $b->{'ExternalObjectID'} } @workplaceValues;


# get data in desired format
my $groupData = {
	data   => {Contact => [{Group => [{PerunGroup => \@groupValues}]}]}
};
my $workplaceData = {
	data   => {Contact => [{Group => [{PerunWorkplace => \@workplaceValues}]}]}
};
my $personData = {
	data   => {Contact => [{Person => \@personValues }]}
};

# process files
my $dataDirectory= $DIRECTORY . "/Data";
if ( ! -e $dataDirectory) {
	mkdir $dataDirectory or die "Cannot create directory $dataDirectory!";
}

my $groupFile = "$dataDirectory/" . "group.json";
my $workplaceFile = "$dataDirectory/" . "workplace.json";
my $personFile = "$dataDirectory/" . "person.json";
open FILE_GROUP, ">$groupFile" or die "Cannot open $groupFile: $! \n";
print FILE_GROUP JSON::XS->new->utf8->pretty->canonical->encode($groupData), "\n";
close(FILE_GROUP) or die "Cannot close $groupFile: $! \n";

open FILE_WORKPLACE, ">$workplaceFile" or die "Cannot open $workplaceFile: $! \n";
print FILE_WORKPLACE JSON::XS->new->utf8->pretty->canonical->encode($workplaceData), "\n";
close(FILE_WORKPLACE) or die "Cannot close $workplaceFile: $! \n";

open FILE_PERSON, ">$personFile" or die "Cannot open $personFile: $! \n";
print FILE_PERSON JSON::XS->new->utf8->pretty->canonical->encode($personData), "\n";
close(FILE_PERSON) or die "Cannot close $personFile: $! \n";

perunServicesInit::finalize;
