@prefix : <https://ontology.inferal.com/modules/advertising-delivery-state/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rng: <https://ontology.inferal.com/modules/range/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

:DeliveryStateShape
    a sh:NodeShape ;
    rdfs:label "delivery state shape" ;
    rdfs:comment "A state must answer the questions separately: which entity, what the advertiser intends, whether it is serving, and its lifecycle standing. Holds are optional, because an entity that is serving has none." ;
    sh:targetClass :DeliveryState ;
    sh:property [
        sh:path :stateOf ;
        sh:name "state of" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :intendedDelivery ;
        sh:name "intended delivery" ;
        sh:description "Mandatory. Without it, an entity prevented from delivering is indistinguishable from one the advertiser switched off." ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :DeliveryIntent ;
    ] ;
    sh:property [
        sh:path :observedServing ;
        sh:name "observed serving" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :ServingState ;
    ] ;
    sh:property [
        sh:path :lifecycleStanding ;
        sh:name "lifecycle standing" ;
        sh:description "Mandatory and separate from intent, so a query for switched-off entities does not return deleted ones. The value is one of the three standings this module defines, or a profile concept narrower than exactly one of them, such as a draft narrower than live. Every standing therefore rolls up to one shared value, and a comparability rule written for the three still accounts for a provider's finer standing." ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :LifecycleState ;
        sh:node :LifecycleStandingValueShape ;
    ] ;
    sh:property [
        sh:path :heldBy ;
        sh:name "held by" ;
        sh:description "Optional and repeatable: several independent causes can prevent delivery at once." ;
        sh:class :ServingHold ;
    ] ;
    sh:property [
        sh:path :effectiveDuring ;
        sh:name "effective during" ;
        sh:maxCount 1 ;
        sh:class rng:Range ;
    ] .

:ServingHoldShape
    a sh:NodeShape ;
    rdfs:label "serving hold shape" ;
    rdfs:comment "A hold states its kind and where it came from. Both are required: the kind decides who can act, and the origin is what makes an inherited pause traceable to the ancestor responsible instead of leaving an operator to walk the hierarchy." ;
    sh:targetClass :ServingHold ;
    sh:property [
        sh:path :holdKind ;
        sh:name "hold kind" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :HoldKind ;
    ] ;
    sh:property [
        sh:path :originatingResource ;
        sh:name "originating resource" ;
        sh:description "Mandatory. A hold with no origin cannot be acted on, which is the defect in provider enumerations whose values say an ancestor is paused without naming it." ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] .

:DeliveryIntentShape
    a sh:NodeShape ;
    rdfs:label "delivery intent shape" ;
    rdfs:comment "Intents must be resolvable IRIs so a rule branches on identity rather than a label." ;
    sh:targetClass :DeliveryIntent ;
    sh:nodeKind sh:IRI .

:ServingStateShape
    a sh:NodeShape ;
    rdfs:label "serving state shape" ;
    rdfs:comment "Serving states must be resolvable IRIs for the same reason as intents." ;
    sh:targetClass :ServingState ;
    sh:nodeKind sh:IRI .

:HoldKindShape
    a sh:NodeShape ;
    rdfs:label "hold kind shape" ;
    rdfs:comment "Hold kinds are extended by provider profiles, so being a resolvable IRI keeps the vocabulary navigable." ;
    sh:targetClass :HoldKind ;
    sh:nodeKind sh:IRI .

:LifecycleStateShape
    a sh:NodeShape ;
    rdfs:label "lifecycle state shape" ;
    rdfs:comment "Lifecycle values must be resolvable IRIs. Which values a delivery state may stand in is decided on the standing, by the lifecycle standing value shape, so a provider profile can still declare a concept carrying its own token." ;
    sh:targetClass :LifecycleState ;
    sh:nodeKind sh:IRI .

:LifecycleStandingValueShape
    a sh:NodeShape ;
    rdfs:label "lifecycle standing value shape" ;
    rdfs:comment "A standing is live, archived or removed, or a concept whose single broader concept is one of the three. A standing with no broader shared standing, with another kind of broader concept, or with two broader standings is refused, because no comparability rule could say which shared standing it counts as." ;
    sh:or (
        [ sh:in ( :liveEntity :archivedEntity :removedEntity ) ]
        [
            sh:path skos:broader ;
            sh:minCount 1 ;
            sh:maxCount 1 ;
            sh:in ( :liveEntity :archivedEntity :removedEntity ) ;
        ]
    ) .
