#!/usr/bin/perl
use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use File::Basename;
no if $] >= 5.017011, warnings => 'experimental::smartmatch';

our $SERVICE_NAME = basename($0);
our $PROTOCOL_VERSION = "3.1.0";
my $SCRIPT_VERSION = "3.0.3";

sub mergeQuotas {
	my ($sumQuota, $resourceQuota, $memberQuota) = @_;
	{
		no warnings qw(uninitialized);
		if($sumQuota ~~ 0 || $resourceQuota == 0 || $memberQuota ~~ 0) { return 0; } #note operator == at $resourceQuota
	}

	if(!defined $memberQuota) { $memberQuota = 0; }

	return ($sumQuota || 0) + ($memberQuota > $resourceQuota ? $memberQuota : $resourceQuota);
}

sub mergeStatuses {
	my ($finalStatus, $memberStatus) = @_;
	unless(defined $finalStatus) { return $memberStatus; }

	# SUSPENDED state has priority, then it is enough to have at least one member status == VALID
	if ($memberStatus eq "SUSPENDED" || $finalStatus eq "SUSPENDED") {
		return "SUSPENDED";
	} elsif ($memberStatus eq "VALID" || $finalStatus eq "VALID") {
		return "VALID";
	} elsif ($memberStatus eq "EXPIRED" || $finalStatus eq "EXPIRED") {
		return "EXPIRED";
	} elsif ($memberStatus eq "DISABLED" || $finalStatus eq "DISABLED") {
		return "DISABLED";
	}
}

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

#Constants
our $A_USER_OPTIONAL_LOGIN;        *A_USER_OPTIONAL_LOGIN =         \'urn:perun:user:attribute-def:virt:optionalLogin-namespace:mu';
our $A_UF_LOGIN;                   *A_UF_LOGIN =                     \'urn:perun:user_facility:attribute-def:virt:login';
our $A_UF_DATA_QUOTAS;             *A_UF_DATA_QUOTAS =               \'urn:perun:user_facility:attribute-def:virt:dataQuotas';
our $A_UF_FILE_QUOTAS;             *A_UF_FILE_QUOTAS =               \'urn:perun:user_facility:attribute-def:virt:fileQuotas';
our $A_MEMBER_STATUS;              *A_MEMBER_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_R_HOME_MOUNTPOINT;          *A_R_HOME_MOUNTPOINT =            \'urn:perun:resource:attribute-def:def:fsHomeMountPoint';
our $A_R_VOLUME;                   *A_R_VOLUME =                     \'urn:perun:resource:attribute-def:def:fsVolume';
our $A_R_GID;                      *A_R_GID =                        \'urn:perun:resource:attribute-def:virt:unixGID';
our $A_UF_UID;                     *A_UF_UID =                       \'urn:perun:user_facility:attribute-def:virt:UID';
our $A_F_UMASK;                    *A_F_UMASK =                      \'urn:perun:facility:attribute-def:def:homeDirUmask';
our $A_F_QUOTAENABLED;             *A_F_QUOTAENABLED =               \'urn:perun:facility:attribute-def:def:quotaEnabled';
our $A_GR_UNIX_GROUP_NAME;         *A_GR_UNIX_GROUP_NAME =           \'urn:perun:group_resource:attribute-def:virt:unixGroupName';
our $A_GROUP_GROUP_NAME;           *A_GROUP_GROUP_NAME =             \'urn:perun:group:attribute-def:core:name';
our $A_R_VO_NAME;                  *A_R_VO_NAME =                    \'urn:perun:resource:attribute-def:virt:voShortName';

our $STRUC_GROUPS;   *STRUC_GROUPS =  \"groups";

#headers
my $DATA_QUOTA_HEADER = "dataQuota";
my $DATA_LIMIT_HEADER = "dataLimit";
my $FILE_QUOTA_HEADER = "fileQuota";
my $FILE_LIMIT_HEADER = "fileLimit";

my $service_file_name = "$DIRECTORY/$::SERVICE_NAME";
my $quotas_file_name = "$DIRECTORY/quotas";
my $umask_file_name = "$DIRECTORY/umask";
my $quota_enabled_file_name = "$DIRECTORY/quota_enabled";

#----------------------------------------------------------
# HOME DATA
#----------------------------------------------------------

#structured data about user's home directories
my $dataForUserHomeByLogin = {};

open SERVICE_FILE,">$service_file_name" or die "Cannot open $service_file_name: $! \n";

