@prefix : <https://ontology.inferal.com/modules/advertising-budget/> .
@prefix adm: <https://ontology.inferal.com/modules/advertising-measurement/> .
@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#> .

:SpendingSpecificationShape
    a sh:NodeShape ;
    rdfs:label "spending specification shape" ;
    rdfs:comment "Validates optional applicability windows without imposing allowance or ceiling requirements." ;
    sh:targetClass :SpendingSpecification ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:property [
        sh:path :effectiveWindow ;
        sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:class rng:Range ;
    ] .

:BudgetShape
    a sh:NodeShape ;
    rdfs:label "budget shape" ;
    rdfs:comment "A budget figure is meaningless without its period, pacing, and scope, so all four parts are required. This is the constraint that stops a bare number being carried through as if it were comparable to another bare number." ;
    sh:targetClass :Budget ;
    sh:property [
        sh:path :budgetAmount ;
        sh:name "budget amount" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class adm:MonetaryAmount ;
    ] ;
    sh:property [
        sh:path :budgetPeriod ;
        sh:name "budget period" ;
        sh:description "Mandatory: a daily and a lifetime figure are different quantities, and without the period nothing can tell them apart." ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :BudgetPeriod ;
    ] ;
    sh:property [
        sh:path :budgetDeliveryMethod ;
        sh:name "delivery method" ;
        sh:description "Mandatory: pacing changes what a partial-period spend figure means." ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :DeliveryMethod ;
    ] ;
    sh:property [
        sh:path :appliesToScope ;
        sh:name "applies to scope" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :BudgetScope ;
    ] ;
    sh:property [
        sh:path :effectiveWindow ;
        sh:name "effective window" ;
        sh:description "Optional. Absence leaves applicability unspecified; it does not establish a standing allowance." ;
        sh:maxCount 1 ;
        sh:class rng:Range ;
    ] .

:BudgetScopeShape
    a sh:NodeShape ;
    rdfs:label "budget scope shape" ;
    rdfs:comment "Scope evidence may be missing or partial. Zero is retained; optional single-valued facts remain quality constraints. Configuration, native counts, represented references and owner roles are independent." ;
    sh:targetClass :BudgetScope ;
    sh:property [
        sh:path :coversEntity ;
        sh:name "covers entity" ;
        sh:description "A legacy covered-entity link with producer-qualified meaning. It may denote a budget owner rather than a funded member; no completeness or currentness follows." ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :coveredEntityCount ;
        sh:name "covered entity count" ;
        sh:description "A legacy producer-qualified scope count, including zero. This may be a native reported count or a derived explicit single-allocation count." ;
        sh:maxCount 1 ;
        sh:datatype xsd:integer ;
        sh:minInclusive 0 ;
    ] ;
    sh:property [
        sh:path :sharedAcrossEntities ;
        sh:name "shared across entities" ;
        sh:description "Legacy sharing evidence whose basis is provider-specific: configured intent or shared-allocation kind. It is not proof of multiple current consumers or complete enumeration." ;
        sh:maxCount 1 ;
        sh:datatype xsd:boolean ;
    ] ;
    sh:property [
        sh:path :configuredSharing ;
        sh:name "configured sharing" ;
        sh:description "A returned configured sharing policy. Absence is unspecified; a create default does not supply a returned value. This evidence grants no write authorization." ;
        sh:maxCount 1 ;
        sh:datatype xsd:boolean ;
    ] ;
    sh:property [
        sh:path :rebalanceEnabled ;
        sh:name "rebalance enabled" ;
        sh:description "A returned rebalance flag. It does not state transfer amounts, redistribution limits, reversibility or a confirmed mutation effect." ;
        sh:maxCount 1 ;
        sh:datatype xsd:boolean ;
    ] ;
    sh:property [
        sh:path :scopeOwner ;
        sh:name "scope owner" ;
        sh:description "An explicitly represented owner role, independent of funded membership. An owner may also be a funded member when that link is stated; ownership alone neither infers nor excludes funding." ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :observedFundedEntity ;
        sh:name "observed funded entity" ;
        sh:description "An explicitly represented native budget-reference or allocation link. It is not measured spending, proof of current active membership or a complete funded-member enumeration." ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :memberBasis ;
        sh:name "member basis" ;
        sh:description "The native predicate or explicit source basis supporting represented budget-reference or allocation links. It does not qualify their completeness or currentness." ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :reportedEntityCount ;
        sh:name "reported entity count" ;
        sh:description "An actual native provider-reported reference or association count, including zero. Its native denominator is recorded by reportedCountBasis; no complete enumeration or write permission follows." ;
        sh:maxCount 1 ;
        sh:datatype xsd:integer ;
        sh:minInclusive 0 ;
    ] ;
    sh:property [
        sh:path :reportedCountBasis ;
        sh:name "reported count basis" ;
        sh:description "The native source predicate whose count is preserved as reportedEntityCount. Producer-derived campaign-owned counts do not carry this basis." ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :sharingBasis ;
        sh:name "sharing basis" ;
        sh:description "The source predicate supporting legacy sharedAcrossEntities evidence. Configured intent and shared-allocation kind have different meanings across producers." ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :scopeAccount ;
        sh:name "scope account" ;
        sh:description "The account resource attached to this scope observation by its producer. This records represented account context without establishing cross-record identity or authorization." ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :scopeObservedAt ;
        sh:name "scope observed at" ;
        sh:description "The source capture instant of this scope observation when explicitly available. It does not establish currentness, a validity interval or complete membership." ;
        sh:maxCount 1 ;
        sh:datatype xsd:dateTime ;
    ] .

:SpendCapShape
    a sh:NodeShape ;
    rdfs:label "spend cap shape" ;
    rdfs:comment "A cap states an amount, the period it applies over, and what it constrains. It carries no delivery method, because a ceiling does not pace." ;
    sh:targetClass :SpendCap ;
    sh:property [
        sh:path :capAmount ;
        sh:name "cap amount" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class adm:MonetaryAmount ;
    ] ;
    sh:property [
        sh:path :capPeriod ;
        sh:name "cap period" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :BudgetPeriod ;
    ] ;
    sh:property [
        sh:path :constrainsScope ;
        sh:name "constrains scope" ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class :BudgetScope ;
    ] ;
    sh:property [
        sh:path :budgetDeliveryMethod ;
        sh:name "delivery method" ;
        sh:description "Forbidden: a ceiling constrains delivery without directing it, so pacing does not apply." ;
        sh:maxCount 0 ;
    ] .

:BudgetPeriodShape
    a sh:NodeShape ;
    rdfs:label "budget period shape" ;
    rdfs:comment "Periods must be resolvable IRIs so a comparability rule can branch on identity rather than on a label." ;
    sh:targetClass :BudgetPeriod ;
    sh:nodeKind sh:IRI .

:DeliveryMethodShape
    a sh:NodeShape ;
    rdfs:label "delivery method shape" ;
    rdfs:comment "Delivery methods must be resolvable IRIs for the same reason as periods." ;
    sh:targetClass :DeliveryMethod ;
    sh:nodeKind sh:IRI .
