#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use Perun::Agent;
use Perun::GroupsAgent;
use open qw/:std :utf8/;

local $::SERVICE_NAME = "drupal_elixir";
local $::PROTOCOL_VERSION = "3.1.0";
my $SCRIPT_VERSION = "3.1.2";

perunServicesInit::init;
my $DIRECTORY = perunServicesInit::getDirectory;
my $data = perunServicesInit::getHashedDataWithGroups;

#forward declaration
sub processUsers;
sub processGroups;
sub processMemberships;

#Constants
our $A_USER_ID;                   *A_USER_ID =               \'urn:perun:user:attribute-def:core:id';
our $A_USER_LOGIN;                *A_USER_LOGIN =            \'urn:perun:user:attribute-def:def:login-namespace:elixir';
our $A_USER_STATUS;               *A_USER_STATUS =           \'urn:perun:member:attribute-def:core:status';
our $A_MEMBER_IS_SUSPENDED;       *A_MEMBER_IS_SUSPENDED =   \'urn:perun:member:attribute-def:virt:isSuspended';
our $A_USER_EMAIL;                *A_USER_EMAIL =            \'urn:perun:user:attribute-def:def:preferredMail';
our $A_USER_EPPNS;                *A_USER_EPPNS =            \'urn:perun:user:attribute-def:virt:eduPersonPrincipalNames';
our $A_USER_D_NAME;               *A_USER_D_NAME =           \'urn:perun:user:attribute-def:core:displayName';
our $A_GROUP_ID;                  *A_GROUP_ID =              \'urn:perun:group:attribute-def:core:id';
our $A_GROUP_NAME;                *A_GROUP_NAME =            \'urn:perun:group:attribute-def:core:name';
our $A_PARENT_GROUP_ID;           *A_PARENT_GROUP_ID =       \'urn:perun:group:attribute-def:core:parentGroupId';
our $A_GROUP_TYPE;                *A_GROUP_TYPE =            \'urn:perun:group_resource:attribute-def:def:drupalGroupType';
our $A_M_TYPE_MEMBER;             *A_M_TYPE_MEMBER =         \'member';
our $A_M_TYPE_ADMIN;              *A_M_TYPE_ADMIN =          \'admin';

our $STATUS_VALID;                *STATUS_VALID =            \'VALID';
our $STATUS_EXPIRED;              *STATUS_EXPIRED =          \'EXPIRED';
our $STATUS_SUSPENDED;            *STATUS_SUSPENDED =        \'SUSPENDED';

our $userStruc = {};
our $u_login = {};
our $u_status = {};
our $u_email = {};
our $u_name = {};
our $u_eppn = {};

our $duplicityUserStruc = {};
our $lowerCaseEmailStruc = {};

our $groupStruc = {};
our $g_name = {};
our $g_type = {};
our $g_parent_id = {};

our $membershipStruc = {};

my $fileUsers = $DIRECTORY . "users.csv";
my $fileUsersDuplicities = $DIRECTORY . "users-duplicities.csv";
my $fileGroups = $DIRECTORY . "groups.csv";
my $fileMemberships = $DIRECTORY . "memberships.csv";


my $agent = perunServicesInit->getAgent;
my $groupAgent = $agent->getGroupsAgent;

foreach my $resourceId ( $data->getResourceIds() ) {
	foreach my $groupId ( $data->getGroupIdsForResource( resource => $resourceId ) ) {
		processGroups $groupId, $resourceId;
	}
}

# USERS: user_id login status e-mail display_name
open FILE_USERS,">$fileUsers" or die "Cannot open $fileUsers: $! \n";
foreach my $uid (sort keys %$userStruc) {
	print FILE_USERS "\"$uid\", \"$userStruc->{$uid}->{$u_login}\", \"$userStruc->{$uid}->{$u_status}\", \"$userStruc->{$uid}->{$u_email}\", \"$userStruc->{$uid}->{$u_name}\", \"$userStruc->{$uid}->{$u_eppn}\"","\n";
}
close (FILE_USERS) or die "Cannot close $fileUsers: $! \n";

# USERS-duplicities: $uid login e-mail
open FILE_USERS_DUPLICITIES,">$fileUsersDuplicities" or die "Cannot open $fileUsersDuplicities: $! \n";
foreach my $uid (sort keys %$duplicityUserStruc) {
	print FILE_USERS_DUPLICITIES "UID:\"$uid\", LOGIN:\"$duplicityUserStruc->{$uid}->{$u_login}\", EMAIL:\"$duplicityUserStruc->{$uid}->{$u_email}\"", "\n";
}
close (FILE_USERS_DUPLICITIES) or die "Cannot close $fileUsersDuplicities: $! \n";

# GROUPS: group_id group_name type parent_id
open FILE_GROUPS,">$fileGroups" or die "Cannot open $fileGroups: $! \n";
foreach my $gid (sort keys %$groupStruc) {
	print FILE_GROUPS "\"$gid\", \"$groupStruc->{$gid}->{$g_name}\", \"$groupStruc->{$gid}->{$g_type}\", ";
	if ($groupStruc->{$gid}->{$g_parent_id}) {
		print FILE_GROUPS "\"$groupStruc->{$gid}->{$g_parent_id}\""
	} else {
		print FILE_GROUPS "\"0\"";
	}
	print FILE_GROUPS "\n";
}
close (FILE_GROUPS) or die "Cannot close $fileGroups: $! \n";