#prepare home data
foreach my $resourceId ( $data->getResourceIds() ) {
	my $volumeAttr = $data->getResourceAttributeValue( resource => $resourceId, attrName => $A_R_VOLUME );
	my $homeMountPointAttr = $data->getResourceAttributeValue( resource => $resourceId, attrName => $A_R_HOME_MOUNTPOINT );
	my $volume = $volumeAttr || $homeMountPointAttr;
	my $voName = $data->getResourceAttributeValue( resource => $resourceId, attrName => $A_R_VO_NAME );
	my $gid = $data->getResourceAttributeValue( resource => $resourceId, attrName => $A_R_GID );

	foreach my $memberId ( $data->getMemberIdsForResource( resource => $resourceId ) ) {
		my $login = $data->getUserFacilityAttributeValue( member => $memberId, attrName => $A_UF_LOGIN );
		my $optionalLogin = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_OPTIONAL_LOGIN );
		my $uid = $data->getUserFacilityAttributeValue( member => $memberId, attrName => $A_UF_UID );
		my $status = $data->getMemberAttributeValue( member => $memberId, attrName => $A_MEMBER_STATUS );
		my $isSuspended = $data->getMemberAttributeValue( member => $memberId, attrName => $A_MEMBER_IS_SUSPENDED );
		if($isSuspended) { $status = "SUSPENDED"; }

		unless(defined $dataForUserHomeByLogin->{$login}->{$volume}) { $dataForUserHomeByLogin->{$login}->{$volume} = {}; }
		my $alreadyStoredMemberAttrByMount = $dataForUserHomeByLogin->{$login}->{$volume};

		$alreadyStoredMemberAttrByMount->{$A_R_VOLUME} = $volume;
		$alreadyStoredMemberAttrByMount->{$A_UF_LOGIN} = $login;
		$alreadyStoredMemberAttrByMount->{$A_USER_OPTIONAL_LOGIN} = $optionalLogin;
		$alreadyStoredMemberAttrByMount->{$A_R_HOME_MOUNTPOINT}->{$homeMountPointAttr}->{$A_R_GID} = $gid;
		$alreadyStoredMemberAttrByMount->{$A_R_HOME_MOUNTPOINT}->{$homeMountPointAttr}->{$A_UF_UID} = $uid;
		$alreadyStoredMemberAttrByMount->{$A_R_HOME_MOUNTPOINT}->{$homeMountPointAttr}->{$A_MEMBER_STATUS} = mergeStatuses $alreadyStoredMemberAttrByMount->{$A_R_HOME_MOUNTPOINT}->{$homeMountPointAttr}->{$A_MEMBER_STATUS}, $status;
	}

	foreach my $groupId ( $data->getGroupIdsForResource( resource => $resourceId ) ) {
		my $groupName = $data->getGroupAttributeValue( group => $groupId, attrName => $A_GROUP_GROUP_NAME );
		my $groupNameWithVo = $voName . ":" . $groupName;
		my $unixGroupName = $data->getGroupResourceAttributeValue( group => $groupId, resource => $resourceId, attrName => $A_GR_UNIX_GROUP_NAME );

		foreach my $memberId ( $data->getMemberIdsForResourceAndGroup( resource => $resourceId, group => $groupId ) ) {
			my $login = $data->getUserFacilityAttributeValue( member => $memberId, attrName => $A_UF_LOGIN );
			$dataForUserHomeByLogin->{$login}->{$volume}->{$STRUC_GROUPS}->{$groupNameWithVo} = $unixGroupName;
		}
	}
}

foreach my $login (sort keys %$dataForUserHomeByLogin) {
	my $userAttributesByMount = $dataForUserHomeByLogin->{$login};

	for my $userAttributes (values %$userAttributesByMount) {

		for my $mountPoint (sort keys %{$userAttributes->{$A_R_HOME_MOUNTPOINT}}) {
			print SERVICE_FILE $mountPoint . "\t";
			print SERVICE_FILE $login . "\t";
			print SERVICE_FILE $userAttributes->{$A_R_HOME_MOUNTPOINT}->{$mountPoint}->{$A_UF_UID} . "\t";
			print SERVICE_FILE $userAttributes->{$A_R_HOME_MOUNTPOINT}->{$mountPoint}->{$A_R_GID} . "\t";
			print SERVICE_FILE $userAttributes->{$A_R_HOME_MOUNTPOINT}->{$mountPoint}->{$A_MEMBER_STATUS} . "\t";

			print SERVICE_FILE join ',', map { $_ . ">" . ($userAttributes->{$STRUC_GROUPS}->{$_} || "") } sort keys %{$userAttributes->{$STRUC_GROUPS}};
			if(defined($userAttributes->{$A_USER_OPTIONAL_LOGIN})) {
				print SERVICE_FILE "\t" . $userAttributes->{$A_USER_OPTIONAL_LOGIN};
			}
			print SERVICE_FILE "\n";

		}
	}
}

