Feature: Team collaboration — multi-user sessions, RBAC, and conflict resolution
  As a CleverAgents team lead
  I want team collaboration features with role-based access control
  So that multiple users can work concurrently on shared projects

  # ── TeamRole enum ────────────────────────────────────────────────

  Scenario: TeamRole enum contains expected members
    Given the TeamRole enum
    Then the TeamRole enum should contain "owner"
    And the TeamRole enum should contain "admin"
    And the TeamRole enum should contain "member"
    And the TeamRole enum should contain "viewer"

  # ── TeamPermission enum ──────────────────────────────────────────

  Scenario: TeamPermission enum contains expected members
    Given the TeamPermission enum
    Then the TeamPermission enum should contain "read"
    And the TeamPermission enum should contain "write"
    And the TeamPermission enum should contain "admin"
    And the TeamPermission enum should contain "manage_members"

  # ── ConflictResolutionStrategy enum ──────────────────────────────

  Scenario: ConflictResolutionStrategy enum contains expected members
    Given the ConflictResolutionStrategy enum
    Then the ConflictResolutionStrategy enum should contain "last_writer_wins"
    And the ConflictResolutionStrategy enum should contain "reject"
    And the ConflictResolutionStrategy enum should contain "merge"

  # ── TEAM_ROLE_PERMISSIONS constant ───────────────────────────────

  Scenario: OWNER role has all 4 permissions
    Given the TEAM_ROLE_PERMISSIONS constant
    Then the "owner" team role should have 4 allowed permissions

  Scenario: ADMIN role has all 4 permissions
    Given the TEAM_ROLE_PERMISSIONS constant
    Then the "admin" team role should have 4 allowed permissions

  Scenario: MEMBER role has read and write
    Given the TEAM_ROLE_PERMISSIONS constant
    Then the "member" team role should have 2 allowed permissions

  Scenario: VIEWER role has only read
    Given the TEAM_ROLE_PERMISSIONS constant
    Then the "viewer" team role should have 1 allowed permissions

  # ── TeamMember model ─────────────────────────────────────────────

  Scenario: Create a valid TeamMember
    Given a team member "alice" with role "owner" and email "alice@example.com"
    Then the team member user_id should be "alice"
    And the team member role should be "owner"
    And the team member email should be "alice@example.com"

  Scenario: TeamMember email validation rejects missing @
    When I try to create a team member with email "invalid-email"
    Then a team member validation error should be raised

  Scenario: TeamMember has_permission returns True for allowed action
    Given a team member "bob" with role "member" and email "bob@example.com"
    Then the team member should have "read" permission
    And the team member should have "write" permission

  Scenario: TeamMember has_permission returns False for denied action
    Given a team member "carol" with role "viewer" and email "carol@example.com"
    Then the team member should not have "write" permission
    And the team member should not have "admin" permission

  # ── UserSession model ────────────────────────────────────────────

  Scenario: Create a valid UserSession
    Given a user session for user "alice" on project "proj-1"
    Then the user session user_id should be "alice"
    And the user session project_id should be "proj-1"
    And the user session should be active

  Scenario: Deactivate a UserSession
    Given a user session for user "alice" on project "proj-1"
    When the session is deactivated
    Then the user session should not be active

  Scenario: Touch updates last_active timestamp
    Given a user session for user "alice" on project "proj-1"
    When the session is touched
    Then the last_active timestamp should be updated

  # ── VersionStamp model ───────────────────────────────────────────

  Scenario: Create a valid VersionStamp
    Given a version stamp for resource "res-1" by user "alice"
    Then the version stamp version should be 1
    And the version stamp updated_by should be "alice"

  Scenario: Increment a VersionStamp
    Given a version stamp for resource "res-1" by user "alice"
    When the version stamp is incremented by "bob"
    Then the new version stamp version should be 2
    And the new version stamp updated_by should be "bob"

  Scenario: VersionStamp increment rejects empty user
    Given a version stamp for resource "res-1" by user "alice"
    Then incrementing with empty user should raise ValueError

  # ── ConflictDetectionResult model ────────────────────────────────

  Scenario: Create a no-conflict result
    Given a conflict detection result with no conflict for "res-1"
    Then the result detected should be False
    And the result resource_id should be "res-1"

  # ── VersionConflictError ─────────────────────────────────────────

  Scenario: VersionConflictError carries version info
    When a VersionConflictError is raised for "res-1" expected 1 actual 2
    Then the error resource_id should be "res-1"
    And the error expected_version should be 1
    And the error actual_version should be 2

  Scenario: VersionConflictError rejects empty resource_id
    Then creating VersionConflictError with empty resource_id raises ValueError

  # ── SessionRegistry ──────────────────────────────────────────────

  Scenario: Register and retrieve a session
    Given a session registry
    And a user session "sess-1" for user "alice" on project "proj-1"
    When the session is registered
    Then the registry should contain the session "sess-1"
    And the registry active count should be 1

  Scenario: Unregister a session
    Given a session registry
    And a user session "sess-1" for user "alice" on project "proj-1"
    When the session is registered
    And the session "sess-1" is unregistered
    Then the registry should not contain the session "sess-1"
    And the registry active count should be 0

  Scenario: Get sessions for a specific user
    Given a session registry
    And a user session "sess-1" for user "alice" on project "proj-1"
    And a user session "sess-2" for user "alice" on project "proj-2"
    And a user session "sess-3" for user "bob" on project "proj-1"
    When all sessions are registered
    Then the registry should have 2 sessions for user "alice"
    And the registry should have 1 sessions for user "bob"

  Scenario: Get sessions for a specific project
    Given a session registry
    And a user session "sess-1" for user "alice" on project "proj-1"
    And a user session "sess-2" for user "bob" on project "proj-1"
    And a user session "sess-3" for user "carol" on project "proj-2"
    When all sessions are registered
    Then the registry should have 2 sessions for project "proj-1"
    And the registry should have 1 sessions for project "proj-2"

  Scenario: Clear all sessions
    Given a session registry
    And a user session "sess-1" for user "alice" on project "proj-1"
    And a user session "sess-2" for user "bob" on project "proj-2"
    When all sessions are registered
    And the registry is cleared
    Then the registry active count should be 0

  Scenario: Duplicate session registration raises ValueError
    Given a session registry
    And a user session "sess-1" for user "alice" on project "proj-1"
    When the session is registered
    Then registering the same session again raises ValueError

  Scenario: Session registry generate_session_id returns valid ULID
    Given a session registry
    Then the generated session ID should be a valid ULID

  # ── TeamCollaborationService — member management ─────────────────

  Scenario: Add and retrieve a team member via service
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    Then the service should have 1 members
    And getting member "alice" should return the member

  Scenario: Adding duplicate member raises TeamMemberExistsError
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    Then adding member "alice" again raises TeamMemberExistsError

  Scenario: Remove a team member via service
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    And I remove member "alice"
    Then the service should have 0 members

  Scenario: Removing non-existent member raises TeamMemberNotFoundError
    Given a team collaboration service
    Then removing member "ghost" raises TeamMemberNotFoundError

  Scenario: Update a member role
    Given a team collaboration service
    When I add member "alice" with role "member" and email "alice@example.com"
    And I update member "alice" role to "admin"
    Then member "alice" should have role "admin"

  # ── TeamCollaborationService — permission checks ─────────────────

  Scenario: Owner has all permissions
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    Then member "alice" should have "read" team permission
    And member "alice" should have "write" team permission
    And member "alice" should have "admin" team permission
    And member "alice" should have "manage_members" team permission

  Scenario: Viewer has only read permission
    Given a team collaboration service
    When I add member "viewer-user" with role "viewer" and email "view@example.com"
    Then member "viewer-user" should have "read" team permission
    And member "viewer-user" should not have "write" team permission

  Scenario: Enforce permission raises PermissionError for viewer writing
    Given a team collaboration service
    When I add member "viewer-user" with role "viewer" and email "view@example.com"
    Then enforcing "write" on "viewer-user" raises PermissionError

  # ── TeamCollaborationService — session management ────────────────

  Scenario: Register a session for a team member
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    And I register a session for "alice" on project "proj-1"
    Then the project "proj-1" should have 1 active sessions

  Scenario: Multiple users have concurrent sessions on same project
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    And I add member "bob" with role "member" and email "bob@example.com"
    And I register a session for "alice" on project "proj-1"
    And I register a session for "bob" on project "proj-1"
    Then the project "proj-1" should have 2 active sessions

  Scenario: Unregister a session reduces active count
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    And I register a session for "alice" on project "proj-1"
    And I unregister the last session
    Then the project "proj-1" should have 0 active sessions

  Scenario: Removing a member cleans up their sessions
    Given a team collaboration service
    When I add member "alice" with role "owner" and email "alice@example.com"
    And I register a session for "alice" on project "proj-1"
    And I remove member "alice"
    Then the project "proj-1" should have 0 active sessions

  # ── TeamCollaborationService — conflict resolution ───────────────

  Scenario: Create and retrieve a version stamp
    Given a team collaboration service
    When I create a version stamp for "res-1" by "alice"
    Then the version stamp for "res-1" should have version 1

  Scenario: Update version with matching expected version succeeds
    Given a team collaboration service
    When I create a version stamp for "res-1" by "alice"
    And I update version for "res-1" expecting 1 by "bob"
    Then the version stamp for "res-1" should have version 2

  Scenario: Update version with mismatched version using reject strategy raises error
    Given a team collaboration service
    When I create a version stamp for "res-1" by "alice"
    And I update version for "res-1" expecting 1 by "bob"
    Then updating "res-1" expecting 1 by "carol" with reject raises VersionConflictError

  Scenario: Update version with last-writer-wins resolves conflict
    Given a team collaboration service
    When I create a version stamp for "res-1" by "alice"
    And I update version for "res-1" expecting 1 by "bob"
    And I update "res-1" expecting 1 by "carol" with last_writer_wins
    Then the conflict result should be detected and resolved
    And the version stamp for "res-1" should have version 3

  Scenario: Detect conflict without resolving
    Given a team collaboration service
    When I create a version stamp for "res-1" by "alice"
    And I update version for "res-1" expecting 1 by "bob"
    Then detecting conflict on "res-1" expecting 1 should return True
    And detecting conflict on "res-1" expecting 2 should return False

  Scenario: Update version with merge strategy resolves conflict
    Given a team collaboration service
    When I create a version stamp for "res-1" by "alice"
    And I update version for "res-1" expecting 1 by "bob"
    And I update "res-1" expecting 1 by "carol" with merge
    Then the conflict result should be detected and resolved

  # ── Error hierarchy ──────────────────────────────────────────────

  Scenario: TeamMemberNotFoundError is a TeamCollaborationError
    Then TeamMemberNotFoundError should be a subclass of TeamCollaborationError

  Scenario: TeamMemberExistsError is a TeamCollaborationError
    Then TeamMemberExistsError should be a subclass of TeamCollaborationError

  Scenario: VersionConflictError is a TeamCollaborationError
    Then VersionConflictError should be a subclass of TeamCollaborationError

  # -----------------------------------------------------------------------
  # Service edge case validations
  # -----------------------------------------------------------------------

  Scenario: Service session_registry property returns the registry
    Given a fresh TeamCollaborationService
    Then the service session_registry should be a SessionRegistry instance

  Scenario: Service add_member rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to add a member with empty user_id via service
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service add_member rejects empty display_name
    Given a fresh TeamCollaborationService
    When I try to add a member with empty display_name via service
    Then a collab ValueError should be raised mentioning "display_name"

  Scenario: Service add_member rejects empty email
    Given a fresh TeamCollaborationService
    When I try to add a member with empty email via service
    Then a collab ValueError should be raised mentioning "email"

  Scenario: Service remove_member rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to remove a member with empty user_id via service
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service get_member rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to get a member with empty user_id via service
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service get_member for non-existent user raises error
    Given a fresh TeamCollaborationService
    When I try to get member "ghost" via service
    Then a TeamMemberNotFoundError should be raised

  Scenario: Service update_member_role rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to update role with empty user_id via service
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service register_session rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to register session with empty user_id via service
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service register_session rejects empty project_id
    Given a fresh TeamCollaborationService
    And a team member "u1" with role "member"
    When I try to register session with empty project_id for user "u1"
    Then a collab ValueError should be raised mentioning "project_id"

  Scenario: Service unregister_session rejects empty session_id
    Given a fresh TeamCollaborationService
    When I try to unregister session with empty session_id
    Then a collab ValueError should be raised mentioning "session_id"

  Scenario: Service get_active_sessions_for_project rejects empty project_id
    Given a fresh TeamCollaborationService
    When I try to get sessions for empty project_id
    Then a collab ValueError should be raised mentioning "project_id"

  Scenario: Service create_version_stamp rejects empty resource_id
    Given a fresh TeamCollaborationService
    When I try to create version stamp with empty resource_id
    Then a collab ValueError should be raised mentioning "resource_id"

  Scenario: Service create_version_stamp rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to create version stamp with empty user_id
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service get_version_stamp rejects empty resource_id
    Given a fresh TeamCollaborationService
    When I try to get version stamp with empty resource_id
    Then a collab ValueError should be raised mentioning "resource_id"

  Scenario: Service update_version rejects empty resource_id
    Given a fresh TeamCollaborationService
    When I try to update version with empty resource_id
    Then a collab ValueError should be raised mentioning "resource_id"

  Scenario: Service update_version rejects empty user_id
    Given a fresh TeamCollaborationService
    When I try to update version with empty user_id
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: Service update_version rejects zero expected_version
    Given a fresh TeamCollaborationService
    When I try to update version with expected_version 0
    Then a collab ValueError should be raised mentioning "expected_version"

  Scenario: Service update_version creates stamp for untracked resource
    Given a fresh TeamCollaborationService
    When I update version for untracked resource "new-res" by user "u1" expecting version 1
    Then the result should indicate no conflict detected
    And the result details should contain "New resource created"

  Scenario: Service detect_conflict rejects empty resource_id
    Given a fresh TeamCollaborationService
    When I try to detect conflict with empty resource_id
    Then a collab ValueError should be raised mentioning "resource_id"

  Scenario: Service detect_conflict rejects zero expected_version
    Given a fresh TeamCollaborationService
    When I try to detect conflict with expected_version 0
    Then a collab ValueError should be raised mentioning "expected_version"

  Scenario: Service detect_conflict returns False for untracked resource
    Given a fresh TeamCollaborationService
    When I detect conflict for untracked resource "phantom" expecting version 1
    Then the conflict result should be False

  # -----------------------------------------------------------------------
  # Domain model edge case validations
  # -----------------------------------------------------------------------

  Scenario: VersionConflictError rejects negative expected_version
    When I try to create VersionConflictError with expected_version -1
    Then a collab ValueError should be raised mentioning "expected_version"

  Scenario: VersionConflictError rejects negative actual_version
    When I try to create VersionConflictError with actual_version -1
    Then a collab ValueError should be raised mentioning "actual_version"

  Scenario: SessionRegistry register rejects empty session_id
    Given a fresh SessionRegistry
    When I try to register a session with empty session_id
    Then a collab ValueError should be raised mentioning "session_id"

  Scenario: SessionRegistry unregister rejects empty session_id
    Given a fresh SessionRegistry
    When I try to unregister a session with empty session_id
    Then a collab ValueError should be raised mentioning "session_id"

  Scenario: SessionRegistry unregister returns False for unknown session
    Given a fresh SessionRegistry
    When I unregister session "nonexistent-id"
    Then the unregister result should be False

  Scenario: SessionRegistry get rejects empty session_id
    Given a fresh SessionRegistry
    When I try to get session with empty session_id
    Then a collab ValueError should be raised mentioning "session_id"

  Scenario: SessionRegistry list_active_sessions returns only active ones
    Given a fresh SessionRegistry
    And a registered session "s1" for user "u1" in project "p1"
    And a registered session "s2" for user "u2" in project "p1"
    When I deactivate session "s1" in the registry
    Then get_active_sessions should return 1 session

  Scenario: SessionRegistry get_sessions_for_user rejects empty user_id
    Given a fresh SessionRegistry
    When I try to get sessions for empty user_id
    Then a collab ValueError should be raised mentioning "user_id"

  Scenario: SessionRegistry get_sessions_for_project rejects empty project_id
    Given a fresh SessionRegistry
    When I try to get sessions for empty project_id via registry
    Then a collab ValueError should be raised mentioning "project_id"
