@prefix convo: <https://ontology.inferal.com/modules/conversation/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix loc: <https://ontology.inferal.com/modules/localization/> .
@prefix prof: <http://www.w3.org/ns/dx/prof/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix role: <http://www.w3.org/ns/dx/prof/role/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

convo:TranscriptProfile
    a prof:Profile ;
    prof:isProfileOf convo: ;
    prof:hasResource <https://ontology.inferal.com/modules/conversation/profiles/transcript#constraints> .

<https://ontology.inferal.com/modules/conversation/profiles/transcript#constraints>
    a prof:ResourceDescriptor ;
    rdfs:label "Transcript constraints descriptor"@en ;
    rdfs:comment "Describes the SHACL artifact that validates conversation records and ordered memberships."@en ;
    prof:hasArtifact <https://ontology.inferal.com/modules/conversation/profiles/transcript.ttl> ;
    prof:hasRole role:constraints, role:validation ;
    dcterms:conformsTo <http://www.w3.org/ns/shacl#> ;
    dcterms:format <https://www.iana.org/assignments/media-types/text/turtle> .

convo:TranscriptViewShape
    a sh:NodeShape ;
    rdfs:label "transcript ordered view shape"@en ;
    rdfs:comment "Requires each ordered view to name one position frame and at least one contextual membership through either inverse direction."@en ;
    sh:targetClass convo:OrderedView ;
    sh:property [ sh:path convo:hasPositionFrame ; sh:class convo:ContributionSequenceFrame ; sh:minCount 1 ; sh:maxCount 1 ] ;
    sh:property [
        sh:path [ sh:alternativePath ( convo:hasOrderedMembership [ sh:inversePath convo:orderedMembershipOf ] ) ] ;
        sh:class convo:OrderedMembership ;
        sh:minCount 1 ;
    ] .

convo:TranscriptMembershipShape
    a sh:NodeShape ;
    rdfs:label "transcript membership shape"@en ;
    rdfs:comment "Requires a view-local membership to identify one view, contribution, and locus and to remain consistent with the view's recorded conversation."@en ;
    sh:targetClass convo:OrderedMembership ;
    sh:property [
        sh:path [ sh:alternativePath ( convo:orderedMembershipOf [ sh:inversePath convo:hasOrderedMembership ] ) ] ;
        sh:class convo:OrderedView ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [ sh:path convo:membershipContribution ; sh:class convo:Contribution ; sh:minCount 1 ; sh:maxCount 1 ] ;
    sh:property [ sh:path convo:membershipLocus ; sh:class loc:Locus ; sh:minCount 1 ; sh:maxCount 1 ] ;
    sh:sparql [
        sh:message "A membership contribution must belong to the conversation recorded by its ordered view."@en ;
        sh:select """
            PREFIX convo: <https://ontology.inferal.com/modules/conversation/>
            SELECT $this ?contribution ?conversation WHERE {
                $this (convo:orderedMembershipOf|^convo:hasOrderedMembership) ?view ;
                      convo:membershipContribution ?contribution .
                ?view convo:recordsConversation ?conversation .
                FILTER NOT EXISTS {
                    ?contribution (convo:contributionOf|^convo:hasContribution) ?conversation .
                }
            }
        """ ;
    ] .
