Feature: Per-Session and Per-Org Cost Budgets
  As a platform operator
  I want per-session and per-org cost budgets with hierarchy enforcement
  So that spending stays within defined limits at every tier

  # ---- SessionCostBudget model ----

  Scenario: Create session cost budget with defaults
    When I create a session cost budget with defaults
    Then the session budget total_cost should be 0.0
    And the session budget max_cost_usd should be None
    And the session budget utilization should be None
    And the session budget remaining should be None
    And the session budget is_exceeded should be false

  Scenario: Create session cost budget with cap
    When I create a session cost budget with max_cost_usd 100.0
    Then the session budget total_cost should be 0.0
    And the session budget max_cost_usd should be 100.0

  Scenario: Session budget rejects negative max_cost
    When I try to create a session cost budget with max_cost_usd -1.0
    Then a cost budget validation error should be raised

  Scenario: Session budget records cost
    Given a session cost budget with max_cost_usd 100.0
    When I record session cost 25.0
    Then the session budget total_cost should be 25.0
    And the session budget utilization should be 0.25
    And the session budget remaining should be 75.0

  Scenario: Session budget rejects negative cost
    Given a session cost budget with max_cost_usd 100.0
    When I try to record session cost -5.0
    Then a cost budget validation error should be raised

  Scenario: Session budget rejects non-numeric cost
    Given a session cost budget with max_cost_usd 100.0
    When I try to record session cost with type error
    Then a cost budget type error should be raised

  Scenario: Session budget is_exceeded when over cap
    Given a session cost budget with max_cost_usd 10.0
    When I record session cost 15.0
    Then the session budget is_exceeded should be true

  Scenario: Session budget would_exceed check
    Given a session cost budget with max_cost_usd 10.0
    When I record session cost 8.0
    Then the session budget would_exceed 5.0 should be true
    And the session budget would_exceed 1.0 should be false

  Scenario: Session budget would_exceed rejects negative
    Given a session cost budget with max_cost_usd 10.0
    When I try to check would_exceed with -1.0
    Then a cost budget validation error should be raised

  Scenario: Session budget would_exceed rejects non-numeric
    Given a session cost budget with max_cost_usd 10.0
    When I try to check would_exceed with type error
    Then a cost budget type error should be raised

  # ---- OrgCostAccumulator model ----

  Scenario: Create org accumulator with defaults
    When I create an org accumulator for org "org-1"
    Then the org accumulator total_cost should be 0.0
    And the org accumulator max_cost_usd should be None

  Scenario: Create org accumulator with cap
    When I create an org accumulator for org "org-1" with max_cost_usd 500.0
    Then the org accumulator max_cost_usd should be 500.0

  Scenario: Org accumulator rejects negative max_cost
    When I try to create an org accumulator with max_cost_usd -10.0
    Then a cost budget validation error should be raised

  Scenario: Org accumulator records cost
    Given an org accumulator for org "org-1" with max_cost_usd 500.0
    When I record org cost 100.0
    Then the org accumulator total_cost should be 100.0
    And the org accumulator utilization should be 0.2
    And the org accumulator remaining should be 400.0

  Scenario: Org accumulator is_exceeded
    Given an org accumulator for org "org-1" with max_cost_usd 50.0
    When I record org cost 60.0
    Then the org accumulator is_exceeded should be true

  Scenario: Org accumulator rejects negative cost
    Given an org accumulator for org "org-1" with max_cost_usd 100.0
    When I try to record org cost -5.0
    Then a cost budget validation error should be raised

  Scenario: Org accumulator rejects non-numeric cost
    Given an org accumulator for org "org-1" with max_cost_usd 100.0
    When I try to record org cost with type error
    Then a cost budget type error should be raised

  Scenario: Org accumulator would_exceed check
    Given an org accumulator for org "org-1" with max_cost_usd 50.0
    When I record org cost 40.0
    Then the org accumulator would_exceed 20.0 should be true
    And the org accumulator would_exceed 5.0 should be false

  # ---- ThreadSafeOrgCostAccumulator ----

  Scenario: Thread-safe wrapper delegates correctly
    Given a thread-safe org accumulator for org "safe-org" with max_cost_usd 200.0
    When I record thread-safe org cost 50.0
    Then the thread-safe org total_cost should be 50.0
    And the thread-safe org utilization should be 0.25
    And the thread-safe org remaining should be 150.0

  Scenario: Thread-safe wrapper snapshot
    Given a thread-safe org accumulator for org "snap-org" with max_cost_usd 100.0
    When I record thread-safe org cost 30.0
    And I take a snapshot of the thread-safe org accumulator
    Then the snapshot total_cost should be 30.0
    And the snapshot org_id should be "snap-org"

  Scenario: Thread-safe wrapper rejects non-accumulator
    When I try to create a thread-safe wrapper with invalid argument
    Then a cost budget type error should be raised

  Scenario: Thread-safe wrapper is_exceeded
    Given a thread-safe org accumulator for org "ex-org" with max_cost_usd 10.0
    When I record thread-safe org cost 15.0
    Then the thread-safe org is_exceeded should be true

  Scenario: Thread-safe wrapper would_exceed
    Given a thread-safe org accumulator for org "we-org" with max_cost_usd 10.0
    When I record thread-safe org cost 8.0
    Then the thread-safe org would_exceed 5.0 should be true
    And the thread-safe org would_exceed 1.0 should be false

  Scenario: Thread-safe wrapper would_exceed rejects negative
    Given a thread-safe org accumulator for org "neg-org" with max_cost_usd 10.0
    When I try to check thread-safe would_exceed with -1.0
    Then a cost budget validation error should be raised

  Scenario: Thread-safe wrapper would_exceed rejects non-numeric
    Given a thread-safe org accumulator for org "te-org" with max_cost_usd 10.0
    When I try to check thread-safe would_exceed with type error
    Then a cost budget type error should be raised

  Scenario: Thread-safe wrapper record_cost rejects negative
    Given a thread-safe org accumulator for org "rc-org" with max_cost_usd 10.0
    When I try to record thread-safe org cost -3.0
    Then a cost budget validation error should be raised

  Scenario: Thread-safe wrapper record_cost rejects non-numeric
    Given a thread-safe org accumulator for org "rcte-org" with max_cost_usd 10.0
    When I try to record thread-safe org cost with type error
    Then a cost budget type error should be raised

  # ---- CostBudgetService ----

  Scenario: Service configure session budget
    Given a cost budget service
    When I configure session "sess-1" with max_cost_usd 50.0
    Then session "sess-1" budget should have max_cost_usd 50.0

  Scenario: Service configure org budget
    Given a cost budget service
    When I configure org "org-1" with max_cost_usd 200.0
    Then org "org-1" accumulator should have max_cost_usd 200.0

  Scenario: Service rejects empty session_id
    Given a cost budget service
    When I try to configure session "" with max_cost_usd 10.0
    Then a cost budget validation error should be raised

  Scenario: Service rejects empty org_id
    Given a cost budget service
    When I try to configure org "" with max_cost_usd 10.0
    Then a cost budget validation error should be raised

  Scenario: Service rejects negative session max_cost
    Given a cost budget service
    When I try to configure session "bad" with max_cost_usd -1.0
    Then a cost budget validation error should be raised

  Scenario: Service rejects negative org max_cost
    Given a cost budget service
    When I try to configure org "bad" with max_cost_usd -1.0
    Then a cost budget validation error should be raised

  Scenario: Service budget hierarchy allows when within all limits
    Given a cost budget service with warning threshold 0.8
    And session "s1" configured with max_cost_usd 100.0
    And org "o1" configured with max_cost_usd 500.0
    And session "s1" associated with org "o1"
    When I check budget hierarchy for session "s1" with plan_cost 10.0
    Then the budget check should be allowed
    And the budget check warning should be false

  Scenario: Service budget hierarchy blocks at session level
    Given a cost budget service
    And session "s2" configured with max_cost_usd 10.0
    When I record plan cost 8.0 for session "s2"
    And I check budget hierarchy for session "s2" with plan_cost 5.0
    Then the budget check should be denied
    And the budget check exceeded_level should be "session"

  Scenario: Service budget hierarchy blocks at org level
    Given a cost budget service
    And session "s3" configured with max_cost_usd 100.0
    And org "o2" configured with max_cost_usd 10.0
    And session "s3" associated with org "o2"
    When I record plan cost 8.0 for session "s3"
    And I check budget hierarchy for session "s3" with plan_cost 5.0
    Then the budget check should be denied
    And the budget check exceeded_level should be "org"

  Scenario: Service budget hierarchy blocks at plan level
    Given a cost budget service
    And session "s4" configured with max_cost_usd 100.0
    When I check budget hierarchy for session "s4" with plan_cost 5.0 and plan_budget 3.0 and plan_spent 0.0
    Then the budget check should be denied
    And the budget check exceeded_level should be "plan"

  Scenario: Service budget hierarchy emits warning at threshold
    Given a cost budget service with warning threshold 0.5
    And session "sw1" configured with max_cost_usd 100.0
    When I record plan cost 60.0 for session "sw1"
    And I check budget hierarchy for session "sw1" with plan_cost 1.0
    Then the budget check should be allowed
    And the budget check warning should be true

  Scenario: Service record plan cost
    Given a cost budget service
    And session "rc1" configured with max_cost_usd 100.0
    When I record plan cost 25.0 for session "rc1"
    Then session "rc1" budget total_cost should be 25.0

  Scenario: Service record plan cost rejects empty session_id
    Given a cost budget service
    When I try to record plan cost 10.0 for session ""
    Then a cost budget validation error should be raised

  Scenario: Service record plan cost rejects negative cost
    Given a cost budget service
    And session "neg1" configured with max_cost_usd 100.0
    When I try to record plan cost -5.0 for session "neg1"
    Then a cost budget validation error should be raised

  Scenario: Service record plan cost rejects non-numeric cost
    Given a cost budget service
    And session "te1" configured with max_cost_usd 100.0
    When I try to record plan cost with type error for session "te1"
    Then a cost budget type error should be raised

  Scenario: Service check hierarchy rejects empty session_id
    Given a cost budget service
    When I try to check budget hierarchy for session "" with plan_cost 1.0
    Then a cost budget validation error should be raised

  Scenario: Service check hierarchy rejects negative plan_cost
    Given a cost budget service
    And session "hn1" configured with max_cost_usd 100.0
    When I try to check budget hierarchy for session "hn1" with plan_cost -1.0
    Then a cost budget validation error should be raised

  Scenario: Service check hierarchy rejects non-numeric plan_cost
    Given a cost budget service
    And session "hte1" configured with max_cost_usd 100.0
    When I try to check budget hierarchy for session "hte1" with plan_cost type error
    Then a cost budget type error should be raised

  Scenario: Service check hierarchy rejects negative plan_spent
    Given a cost budget service
    And session "hps1" configured with max_cost_usd 100.0
    When I try to check budget hierarchy for session "hps1" with plan_cost 1.0 and plan_spent -1.0
    Then a cost budget validation error should be raised

  Scenario: Service remove session cleanup
    Given a cost budget service
    And session "rm1" configured with max_cost_usd 100.0
    When I remove session "rm1" from cost budget service
    Then session "rm1" budget should be None

  Scenario: Service remove org cleanup
    Given a cost budget service
    And org "rmo1" configured with max_cost_usd 100.0
    When I remove org "rmo1" from cost budget service
    Then org "rmo1" accumulator should be None

  Scenario: Service get_session_budget rejects empty id
    Given a cost budget service
    When I try to get session budget for ""
    Then a cost budget validation error should be raised

  Scenario: Service get_org_accumulator rejects empty id
    Given a cost budget service
    When I try to get org accumulator for ""
    Then a cost budget validation error should be raised

  Scenario: Service rejects invalid warning threshold too high
    When I try to create a cost budget service with warning threshold 1.5
    Then a cost budget validation error should be raised

  Scenario: Service rejects invalid warning threshold too low
    When I try to create a cost budget service with warning threshold -0.1
    Then a cost budget validation error should be raised

  Scenario: Service rejects non-numeric warning threshold
    When I try to create a cost budget service with non-numeric warning threshold
    Then a cost budget type error should be raised

  # ---- AutonomyGuardrailService budget hierarchy integration ----

  Scenario: Guardrail service check budget hierarchy with no session
    Given a guardrail service with cost budget service
    And guardrails configured for plan "p1" with tool_budget 50.0
    When I check budget hierarchy for plan "p1" with tool_cost 10.0
    Then the budget hierarchy check should be allowed

  Scenario: Guardrail service check budget hierarchy blocks at plan
    Given a guardrail service with cost budget service
    And guardrails configured for plan "p2" with tool_budget 5.0
    When I check budget hierarchy for plan "p2" with tool_cost 10.0
    Then the budget hierarchy check should be denied
    And the hierarchy exceeded_level should be "plan"

  Scenario: Guardrail service check budget hierarchy blocks at session
    Given a guardrail service with cost budget service
    And session "gs1" configured on cost budget service with max_cost_usd 10.0
    And plan "gp1" associated with session "gs1"
    When I record guardrail plan cost 8.0 for plan "gp1"
    And I check budget hierarchy for plan "gp1" with tool_cost 5.0
    Then the budget hierarchy check should be denied
    And the hierarchy exceeded_level should be "session"

  Scenario: Guardrail service record plan cost to session
    Given a guardrail service with cost budget service
    And session "grc1" configured on cost budget service with max_cost_usd 100.0
    And plan "gp3" associated with session "grc1"
    And guardrails configured for plan "gp3" with tool_budget 50.0
    When I record guardrail plan cost 10.0 for plan "gp3"
    Then plan "gp3" guardrails budget_spent should be 10.0

  Scenario: Guardrail service associate plan rejects empty plan_id
    Given a guardrail service with cost budget service
    When I try to associate plan "" with session "s1"
    Then a cost budget validation error should be raised

  Scenario: Guardrail service associate plan rejects empty session_id
    Given a guardrail service with cost budget service
    When I try to associate plan "p1" with session ""
    Then a cost budget validation error should be raised

  Scenario: Guardrail service check hierarchy rejects empty plan_id
    Given a guardrail service with cost budget service
    When I try to check budget hierarchy for plan "" with tool_cost 1.0
    Then a cost budget validation error should be raised

  Scenario: Guardrail service check hierarchy rejects negative tool_cost
    Given a guardrail service with cost budget service
    When I try to check budget hierarchy for plan "p1" with tool_cost -1.0
    Then a cost budget validation error should be raised

  Scenario: Guardrail service check hierarchy rejects non-numeric tool_cost
    Given a guardrail service with cost budget service
    When I try to check budget hierarchy for plan "p1" with non-numeric tool_cost
    Then a cost budget type error should be raised

  Scenario: Guardrail service record cost rejects empty plan_id
    Given a guardrail service with cost budget service
    When I try to record guardrail plan cost 1.0 for plan ""
    Then a cost budget validation error should be raised

  Scenario: Guardrail service record cost rejects negative
    Given a guardrail service with cost budget service
    When I try to record guardrail plan cost -1.0 for plan "p1"
    Then a cost budget validation error should be raised

  Scenario: Guardrail service record cost rejects non-numeric
    Given a guardrail service with cost budget service
    When I try to record guardrail plan cost with type error for plan "p1"
    Then a cost budget type error should be raised

  Scenario: Guardrail service remove plan cleans up session map
    Given a guardrail service with cost budget service
    And plan "rp1" associated with session "rs1"
    When I remove plan "rp1" from guardrail service
    Then plan "rp1" should not be associated with any session

  # ---- Settings ----

  Scenario: Settings default budget fields
    When I load default settings
    Then session_max_cost_usd should be None
    And org_max_cost_usd should be None
    And budget_warning_threshold should be 0.8

  # ---- Session model cost_budget integration ----

  Scenario: Session model has cost_budget field
    When I create a session with cost budget max_cost_usd 50.0
    Then the session cost_budget max_cost_usd should be 50.0
    And the session cost_budget total_cost should be 0.0

  Scenario: Session as_cli_dict includes cost_budget
    When I create a session with cost budget max_cost_usd 100.0
    And I record 40.0 to the session cost budget
    And I get the session cli dict
    Then the cli dict should contain "cost_budget" key
    And the cli dict cost_budget total_cost should be 40.0
    And the cli dict cost_budget max_cost_usd should be 100.0
    And the cli dict cost_budget utilization should be "40%"

  Scenario: Service budget hierarchy with event bus emits warning
    Given a cost budget service with mock event bus and warning threshold 0.5
    And session "ew1" configured with max_cost_usd 100.0
    When I record plan cost 60.0 for session "ew1"
    And I check budget hierarchy for session "ew1" with plan_cost 1.0
    Then the mock event bus should have received a BUDGET_WARNING event

  Scenario: Service budget hierarchy with event bus emits exceeded
    Given a cost budget service with mock event bus and warning threshold 0.8
    And session "ee1" configured with max_cost_usd 10.0
    When I record plan cost 8.0 for session "ee1"
    And I check budget hierarchy for session "ee1" with plan_cost 5.0
    Then the mock event bus should have received a BUDGET_EXCEEDED event

  Scenario: Service configure session with org_id association
    Given a cost budget service
    And org "ao1" configured with max_cost_usd 200.0
    When I configure session "as1" with max_cost_usd 50.0 and org_id "ao1"
    And I record plan cost 5.0 for session "as1"
    Then org "ao1" accumulator total_cost should be 5.0

  Scenario: Service configure session rejects empty org_id
    Given a cost budget service
    When I try to configure session "s1" with max_cost_usd 10.0 and empty org_id
    Then a cost budget validation error should be raised

  Scenario: Uncapped session budget utilization is none
    Given a session cost budget with no cap
    Then the session budget utilization should be None
    And the session budget remaining should be None
    And the session budget is_exceeded should be false

  Scenario: Uncapped org accumulator utilization is none
    Given an org accumulator for org "unc-org" with no cap
    Then the org accumulator utilization should be None
    And the org accumulator remaining should be None
    And the org accumulator is_exceeded should be false

  Scenario: Uncapped session budget would_exceed is always false
    Given a session cost budget with no cap
    Then the session budget would_exceed 999.0 should be false

  Scenario: Uncapped org accumulator would_exceed is always false
    Given an org accumulator for org "unc-org2" with no cap
    Then the org accumulator would_exceed 999.0 should be false

  Scenario: Service budget hierarchy with no budget service on guardrail
    Given a guardrail service without cost budget service
    And guardrails configured for plan "np1" with tool_budget 50.0
    And plan "np1" associated with session "ns1" on guardrail service
    When I check budget hierarchy for plan "np1" with tool_cost 10.0
    Then the budget hierarchy check should be allowed

  Scenario: Service budget hierarchy no guardrails no session
    Given a guardrail service with cost budget service
    When I check budget hierarchy for plan "unp1" with tool_cost 10.0
    Then the budget hierarchy check should be allowed

  Scenario: Service budget warning only emitted once per session
    Given a cost budget service with mock event bus and warning threshold 0.5
    And session "once1" configured with max_cost_usd 100.0
    When I record plan cost 60.0 for session "once1"
    And I check budget hierarchy for session "once1" with plan_cost 1.0
    And I check budget hierarchy for session "once1" with plan_cost 1.0
    Then the mock event bus should have received exactly 1 BUDGET_WARNING event

  Scenario: BudgetCheckResult model is frozen
    When I create a budget check result allowed=true
    Then modifying the budget check result should raise an error
