@prefix cig:  <http://purl.org/capability-interaction-graph#> .
@prefix gufo: <http://purl.org/nemo/gufo#> .
@prefix sh:   <http://www.w3.org/ns/shacl#> .
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

# =================================================================
#  SHACL shapes for the Capability Interaction Graph (CIG) Ontology
#  <http://purl.org/capability-interaction-graph#>
#
#  Scope: this shapes graph validates the *static* architectural
#  constraints of Section III.B and Section IV.D of the source
#  paper ("Ontology-Grounded Capability Interaction Graphs: From
#  Knowledge Graphs to Fault Trees") -- i.e. constraints that hold
#  of a single CIG snapshot (the initial situation Gamma_0
#  constructed from a system-architecture KG, cf. Section X).
#
#  Out of scope, and NOT validated here: the paper's dynamic /
#  temporal semantics (Sections V-XII) -- situations, events,
#  fault activation, error propagation, the recursive error
#  structure function, and the CIG-to-Fault-Tree synthesis
#  algorithm. These describe how a CIG changes across situations
#  and how to compute over it; they are not constraints on a
#  single RDF graph and cannot be meaningfully expressed as SHACL
#  node/property shapes (nor as OWL axioms) without reifying time,
#  which the paper deliberately keeps outside of the KG layer.
#  Definition 9 (Expectations) is likewise excluded, matching the
#  scope of capability-interaction-graph.ttl.
#
#  Two kinds of shapes are used:
#   (1) Ordinary node/property shapes -- cardinality and class
#       membership checks that mirror the OWL declarations, but
#       are enforced directly against the asserted graph (SHACL
#       does not require, and does not perform, OWL inferencing).
#   (2) SPARQL-based shapes -- for constraints that compare values
#       reached via two different property paths on two different
#       individuals (e.g. "a fault and the capability it prevents
#       must share the same bearer"). These are the constraints
#       that OWL 2 DL cannot express (role-value maps), and are
#       exactly what SHACL's SPARQL constraint component is for.
# =================================================================


# #################################################################
# #  1. Structural shapes (typing, cardinality)
# #################################################################

cig:ComponentShape
    a sh:NodeShape ;
    sh:targetClass cig:Component ;
    sh:property [
        sh:path cig:has ;
        sh:or ( [ sh:class cig:Capability ] [ sh:class cig:Fault ] ) ;
        sh:message "cig:has, from a Component, must point to a Capability or a Fault (Def. 5)."@en ;
    ] .

cig:ChannelShape
    a sh:NodeShape ;
    sh:targetClass cig:Channel ;
    sh:property [
        sh:path cig:accommodates ;
        sh:class cig:ResourceType ;
        sh:minCount 1 ;
        sh:message "A Channel must accommodate at least one ResourceType (Def. 6)."@en ;
    ] ;
    sh:property [
        sh:path cig:has ;
        sh:or ( [ sh:class cig:Capability ] [ sh:class cig:Fault ] ) ;
        sh:message "cig:has, from a Channel, must point to a Capability or a Fault (Def. 6)."@en ;
    ] .

cig:CapabilityShape
    a sh:NodeShape ;
    sh:targetClass cig:Capability ;
    sh:property [
        sh:path gufo:inheresIn ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:or ( [ sh:class cig:Component ] [ sh:class cig:Channel ] ) ;
        sh:message "A Capability must inhere in exactly one Component or Channel (Def. 2, axiom D1)."@en ;
    ] ;
    sh:property [
        sh:path cig:consumes ;
        sh:class cig:ResourceType ;
        sh:message "cig:consumes must point to a ResourceType (Def. 2, Eq. 8)."@en ;
    ] ;
    sh:property [
        sh:path cig:produces ;
        sh:class cig:ResourceType ;
        sh:message "cig:produces must point to a ResourceType (Def. 2, Eq. 8)."@en ;
    ] .

