#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use open qw/:std :utf8/;
use JSON::XS;
use utf8;

local $::SERVICE_NAME = "scim";
local $::PROTOCOL_VERSION = "1.0.0";
my $SCRIPT_VERSION = "1.0.1";

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_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_LOGIN;                *A_USER_LOGIN =            \'urn:perun:user_facility:attribute-def:virt:login';
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_GROUP_PAR_ID;              *A_GROUP_PAR_ID =          \'urn:perun:group:attribute-def:core:parentGroupId';

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

my $userStruc = {};
my $groupStruc = {};
my $membershipStruc = {};

my $userStatus = {};
my $userEmail = {};
my $userDisplayName = {};
my $userLogin = {};

my $fileUsers = $DIRECTORY . "/users.scim";
my $fileGroups = $DIRECTORY . "/groups.scim";

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

# PREPARE USERSDATA TO JSON
my @users;
foreach my $uid (sort keys %$userStruc) {
	my $user = {};
    $user->{"id"} = $uid;
    $user->{"displayName"} = $userStruc->{$uid}->{$userDisplayName};
    $user->{"status"} = $userStruc->{$uid}->{$userStatus};
    $user->{"mail"} = $userStruc->{$uid}->{$userEmail};
    $user->{"login"} = $userStruc->{$uid}->{$userLogin};

    push @users, $user;
}

# PRINT USERS TO JSON
open FILE_USERS,">$fileUsers" or die "Cannot open $fileUsers: $! \n";
binmode(FILE_USERS);
print FILE_USERS JSON::XS->new->utf8->pretty->canonical->encode(\@users);
close (FILE_USERS) or die "Cannot close $fileUsers: $! \n";

# PREPARE GROUPSDATA TO JSON
my @groups;
foreach my $gid (sort keys %$groupStruc) {
    my $group = {};
    $group->{"id"} = $gid;
	$group->{"name"} = $groupStruc->{$gid}->{$A_GROUP_NAME};
	$group->{"parentGroupId"} = $groupStruc->{$gid}->{$A_GROUP_PAR_ID};

    my @members;
    foreach my $uid (sort keys %{$membershipStruc->{$gid}}){
		my $struct = {};
		$struct->{"userId"} = $uid;
		push @members, $struct;
    }

    $group->{"members"} = \@members;
    push @groups, $group;
}

# PRINT GROUPS TO JSON
open FILE_GROUPS,">$fileGroups" or die "Cannot open $fileGroups: $! \n";
binmode(FILE_GROUPS);
print FILE_GROUPS JSON::XS->new->utf8->pretty->canonical->encode(\@groups);
close (FILE_GROUPS) or die "Cannot close $fileGroups: $! \n";

perunServicesInit::finalize;

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

    my $uid = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_ID );
    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 );
    my $login = $data->getUserFacilityAttributeValue( member => $memberId, facility => $data->getFacilityId, attrName => $A_USER_LOGIN );

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

		if ($memberStatus eq $STATUS_EXPIRED && $status eq $STATUS_VALID){
			# change from EXPIRED to VALID
			$userStruc->{$uid}->{$userStatus} = $status;
		} elsif ($memberStatus eq $STATUS_SUSPENDED && $status eq $STATUS_VALID){
			# change from SUSPENDED to VALID
			$userStruc->{$uid}->{$userStatus} = $status;
		} elsif ($memberStatus eq $STATUS_SUSPENDED && $status eq $STATUS_EXPIRED){
			# change from SUSPENDED to EXPIRED
			$userStruc->{$uid}->{$userStatus} = $status;
		}
	} else {
		$userStruc->{$uid}->{$userStatus} = $status;
		$userStruc->{$uid}->{$userEmail} = $email;
		$userStruc->{$uid}->{$userDisplayName} = $d_name;
		$userStruc->{$uid}->{$userLogin} = $login;
	}

	processMemberships $gid, $uid;
}

## creates structure for groups.scim file
sub processGroups {
	my $groupId = shift;
	my $resourceId = shift;

    if ($data->getGroupAttributeValue( group => $groupId, attrName => $A_GROUP_NAME)) {
        my $groupName = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_NAME);
        my $groupParId = $data->getGroupAttributeValue(group => $groupId, attrName => $A_GROUP_PAR_ID);

        unless(exists $groupStruc->{$groupId}) {
            $groupStruc->{$groupId}->{$A_GROUP_NAME} = $groupName;
            $groupStruc->{$groupId}->{$A_GROUP_PAR_ID} = $groupParId;
        }

		foreach my $memberId ($data->getMemberIdsForResourceAndGroup( group => $groupId, resource => $resourceId )) {
			processUsers $groupId, $memberId;
		}
	}
}

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

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