xquery version "3.0";
(:
    ART-DECOR® STANDARD COPYRIGHT AND LICENSE NOTE
    Copyright © ART-DECOR Expert Group and ART-DECOR Open Tools GmbH
    see https://docs.art-decor.org/copyright and https://docs.art-decor.org/licenses

    This file is part of the ART-DECOR® tools suite.
:)
import module namespace art     = "http://art-decor.org/ns/art" at "art-decor.xqm";
import module namespace get     = "http://art-decor.org/ns/art-decor-settings" at "art-decor-settings.xqm";
declare namespace error         = "http://art-decor.org/ns/decor/terminology/error";

(: function will return only the first of potentially multiple matches :)
declare %private function local:getTerminologyAssociation($associations as element(terminologyAssociation)+, $s_valueSet as xs:string?, $s_flexibility as xs:string?, $s_code as xs:string?, $s_codeSystem as xs:string?, $s_effectiveDate as xs:string?, $s_statusCode as xs:string?) as element() {
    (:filter on effectiveDate:)
    let $associations           := if (empty($s_effectiveDate)) then ($associations) else ($associations[@effectiveDate = $s_effectiveDate])
    
    (:filter on statusCode:)
    let $associations           := if (empty($s_statusCode)) then ($associations) else ($associations[@statusCode = $s_statusCode])
    
    (:filter on valueSet:)
    let $associations           := if (empty($s_valueSet)) then ($associations) else ($associations[@valueSet = $s_valueSet])
    
    (:filter on flexibility:)
    let $associations           := if (empty($s_flexibility)) then ($associations) else ($associations[@flexibility = $s_flexibility])
    
    (:filter on code:)
    let $associations           := if (empty($s_code)) then ($associations) else ($associations[@code = $s_code])
    
    (:filter on codeSystem:)
    let $associations           := if (empty($s_codeSystem)) then ($associations) else ($associations[@codeSystem = $s_codeSystem])
    
    return
        $associations[1]
};

let $updatedAssociation     := if (request:exists()) then request:get-data()/updatedAssociation else (
    (:<updatedAssociation conceptId="" effectiveDate="" statusCode="removed" versionLabel="" expirationDate="" actionText="Koppeling verwijderen" prefix="demo1-" parentConceptId="2.16.840.1.113883.3.1937.99.62.3.2.5" parentConceptFlexibility="2011-01-28T00:00:00" transactionId="2.16.840.1.113883.3.1937.99.62.3.4.2" transactionEffectiveDate="2012-09-05T16:59:35">
        <terminologyAssociation conceptId="2.16.840.1.113883.3.1937.99.62.3.2.5.1" code="DM" codeSystem="2.16.840.1.113883.3.1937.99.62.3.5.2" displayName="Datum meting" effectiveDate="2018-08-25T17:50:01" transactionId="2.16.840.1.113883.3.1937.99.62.3.4.2" transactionEffectiveDate="2012-09-05T16:59:35" codeSystemName="cs-measured-by" equivalence=""/>
    </updatedAssociation>:)
)

let $projectPrefix              := $updatedAssociation/@prefix[string-length() gt 0]
let $transactionId              := $updatedAssociation/*[1]/@transactionId[string-length() gt 0]
let $transactionEffectiveDate   := $updatedAssociation/*[1]/@transactionEffectiveDate[string-length() gt 0]

let $s_cid                      := $updatedAssociation/*[1]/@conceptId[string-length() gt 0]
let $s_ced                      := $updatedAssociation/*[1]/@conceptFlexibility[string-length() gt 0]

(: relevant for transaction bindings to conceptList/concept to know which concept to add them to :)
let $conceptId                  := if ($updatedAssociation/@parentConceptId[string-length() gt 0]) then $updatedAssociation/@parentConceptId else $s_cid
let $conceptEffectiveDate       := if ($updatedAssociation/@parentConceptFlexibility[string-length() gt 0]) then $updatedAssociation/@parentConceptFlexibility else $s_ced

let $s_code                     := $updatedAssociation/*[1]/@code[string-length() gt 0]
let $s_codeSystem               := $updatedAssociation/*[1]/@codeSystem[string-length() gt 0]
let $s_valueSet                 := $updatedAssociation/*[1]/@valueSet[string-length() gt 0]
let $s_flexibility              := $updatedAssociation/*[1]/@flexibility[. castable as xs:dateTime]
let $s_effectiveDate            := $updatedAssociation/*[1]/@effectiveDate[string-length() gt 0]
let $s_statusCode               := $updatedAssociation/*[1]/@statusCode[string-length() gt 0]
let $s_versionLabel             := $updatedAssociation/*[1]/@versionLabel[string-length() gt 0]

(: 
    the only 'statusCode' we currently will support is 'removed'. This is not an actual statusCode, 
    but an instruction to delete the terminologyAssociation
    It's very well possible that in some future stage, terminologyAssociations will have a statusCode.
:)
let $u_statusCode               := $updatedAssociation/@statusCode[. = 'removed']
let $u_versionLabel             := $updatedAssociation/@versionLabel[string-length() gt 0]
let $u_expirationDate           := 
    if ($updatedAssociation/@expirationDate castable as xs:date) then
        concat($updatedAssociation/@expirationDate,'T00:00:00')
    else if ($updatedAssociation/@expirationDate castable as xs:dateTime) then
        $updatedAssociation/@expirationDate
    else ()

let $now                        := format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]')

let $associations               :=
    if ($transactionId) then
        art:getTransactionConcept($conceptId, $conceptEffectiveDate, $transactionId, $transactionEffectiveDate, (), ())/terminologyAssociation
    else (
        art:getDecorByPrefix($projectPrefix)/terminology/terminologyAssociation
    )
let $associations               := 
    if ($s_ced castable as xs:dateTime) then 
        $associations[@conceptId = $s_cid][@conceptFlexibility = $s_ced] 
    else (
        $associations[@conceptId = $s_cid]
    )

(: function will purposely error when no match or more than one match is found :)
let $currentAssociation         := local:getTerminologyAssociation($associations, $s_valueSet, $s_flexibility, $s_code, $s_codeSystem, $s_effectiveDate, ())

let $result                     :=
    if ($u_statusCode='removed') then
        update delete $currentAssociation
    else (
        (:if ($u_statusCode) then
            if ($currentAssociation[@statusCode]) then
                update value $currentAssociation/@statusCode with $u_statusCode
            else (
                update insert attribute statusCode {$u_statusCode} into $currentAssociation
            )
        else (
            update delete $currentAssociation/@statusCode
        )
        ,:)
        if ($u_versionLabel) then 
            if ($currentAssociation[@versionLabel]) then
                update value $currentAssociation/@versionLabel with $u_versionLabel
            else (
                update insert attribute versionLabel {$u_versionLabel} into $currentAssociation
            )
        else (
            update delete $currentAssociation/@versionLabel
        )
        ,
        if ($u_expirationDate) then 
            if ($currentAssociation[@expirationDate]) then
                update value $currentAssociation/@expirationDate with $u_expirationDate
            else (
                update insert attribute expirationDate {$u_expirationDate} into $currentAssociation
            )
        else if ($u_statusCode = 'retired') then
            if ($currentAssociation[@expirationDate]) then
                update value $currentAssociation/@expirationDate with $now
            else (
                update insert attribute expirationDate {$now} into $currentAssociation
            )
        else (
            update delete $currentAssociation/@expirationDate
        )
    )

return
    <data-safe>{true()}</data-safe>