close(SERVICE_FILE);

#----------------------------------------------------------
# QUOTAS DATA
#----------------------------------------------------------

#structured data about quotas
my $dataForUserQuotasByUID = {};

open QUOTAS_FILE,">$quotas_file_name" or die "Cannot open $quotas_file_name: $! \n";

#prepare quotas data
foreach my $resourceId ( $data->getResourceIds() ) {

	foreach my $memberId ( $data->getMemberIdsForResource( resource => $resourceId ) ) {
		my $uid = $data->getUserFacilityAttributeValue( member => $memberId, attrName => $A_UF_UID );
		my $dataQuotas = $data->getUserFacilityAttributeValue( member => $memberId, attrName => $A_UF_DATA_QUOTAS );
		my $fileQuotas = $data->getUserFacilityAttributeValue( member => $memberId, attrName => $A_UF_FILE_QUOTAS );

		unless($dataForUserQuotasByUID->{$uid}) {
			#First process data quotas
			foreach my $volume (keys %{$dataQuotas}) {
				my $dataQuota = $dataQuotas->{$volume};

				my $softDataQuota = $dataQuota;
				$softDataQuota =~ s/:.*$//;
				my $hardDataQuota = $dataQuota;
				$hardDataQuota =~ s/^.*://;
				$dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_QUOTA_HEADER} = quotaToKb $softDataQuota;
				$dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_LIMIT_HEADER} = quotaToKb $hardDataQuota;
				$dataForUserQuotasByUID->{$uid}->{$volume}->{$FILE_QUOTA_HEADER} = 0;
				$dataForUserQuotasByUID->{$uid}->{$volume}->{$FILE_LIMIT_HEADER} = 0;
			}

			#Then process file quotas
			foreach my $volume (keys %{$fileQuotas}) {
				my $fileQuota = $fileQuotas->{$volume};

				my $softFileQuota = $fileQuota;
				$softFileQuota =~ s/:.*$//;
				my $hardFileQuota = $fileQuota;
				$hardFileQuota =~ s/^.*://;
				$dataForUserQuotasByUID->{$uid}->{$volume}->{$FILE_QUOTA_HEADER} = $softFileQuota;
				$dataForUserQuotasByUID->{$uid}->{$volume}->{$FILE_LIMIT_HEADER} = $hardFileQuota;
				unless ($dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_QUOTA_HEADER}) {
					$dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_QUOTA_HEADER} = 0;
				}
				unless ($dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_LIMIT_HEADER}) {
					$dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_LIMIT_HEADER} = 0;
				}
			}
		}
	}
}

foreach my $uid (sort keys %$dataForUserQuotasByUID) {
	foreach my $volume (sort keys %{$dataForUserQuotasByUID->{$uid}}) {
		print QUOTAS_FILE $uid . "\t";
		print QUOTAS_FILE $volume . "\t";
		print QUOTAS_FILE $dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_QUOTA_HEADER} . "\t";
		print QUOTAS_FILE $dataForUserQuotasByUID->{$uid}->{$volume}->{$DATA_LIMIT_HEADER} . "\t";
		print QUOTAS_FILE $dataForUserQuotasByUID->{$uid}->{$volume}->{$FILE_QUOTA_HEADER} . "\t";
		print QUOTAS_FILE $dataForUserQuotasByUID->{$uid}->{$volume}->{$FILE_LIMIT_HEADER} . "\n";
	}
}

close(QUOTAS_FILE);
#----------------------------------------------------------
# UMASK DATA
#----------------------------------------------------------

my $umask = $data->getFacilityAttributeValue( attrName => $A_F_UMASK );
if(defined $umask) {
	open UMASK_FH, ">$umask_file_name" or die "Cannot open $umask_file_name: $!\n";
	print UMASK_FH $umask, "\n";
	close UMASK_FH;
}

#----------------------------------------------------------
# QUOTA ENABLED DATA
#----------------------------------------------------------

my $quotaEnabled = $data->getFacilityAttributeValue( attrName => $A_F_QUOTAENABLED );
if(defined $quotaEnabled) {
	open QUOTA_FH, ">$quota_enabled_file_name" or die "Cannot open $quota_enabled_file_name: $!\n";
	print QUOTA_FH $quotaEnabled, "\n";
	close QUOTA_FH;
}

#####################################################
perunServicesInit::finalize;
