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 get             = "http://art-decor.org/ns/art-decor-settings" at "art-decor-settings.xqm";
import module namespace art             = "http://art-decor.org/ns/art" at "art-decor.xqm";
import module namespace templ           = "http://art-decor.org/ns/decor/template" at "../api/api-decor-template.xqm";
import module namespace adsearch        = "http://art-decor.org/ns/decor/search" at "../api/api-decor-search.xqm";
import module namespace adserver        = "http://art-decor.org/ns/art-decor-server" at "..//api/api-server-settings.xqm";
(:import module namespace getf            = "http://art-decor.org/ns/fhir-settings" at "../../fhir/3.0/api/fhir-settings.xqm";:)

declare namespace http              = "http://expath.org/ns/http-client";
declare namespace f                 = "http://hl7.org/fhir";

(:
    Hard coded Accept header may turn out tricky, but relying on one particular FHIR server for this variable is even more tricky
:)
declare variable $requestHeaders         := 
    <http:request method="GET">
        <http:header name="Accept" value="application/fhir+xml"/>
        <http:header name="Content-Type" value="application/fhir+xml"/>
        <http:header name="Cache-Control" value="no-cache"/>
        <http:header name="Max-Forwards" value="1"/>
    </http:request>
;

declare %private function local:getNextPage($nextLink as xs:string?) as element(f:entry)* {
    if (empty($nextLink)) then () else (
        let $service-uri    := xs:anyURI($nextLink)
        let $requestHeaders := 
            <http:request method="GET" href="{$service-uri}">
                <http:header name="Content-Type" value="text/xml"/>
                <http:header name="Cache-Control" value="no-cache"/>
                <http:header name="Max-Forwards" value="1"/>
            </http:request>
        let $server-response        := http:send-request($requestHeaders)
        let $nextLink               := $server-response[2]/f:link[f:relation/@value = 'next']/f:url/@value
        
        return
            $server-response[2]/f:entry | local:getNextPage($nextLink)
    )
};

let $searchString   := if (request:exists()) then request:get-parameter('searchString',())[string-length() gt 0] else ()
let $searchTerms    := if (request:exists()) then tokenize(lower-case($searchString),'\s') else ('lich')
let $projectPrefix  := if (request:exists()) then (request:get-parameter('project',())) else ('demo1-')
(:normally we only want repository templates, but when we are looking for a prototype, we want to include templates from our own project:)
let $includeLocal   := if (request:exists()) then (request:get-parameter('includelocal','false')='true') else (true())
let $returntype     := if (request:exists()) then request:get-parameter('returntype',()) else ('meta')

let $decor          := $get:colDecorData//decor[project/@prefix=$projectPrefix]

let $buildingBlockRepositories  := 
    if ($includeLocal) then (
        $decor/project/buildingBlockRepository[@format='fhir'] | 
        <buildingBlockRepository url="{adserver:getServerURLServices()}" ident="{$projectPrefix}" format="fhir"/>
    )
    else (
        $decor/project/buildingBlockRepository[@format='fhir']
    )
    
let $cache := $get:colDecorCache//cachedFHIRRepositories
        
let $result := 
    for $repository in $buildingBlockRepositories
    return
    <repositoryStructureDefinitionList url="{$repository/@url}" ident="{$repository/@ident}" cache="{count($cache)}" fullUrl="{concat($repository/@url, $repository/@ident, '/StructureDefinition')}">
    {
        (: in cache? :)
        if ($cache/cacheme[@bbrurl=$repository/@url][@bbrident=$repository/@ident]) then (
            let $entries    := $cache/cacheme[@bbrurl=$repository/@url][@bbrident=$repository/@ident]//f:Bundle/f:entry
            
            return
                if (empty($searchString)) then $entries[f:resource/f:StructureDefinition] else (
                    $entries[f:resource/f:StructureDefinition[
                        f:url[contains(@value, $searchTerms)] | 
                        f:name[contains(@value, $searchTerms)] | 
                        f:title[contains(@value, $searchTerms)]
                    ]]
                )
        ) else (
            (: otherwise retrieve it :)
            try {
                let $url                := concat($repository/@url, $repository/@ident, '/StructureDefinition')
                let $server-response    := http:send-request($requestHeaders, $url)
                let $nextLink           := $server-response//f:Bundle/f:link[f:relation/@value = 'next']/f:url/@value
                let $entries            := $server-response//f:Bundle/f:entry | local:getNextPage($nextLink)
                return (
                    if (empty($searchString)) then $entries[f:resource/f:StructureDefinition] else (
                        $entries[f:resource/f:StructureDefinition[
                            f:url[contains(@value, $searchTerms)] | 
                            f:name[contains(@value, $searchTerms)] | 
                            f:title[contains(@value, $searchTerms)]
                        ]]
                    )
                )
            } catch * { 
                <error/>
            }
        )
    }
    </repositoryStructureDefinitionList>

return (
    if (response:exists()) then response:set-header('Content-Type', 'application/xml') else (),
    <result count="{count($result//f:StructureDefinition)}" search="{$searchTerms}" includelocal="{$includeLocal}">
    {
        for $bbr in $result
        return
            element {name($bbr)} {
                $bbr/@*
                ,
                for $entry in $bbr/f:entry
                let $sortkey    := $entry/f:resource/f:StructureDefinition/f:name/@value
                order by lower-case($sortkey)
                return 
                    if ($returntype = 'meta') then (
                        element {name($entry)} {
                            $entry/@*,
                            for $node in $entry/f:*
                            return 
                                if ($node[local-name() = 'resource']) then 
                                    element {name($node)} {
                                        $node/@*,
                                        for $strucdef in $node/f:StructureDefinition
                                        return 
                                            element {name($strucdef)} {
                                                $strucdef/@*,
                                                $strucdef/(* except (f:snapshot | f:differential)),
                                                comment {'snapshot and differential skipped because only meta was requested'}
                                            }
                                    }
                                else ($node)
                        }
                    )
                    else ($entry)
            }
    }
    </result>
)