#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use Text::Unidecode;
use XML::Simple;

local $::SERVICE_NAME = "appDB";
local $::PROTOCOL_VERSION = "3.0.0";
my $SCRIPT_VERSION = "3.1.0";

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

#Constants
our $A_VO_SHORT_NAME;        *A_VO_SHORT_NAME =          \'urn:perun:vo:attribute-def:core:shortName';
our $A_VO_APPLICATION_URL;   *A_VO_APPLICATION_URL =     \'urn:perun:vo:attribute-def:def:applicationURL';
our $A_VO_CREATED_AT;        *A_VO_CREATED_AT =          \'urn:perun:vo:attribute-def:core:createdAt';
our $A_VO_DASHBOARD_LINK;    *A_VO_DASHBOARD_LINK =      \'urn:perun:vo:attribute-def:def:dashboardLink';
our $A_VO_DESCRIPTION;       *A_VO_DESCRIPTION =         \'urn:perun:vo:attribute-def:def:description';
our $A_USER_MAIL;            *A_USER_MAIL =              \'urn:perun:user:attribute-def:def:preferredMail';
our $A_USER_CERT_DNS;        *A_USER_CERT_DNS =          \'urn:perun:user:attribute-def:virt:userCertDNs';
our $A_USER_EPPNS;           *A_USER_EPPNS =             \'urn:perun:user:attribute-def:virt:eduPersonPrincipalNames';
our $A_USER_STATUS;          *A_USER_STATUS =            \'urn:perun:member:attribute-def:core:status';
our $A_USER_FIRST_NAME;      *A_USER_FIRST_NAME =        \'urn:perun:user:attribute-def:core:firstName';
our $A_USER_LAST_NAME;       *A_USER_LAST_NAME =         \'urn:perun:user:attribute-def:core:lastName';
our $A_R_APPDB_CONTACT_ROLE; *A_R_APPDB_CONTACT_ROLE =   \'urn:perun:resource:attribute-def:def:appDBContactRole';

our $STATUS_VALID;      *STATUS_VALID =        \'VALID';

my $usersXml = {};
my $vosStruc = {};
my $userVoMap = {};
my $voContacts = {};

# Get all VOS data
foreach my $voId ( $data->getVoIds() ) {

	my $shortName = $data->getVoAttributeValue(vo => $voId, attrName => $A_VO_SHORT_NAME);
	my $description = $data->getVoAttributeValue(vo => $voId, attrName => $A_VO_DESCRIPTION);
	my $applicationUrl = $data->getVoAttributeValue(vo => $voId, attrName => $A_VO_APPLICATION_URL);
	my $createdAt = $data->getVoAttributeValue(vo => $voId, attrName => $A_VO_CREATED_AT);
	my $dashboardLink = $data->getVoAttributeValue(vo => $voId, attrName => $A_VO_DASHBOARD_LINK);

	$vosStruc->{$shortName} = {
		name => $shortName,
		status => "Production",
		alias => $shortName,
		enrollmentUrl => [ $applicationUrl ],
		homepageUrl => [ $dashboardLink ],
		description => [ $description ],
		validationDate => { timezone => "UTC+1",
			content  => $createdAt,
		},
	}

}

# process members from resources

foreach my $resourceId ( $data->getResourceIds() ) {

	foreach my $memberId ($data->getMemberIdsForResource( resource => $resourceId )) {

		# skip non-valid members
		my $memberStatus = $data->getMemberAttributeValue(member => $memberId, attrName => $A_USER_STATUS);
		next unless $memberStatus eq $STATUS_VALID;

		# prepare DNs
		my $userCertDNs = $data->getUserAttributeValue(member => $memberId, attrName => $A_USER_CERT_DNS);

		my @dn = ();
		foreach my $subjectDN (keys %{$userCertDNs}) {
			my $subjectDNWithoutPrefix = $subjectDN;
			$subjectDNWithoutPrefix =~ s/^[0-9]+[:]//;
			push @dn, { "content" => $subjectDNWithoutPrefix,
				"ca" => $userCertDNs->{$subjectDN},
			};
		}

		# prepare name
		my $firstName = $data->getUserAttributeValue(member => $memberId, attrName => $A_USER_FIRST_NAME);
		my $lastName = $data->getUserAttributeValue(member => $memberId, attrName => $A_USER_LAST_NAME);

		my $userName = "";
		if($firstName) {
			$userName = $firstName;
			$userName.= " " . $lastName if $lastName;
		} else {
			$userName.= $lastName if $lastName;
		}

		my $appDbContactRole = $data->getResourceAttributeValue(resource => $resourceId, attrName => $A_R_APPDB_CONTACT_ROLE);

		my $voId = $data->getVoIdForResource(resource => $resourceId);
		my $voShortName = $data->getVoAttributeValue(vo => $voId, attrName => $A_VO_SHORT_NAME);

		my $userId = $data->getUserIdForMember(member => $memberId);
		my $mail = $data->getUserAttributeValue(member => $memberId, attrName => $A_USER_MAIL);
		my $eppns = $data->getUserAttributeValue(member => $memberId, attrName => $A_USER_EPPNS);

		# add user to contact role  (for VOs xml)
		if(defined $appDbContactRole) {
			$voContacts->{$voShortName}->{$appDbContactRole . "-" . $userId} = {
				"name" => [ $userName ],
				"role" => [ $appDbContactRole ],
				"email" => [ $mail ],
				"sso" => [ {} ],
				"eppn" => $eppns,
				"dn" => \@dn,
			}
		}

		#skip user if he was already processed for this VO
		next if $userVoMap->{$userId}->{$voShortName}++;

		#prepare user record  (for users xml)
		my @row = {
			"uservo" => [ $userName ],
			"vo" => [ $voShortName ],
			"email" => [ $mail ],
			"sso" => [ {} ],
			"last_update" => [ "" ],
			"first_update" => [ "" ],
			"eppn" => $eppns,
			"dn" => \@dn,
		};

		#add user to global structure
		push @{$usersXml->{"result"}}, { "row" => \@row };

	}

}

#prepare VOs xml from $vosStruc
for my $shortName (keys %$vosStruc) {
	$vosStruc->{$shortName}->{"contacts"}->{"individuals"}->{"contact"} = [ values %{$voContacts->{$shortName}} ];
}

my $vosXml = {};
push @{$vosXml->{"IDCard"}}, values %$vosStruc;

#print users xml
my $fileName = "$DIRECTORY/users";
open FILE,">$fileName" or die "Cannot open $fileName: $! \n";
binmode FILE, ":utf8";
print FILE XMLout($usersXml, KeepRoot => 1);
close FILE or die "Cannot close $fileName: $! \n";

#print VOs xml
$fileName = "$DIRECTORY/vos";
open FILE,">$fileName" or die "Cannot open $fileName: $! \n";
binmode FILE, ":utf8";
print FILE XMLout($vosXml, KeepRoot => 1);
close FILE or die "Cannot close $fileName: $! \n";

perunServicesInit::finalize;