cig:FaultShape
    a sh:NodeShape ;
    sh:targetClass cig:Fault ;
    sh:property [
        sh:path gufo:inheresIn ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:or ( [ sh:class cig:Component ] [ sh:class cig:Channel ] ) ;
        sh:message "A Fault must inhere in exactly one Component or Channel (Def. 4, axiom D1)."@en ;
    ] ;
    sh:property [
        sh:path cig:prevents ;
        sh:minCount 1 ;
        sh:class cig:Capability ;
        sh:message "A Fault must prevent at least one Capability (Def. 4)."@en ;
    ] .

cig:FunctionShape
    a sh:NodeShape ;
    sh:targetClass cig:Function ;
    sh:property [
        sh:path [ sh:inversePath gufo:manifestedIn ] ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class cig:Capability ;
        sh:message "A Function must be the manifestation of exactly one Capability (Def. 3, partial bijection rho)."@en ;
    ] ;
    sh:property [
        sh:path [ sh:inversePath gufo:participatedIn ] ;
        sh:minCount 1 ;
        sh:or ( [ sh:class cig:Component ] [ sh:class cig:Channel ] ) ;
        sh:message "A Function must have a participating Component or Channel."@en ;
    ] .

cig:FaultCreationShape
    a sh:NodeShape ;
    sh:targetClass cig:FaultCreation ;
    sh:property [
        sh:path [ sh:inversePath gufo:wasCreatedIn ] ;
        sh:minCount 1 ;
        sh:class cig:Fault ;
        sh:message "A FaultCreation event must create at least one Fault (Def. 17)."@en ;
    ] .

cig:ConsumerShape
    a sh:NodeShape ;
    sh:targetClass cig:Consumer ;
    sh:property [
        sh:path cig:providedBy ;
        sh:minCount 1 ;
        sh:class cig:Channel ;
        sh:message "A Consumer must be providedBy at least one Channel."@en ;
    ] .

cig:ProducerShape
    a sh:NodeShape ;
    sh:targetClass cig:Producer ;
    sh:property [
        sh:path cig:provides ;
        sh:minCount 1 ;
        sh:class cig:Channel ;
        sh:message "A Producer must provide to at least one Channel."@en ;
    ] .

cig:InputInterfaceShape
    a sh:NodeShape ;
    sh:targetClass cig:InputInterface ;
    sh:property [ sh:path cig:component ; sh:minCount 1 ; sh:maxCount 1 ; sh:class cig:Component ;
        sh:message "An InputInterface must relate exactly one Component (Def. 8)."@en ] ;
    sh:property [ sh:path cig:channel ; sh:minCount 1 ; sh:maxCount 1 ; sh:class cig:Channel ;
        sh:message "An InputInterface must relate exactly one Channel (Def. 8)."@en ] ;
    sh:property [ sh:path cig:component_capabilities ; sh:minCount 1 ; sh:class cig:Capability ;
        sh:message "An InputInterface must aggregate at least one component-side Capability (Def. 8, varpi^i)."@en ] ;
    sh:property [ sh:path cig:channel_capabilities ; sh:minCount 1 ; sh:maxCount 1 ; sh:class cig:Capability ;
        sh:message "An InputInterface must aggregate exactly one channel-side Capability (Def. 8, varsigma^i)."@en ] .

cig:OutputInterfaceShape
    a sh:NodeShape ;
    sh:targetClass cig:OutputInterface ;
    sh:property [ sh:path cig:component ; sh:minCount 1 ; sh:maxCount 1 ; sh:class cig:Component ;
        sh:message "An OutputInterface must relate exactly one Component (Def. 7)."@en ] ;
    sh:property [ sh:path cig:channel ; sh:minCount 1 ; sh:maxCount 1 ; sh:class cig:Channel ;
        sh:message "An OutputInterface must relate exactly one Channel (Def. 7)."@en ] ;
    sh:property [ sh:path cig:component_capabilities ; sh:minCount 1 ; sh:class cig:Capability ;
        sh:message "An OutputInterface must aggregate at least one component-side Capability (Def. 7, varpi^o)."@en ] ;
    sh:property [ sh:path cig:channel_capabilities ; sh:minCount 1 ; sh:maxCount 1 ; sh:class cig:Capability ;
        sh:message "An OutputInterface must aggregate exactly one channel-side Capability (Def. 7, varsigma^o)."@en ] .

