@prefix : <https://ontology.inferal.com/modules/confidence/> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

:ConfidenceAssessmentShape
    a sh:NodeShape ;
    rdfs:label "confidence assessment shape" ;
    rdfs:comment "Validates that each confidence assessment has exactly one structured confidence value and optionally names the assessed target." ;
    sh:targetClass :ConfidenceAssessment ;
    sh:property [
        sh:path :assessmentTarget ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :confidenceValue ;
        sh:class :ConfidenceValue ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .

:ConfidenceValueShape
    a sh:NodeShape ;
    rdfs:label "confidence value shape" ;
    rdfs:comment "Validates scale links shared by all confidence values." ;
    sh:targetClass :ConfidenceValue ;
    sh:property [
        sh:path :scale ;
        sh:class :ConfidenceScale ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .

:ProbabilityValueShape
    a sh:NodeShape ;
    rdfs:label "probability value shape" ;
    rdfs:comment "Validates probability confidence values." ;
    sh:targetClass :ProbabilityValue ;
    sh:property [
        sh:path ( :scale [ sh:zeroOrMorePath :specializes ] ) ;
        sh:hasValue :ProbabilityScale ;
        sh:message "A ProbabilityValue must use ProbabilityScale or a scale that specializes it." ;
    ] ;
    sh:property [
        sh:path :score ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "A ProbabilityValue must have exactly one score between 0 and 1." ;
    ] .

:FuzzyMembershipValueShape
    a sh:NodeShape ;
    rdfs:label "fuzzy membership value shape" ;
    rdfs:comment "Validates fuzzy-membership confidence values." ;
    sh:targetClass :FuzzyMembershipValue ;
    sh:property [
        sh:path ( :scale [ sh:zeroOrMorePath :specializes ] ) ;
        sh:hasValue :FuzzyMembershipScale ;
        sh:message "A FuzzyMembershipValue must use FuzzyMembershipScale or a scale that specializes it." ;
    ] ;
    sh:property [
        sh:path :score ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "A FuzzyMembershipValue must have exactly one score between 0 and 1." ;
    ] .

:SubjectiveLogicValueShape
    a sh:NodeShape ;
    rdfs:label "subjective logic value shape" ;
    rdfs:comment "Validates subjective-logic opinion values and their projected score." ;
    sh:targetClass :SubjectiveLogicValue ;
    sh:property [
        sh:path ( :scale [ sh:zeroOrMorePath :specializes ] ) ;
        sh:hasValue :SubjectiveLogicScale ;
        sh:message "A SubjectiveLogicValue must use SubjectiveLogicScale or a scale that specializes it." ;
    ] ;
    sh:property [
        sh:path :belief ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :disbelief ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :uncertainty ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :baseRate ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :score ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:sparql [
        sh:message "A SubjectiveLogicValue must have belief + disbelief + uncertainty equal to 1 within decimal tolerance." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/confidence/>
            PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
            SELECT $this WHERE {
                $this :belief ?belief ;
                    :disbelief ?disbelief ;
                    :uncertainty ?uncertainty .
                FILTER(ABS((xsd:decimal(?belief) + xsd:decimal(?disbelief) + xsd:decimal(?uncertainty)) - 1.0) > 0.000001)
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A SubjectiveLogicValue score must equal the projected probability belief + baseRate * uncertainty within decimal tolerance." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/confidence/>
            PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
            SELECT $this WHERE {
                $this :belief ?belief ;
                    :uncertainty ?uncertainty ;
                    :baseRate ?baseRate ;
                    :score ?score .
                FILTER(ABS(xsd:decimal(?score) - (xsd:decimal(?belief) + xsd:decimal(?baseRate) * xsd:decimal(?uncertainty))) > 0.000001)
            }
        """ ;
    ] .

:DempsterShaferValueShape
    a sh:NodeShape ;
    rdfs:label "Dempster-Shafer value shape" ;
    rdfs:comment "Validates Dempster-Shafer evidence values used for evidence-source fusion." ;
    sh:targetClass :DempsterShaferValue ;
    sh:property [
        sh:path ( :scale [ sh:zeroOrMorePath :specializes ] ) ;
        sh:hasValue :DempsterShaferScale ;
        sh:message "A DempsterShaferValue must use DempsterShaferScale or a scale that specializes it." ;
    ] ;
    sh:property [
        sh:path :score ;
        sh:datatype xsd:decimal ;
        sh:minInclusive 0 ;
        sh:maxInclusive 1 ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "A DempsterShaferValue must have exactly one scalar projection between 0 and 1." ;
    ] .

:OrdinalValueShape
    a sh:NodeShape ;
    rdfs:label "ordinal value shape" ;
    rdfs:comment "Validates ordinal confidence values." ;
    sh:targetClass :OrdinalValue ;
    sh:property [
        sh:path ( :scale [ sh:zeroOrMorePath :specializes ] ) ;
        sh:hasValue :OrdinalScale ;
        sh:message "An OrdinalValue must use OrdinalScale or a scale that specializes it." ;
    ] ;
    sh:property [
        sh:path :ordinalRank ;
        sh:class :OrdinalRank ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :score ;
        sh:maxCount 0 ;
        sh:message "An OrdinalValue must not carry a scalar score; record an explicit calibration conversion to a numeric scale instead." ;
    ] .

:ConfidenceScaleShape
    a sh:NodeShape ;
    rdfs:label "confidence scale shape" ;
    rdfs:comment "Validates confidence scale composability markers and operator links." ;
    sh:targetClass :ConfidenceScale ;
    sh:property [
        sh:path :composable ;
        sh:datatype xsd:boolean ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :conjunctionOperator ;
        sh:class :CompositionOperator ;
    ] ;
    sh:property [
        sh:path :disjunctionOperator ;
        sh:class :CompositionOperator ;
    ] ;
    sh:property [
        sh:path :negationOperator ;
        sh:class :CompositionOperator ;
    ] ;
    sh:property [
        sh:path :fusionOperator ;
        sh:class :CompositionOperator ;
    ] ;
    sh:property [
        sh:path :specializes ;
        sh:class :ConfidenceScale ;
    ] .

:CompositionOperatorShape
    a sh:NodeShape ;
    rdfs:label "composition operator shape" ;
    rdfs:comment "Validates declared scale signatures and assumptions for confidence operators." ;
    sh:targetClass :CompositionOperator ;
    sh:property [
        sh:path :inputScale ;
        sh:class :ConfidenceScale ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path :outputScale ;
        sh:class :ConfidenceScale ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :requiresAssumption ;
        sh:class :Assumption ;
    ] ;
    sh:property [
        sh:path :minimumInputs ;
        sh:datatype xsd:integer ;
        sh:minInclusive 1 ;
        sh:maxCount 1 ;
    ] .

:ConfidenceCompositionShape
    a sh:NodeShape ;
    rdfs:label "confidence composition shape" ;
    rdfs:comment "Validates PROV-based confidence composition activities and checks required assumptions are recorded." ;
    sh:targetClass :ConfidenceComposition ;
    sh:property [
        sh:path :usedOperator ;
        sh:class :CompositionOperator ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path prov:generated ;
        sh:class :ConfidenceAssessment ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path :recordedAssumption ;
        sh:class :Assumption ;
    ] ;
    sh:property [
        sh:path :evidenceModel ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] ;
    sh:sparql [
        sh:message "A ConfidenceComposition must use at least the minimum number of input ConfidenceAssessment nodes declared by its operator, defaulting to two when the operator declares no minimum." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/confidence/>
            PREFIX prov: <http://www.w3.org/ns/prov#>
            SELECT $this ?operator WHERE {
                {
                    SELECT $this ?operator ?minimumInputs (COUNT(DISTINCT ?input) AS ?inputCount) WHERE {
                        $this :usedOperator ?operator .
                        OPTIONAL { ?operator :minimumInputs ?declaredMinimum . }
                        BIND(COALESCE(?declaredMinimum, 2) AS ?minimumInputs)
                        OPTIONAL {
                            $this prov:used ?input .
                            ?input a :ConfidenceAssessment .
                        }
                    }
                    GROUP BY $this ?operator ?minimumInputs
                }
                FILTER(?inputCount < ?minimumInputs)
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A ConfidenceComposition must record every assumption required by its used operator." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/confidence/>
            SELECT $this ?assumption WHERE {
                $this :usedOperator ?operator .
                ?operator :requiresAssumption ?assumption .
                FILTER NOT EXISTS {
                    $this :recordedAssumption ?assumption .
                }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A ConfidenceComposition whose operator requires the calibrated evidence model assumption must link the concrete model with conf:evidenceModel." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/confidence/>
            SELECT $this WHERE {
                $this :usedOperator ?operator .
                ?operator :requiresAssumption :CalibratedEvidenceModelAssumption .
                FILTER NOT EXISTS {
                    $this :evidenceModel ?model .
                }
            }
        """ ;
    ] .

:ScaleConversionRuleShape
    a sh:NodeShape ;
    rdfs:label "scale conversion rule shape" ;
    rdfs:comment "Validates reusable scale conversion rules." ;
    sh:targetClass :ScaleConversionRule ;
    sh:property [
        sh:path :fromScale ;
        sh:class :ConfidenceScale ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :toScale ;
        sh:class :ConfidenceScale ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :lossy ;
        sh:datatype xsd:boolean ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :requiresAssumption ;
        sh:class :Assumption ;
    ] .

:ScaleConversionShape
    a sh:NodeShape ;
    rdfs:label "scale conversion activity shape" ;
    rdfs:comment "Validates concrete scale conversion activities and the reusable conversion rule they applied." ;
    sh:targetClass :ScaleConversion ;
    sh:property [
        sh:path :appliedConversionRule ;
        sh:class :ScaleConversionRule ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path prov:used ;
        sh:class :ConfidenceAssessment ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path prov:generated ;
        sh:class :ConfidenceAssessment ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path :recordedAssumption ;
        sh:class :Assumption ;
    ] ;
    sh:property [
        sh:path :evidenceModel ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] ;
    sh:sparql [
        sh:message "A ScaleConversion whose applied rule requires the calibrated evidence model assumption must link the concrete model with conf:evidenceModel." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/confidence/>
            SELECT $this WHERE {
                $this :appliedConversionRule ?rule .
                ?rule :requiresAssumption :CalibratedEvidenceModelAssumption .
                FILTER NOT EXISTS {
                    $this :evidenceModel ?model .
                }
            }
        """ ;
    ] .

:AssumptionShape
    a sh:NodeShape ;
    rdfs:label "assumption shape" ;
    rdfs:comment "Validates confidence assumptions as named resources." ;
    sh:targetClass :Assumption ;
    sh:nodeKind sh:IRI .

:OrdinalRankShape
    a sh:NodeShape ;
    rdfs:label "ordinal rank shape" ;
    rdfs:comment "Validates ordinal confidence rank resources." ;
    sh:targetClass :OrdinalRank ;
    sh:nodeKind sh:IRI .