# MEMBERSHIPS: user_id group_id membership
open FILE_MEMBERSHIPS,">$fileMemberships" or die "Cannot open $fileMemberships: $! \n";
foreach my $uid (sort keys %$membershipStruc) {
	foreach my $gid (sort keys %{$membershipStruc->{$uid}}){
		# If the user is member and also admin in the group, then store only admin role
		my $membershipType = "";
		foreach my $membershipTypeTmp (sort keys %{$membershipStruc->{$uid}->{$gid}}){
			$membershipType = $membershipTypeTmp;
			if ($membershipTypeTmp eq $A_M_TYPE_ADMIN) {
				last;
			}
		}
		print FILE_MEMBERSHIPS "\"$uid\", \"$gid\", \"$membershipType\"", "\n";
	}
}
close (FILE_MEMBERSHIPS) or die "Cannot close $fileMemberships: $! \n";

perunServicesInit::finalize;


##############################################################################
#   Only subs definitions down there
##############################################################################
## creates structure for users.csv file
sub processUsers {
	my ($groupId, $memberId, $admins_ref) = @_;
	my @admins = @$admins_ref;

	my $uid = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_ID );
	my $login = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_LOGIN );
	my $status = $data->getMemberAttributeValue( member => $memberId, attrName => $A_USER_STATUS );
	if($data->getMemberAttributeValue( member => $memberId, attrName => $A_MEMBER_IS_SUSPENDED )) {
		$status = $STATUS_SUSPENDED;
	}
	my $email = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_EMAIL );
	my $d_name = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_D_NAME );

	# Select right eppn from the list of eppns, TEMPORARILY get google one, then we will use elixir
	my @eppns = @{$data->getUserAttributeValue( member => $memberId, attrName => $A_USER_EPPNS)};


	#select first eppn which ends with '@elixir-europe.org', if there is no one matching select first eppn, if there is no eppn at all use empty string
	my $eppn = (grep /\@elixir-europe.org$/, @eppns)[0] || $eppns[0] || "";

	if(exists $userStruc->{$uid}) {
		my $memberStatus = $userStruc->{$uid}->{$u_status};

		if ($memberStatus eq $STATUS_EXPIRED && $status eq $STATUS_VALID){
			# change from EXPIRED to VALID
			$userStruc->{$uid}->{$u_status} = $status;
		} elsif ($memberStatus eq $STATUS_SUSPENDED && $status eq $STATUS_VALID){
			# change from SUSPENDED to VALID
			$userStruc->{$uid}->{$u_status} = $status;
		} elsif ($memberStatus eq $STATUS_SUSPENDED && $status eq $STATUS_EXPIRED){
			# change from SUSPENDED to EXPIRED
			$userStruc->{$uid}->{$u_status} = $status;
		}
	} else{
		my $lowerCaseEmail = lc($email);
		#duplicity found, create record of duplicity accounts
		if(exists $lowerCaseEmailStruc->{$lowerCaseEmail}) {
			$duplicityUserStruc->{$uid}->{$u_login} = $login;
			$duplicityUserStruc->{$uid}->{$u_email} = $email;
		} else {
			$userStruc->{$uid}->{$u_login} = $login;
			$userStruc->{$uid}->{$u_status} = $status;
			$userStruc->{$uid}->{$u_email} = $email;
			$userStruc->{$uid}->{$u_name} = $d_name;
			$userStruc->{$uid}->{$u_eppn} = $eppn;
			$lowerCaseEmailStruc->{$lowerCaseEmail} = 1;
		}
	}

	if(@admins) {
		foreach my $user (@admins){
			if ($user->getId() == $uid) {
				processMemberships $groupId, $uid, $A_M_TYPE_ADMIN;
			}
		}
	}

	processMemberships $groupId, $uid, $A_M_TYPE_MEMBER;
}

## creates structure for groups.csv file
sub processGroups {
	my ($groupId, $resourceId) = @_;

	if($data->getGroupAttributeValue( group => $groupId, attrName => $A_GROUP_NAME )) {
		my $groupName = $data->getGroupAttributeValue( group => $groupId, attrName => $A_GROUP_NAME ),
		my $groupParentID = $data->getGroupAttributeValue( group => $groupId, attrName => $A_PARENT_GROUP_ID );
		my $groupType;

		if($data->getGroupResourceAttributeValue( group => $groupId, resource=> $resourceId, attrName => $A_GROUP_TYPE )) {
			$groupType = $data->getGroupResourceAttributeValue( group => $groupId, resource=> $resourceId, attrName => $A_GROUP_TYPE );
		} else {
			$groupType = "public";
		}

		unless(exists $groupStruc->{$groupId}) {
			$groupStruc->{$groupId}->{$g_name} = $groupName;
			$groupStruc->{$groupId}->{$g_type} = $groupType;
			$groupStruc->{$groupId}->{$g_parent_id} = $groupParentID;
		}

		my @admins = $groupAgent->getAdmins(group => $groupId);

		for my $memberId ($data->getMemberIdsForResourceAndGroup( resource=> $resourceId, group => $groupId )) {
			processUsers $groupId, $memberId, \@admins;
		}
	}
}

## creates structure for memberships.csv file
sub processMemberships {
	my ($gid, $uid, $membershipType) = @_;

	unless(exists $membershipStruc->{$uid}->{$gid}->{$membershipType}) {
		$membershipStruc->{$uid}->{$gid}->{$membershipType} = {};
	}
}