cig:InterfaceSubtypeDisjointnessShape
    a sh:NodeShape ;
    sh:targetClass cig:InputInterface ;
    sh:targetClass cig:OutputInterface ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "An Interface must be either an InputInterface or an OutputInterface, not both (Fig. 5, {complete, disjoint})."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this
            WHERE {
                $this a cig:InputInterface, cig:OutputInterface .
            }
        """ ;
    ] .

cig:CapabilityPhaseDisjointnessShape
    a sh:NodeShape ;
    sh:targetClass cig:Capability ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "A Capability must not be both FunctionalCapability and ErroneousCapability at once (Fig. 5, {covering, disjoint})."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this
            WHERE {
                $this a cig:FunctionalCapability, cig:ErroneousCapability .
            }
        """ ;
    ] .


# #################################################################
# #  2. Cross-referential constraints (SPARQL-based)
# #
# #  These validate constraints that compare values reached via two
# #  different property paths -- the class of constraint that OWL 2
# #  DL's "simple role" restrictions rule out (role-value maps),
# #  as discussed for the Fault/Capability bearer-equality case.
# #################################################################

cig:HasInheresInConsistencyShape
    a sh:NodeShape ;
    sh:target [
        a sh:SPARQLTarget ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT ?this
            WHERE { ?this cig:has ?value . }
        """ ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "Whenever X cig:has Y is asserted, Y gufo:inheresIn X should also be asserted (Eq. 1: has(alpha,f) iff inheresIn(f,alpha))."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            PREFIX gufo: <http://purl.org/nemo/gufo#>
            SELECT $this ?value
            WHERE {
                $this cig:has ?value .
                FILTER NOT EXISTS { ?value gufo:inheresIn $this . }
            }
        """ ;
    ] .

cig:FaultBearerConsistencyShape
    a sh:NodeShape ;
    sh:targetClass cig:Fault ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "A Fault can only prevent the manifestation of Capabilities that inhere in the same bearer as the Fault (Def. 4, Eq. 4: beta(f) = beta(id_f))."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            PREFIX gufo: <http://purl.org/nemo/gufo#>
            SELECT $this ?value
            WHERE {
                $this cig:prevents ?value .
                $this gufo:inheresIn ?faultBearer .
                ?value gufo:inheresIn ?capabilityBearer .
                FILTER ( ?faultBearer != ?capabilityBearer )
            }
        """ ;
    ] .

cig:ChannelConsumptionCapabilityUniquenessShape
    a sh:NodeShape ;
    sh:targetClass cig:Channel ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "For each resource type a Channel accommodates, it must have at most one consuming (input) transport Capability: cons_Gamma(c,r) must be unique (Def. 6)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this ?value
            WHERE {
                {
                    SELECT $this ?value (COUNT(?cap) AS ?n)
                    WHERE {
                        $this cig:accommodates ?value .
                        $this cig:has ?cap .
                        ?cap cig:consumes ?value .
                    }
                    GROUP BY $this ?value
                }
                FILTER ( ?n > 1 )
            }
        """ ;
    ] .

cig:ChannelProductionCapabilityUniquenessShape
    a sh:NodeShape ;
    sh:targetClass cig:Channel ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "For each resource type a Channel accommodates, it must have at most one producing (output) transport Capability: prod_Gamma(c,r) must be unique (Def. 6)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this ?value
            WHERE {
                {
                    SELECT $this ?value (COUNT(?cap) AS ?n)
                    WHERE {
                        $this cig:accommodates ?value .
                        $this cig:has ?cap .
                        ?cap cig:produces ?value .
                    }
                    GROUP BY $this ?value
                }
                FILTER ( ?n > 1 )
            }
        """ ;
    ] .

cig:ChannelTransportCapabilityPurityShape
    a sh:NodeShape ;
    sh:targetClass cig:Channel ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "A Channel's own capabilities must each be a *pure* consumption or a *pure* production capability for exactly one resource type: never both consuming and producing, and never more than one resource type (Def. 6: 'a pure consumption capability ... and a pure production capability')."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this ?value
            WHERE {
                $this cig:has ?value .
                {
                    # both consumes and produces something -- not pure
                    ?value cig:consumes ?r1 .
                    ?value cig:produces ?r2 .
                } UNION {
                    # consumes more than one resource type
                    SELECT $this ?value (COUNT(DISTINCT ?r) AS ?n) WHERE {
                        $this cig:has ?value . ?value cig:consumes ?r .
                    } GROUP BY $this ?value HAVING (?n > 1)
                } UNION {
                    # produces more than one resource type
                    SELECT $this ?value (COUNT(DISTINCT ?r) AS ?n) WHERE {
                        $this cig:has ?value . ?value cig:produces ?r .
                    } GROUP BY $this ?value HAVING (?n > 1)
                }
            }
        """ ;
    ] .

cig:TransportCapabilityExclusivityShape
    a sh:NodeShape ;
    sh:targetClass cig:Capability ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "The same Capability cannot simultaneously be the channel-side capability of an InputInterface and of an OutputInterface: a channel's consumption capability cannot itself provide a channel, and its production capability cannot itself be provided by a channel (Section III.B, 'On the special case of transport capabilities')."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this
            WHERE {
                ?ii a cig:InputInterface ; cig:channel_capabilities $this .
                ?oi a cig:OutputInterface ; cig:channel_capabilities $this .
            }
        """ ;
    ] .

cig:OutputInterfaceCapabilityBearerShape
    a sh:NodeShape ;
    sh:targetClass cig:OutputInterface ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "The component-side capabilities of an OutputInterface must inhere in the interface's own component: beta(idf) = proj1(o) (Def. 7)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            PREFIX gufo: <http://purl.org/nemo/gufo#>
            SELECT $this ?value
            WHERE {
                $this cig:component ?comp ; cig:component_capabilities ?value .
                ?value gufo:inheresIn ?bearer .
                FILTER ( ?bearer != ?comp )
            }
        """ ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "The channel-side capability of an OutputInterface must inhere in the interface's own channel (Def. 7)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            PREFIX gufo: <http://purl.org/nemo/gufo#>
            SELECT $this ?value
            WHERE {
                $this cig:channel ?chan ; cig:channel_capabilities ?value .
                ?value gufo:inheresIn ?bearer .
                FILTER ( ?bearer != ?chan )
            }
        """ ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "An OutputInterface must mediate a resource type actually produced by its component-side capability and consumed by its channel-side capability (Def. 7; Section IV.D, resource-type compatibility)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this
            WHERE {
                $this cig:component_capabilities ?compCap ; cig:channel_capabilities ?chanCap .
                FILTER NOT EXISTS {
                    ?compCap cig:produces ?r .
                    ?chanCap cig:consumes ?r .
                }
            }
        """ ;
    ] .

cig:InputInterfaceCapabilityBearerShape
    a sh:NodeShape ;
    sh:targetClass cig:InputInterface ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "The component-side capabilities of an InputInterface must inhere in the interface's own component: beta(idf) = proj2(i) (Def. 8)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            PREFIX gufo: <http://purl.org/nemo/gufo#>
            SELECT $this ?value
            WHERE {
                $this cig:component ?comp ; cig:component_capabilities ?value .
                ?value gufo:inheresIn ?bearer .
                FILTER ( ?bearer != ?comp )
            }
        """ ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "The channel-side capability of an InputInterface must inhere in the interface's own channel (Def. 8)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            PREFIX gufo: <http://purl.org/nemo/gufo#>
            SELECT $this ?value
            WHERE {
                $this cig:channel ?chan ; cig:channel_capabilities ?value .
                ?value gufo:inheresIn ?bearer .
                FILTER ( ?bearer != ?chan )
            }
        """ ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "An InputInterface must mediate a resource type actually produced by its channel-side capability and consumed by its component-side capability (Def. 8; Section IV.D, resource-type compatibility)."@en ;
        sh:select """
            PREFIX cig: <http://purl.org/capability-interaction-graph#>
            SELECT $this
            WHERE {
                $this cig:component_capabilities ?compCap ; cig:channel_capabilities ?chanCap .
                FILTER NOT EXISTS {
                    ?chanCap cig:produces ?r .
                    ?compCap cig:consumes ?r .
                }
            }
        """ ;
    ] .
