# BuddyBoss Group Sub-Account Integration - Technical Specification

## 1. Architecture Overview

### 1.1 Core Concept
Each BuddyBoss group becomes a **multi-tenant container** with its own GHL sub-account (location), isolated product catalog, and custom CRM dashboard. Global site-wide GHL integration remains unchanged.

### 1.2 Context Resolution Strategy
```
Request Flow:
User visits group → GroupContextManager detects group ID → Loads group's location_id → Injects into Client singleton → All API calls use group context
```

**Key Decision:** Use **request-scoped context injection** instead of creating new Client instances.

---

## 2. Database Schema

### 2.1 Group Meta Keys
Store in `bp_groups_groupmeta` table:

```php
_ghl_group_location_id        // string - GHL location ID for this group
_ghl_group_oauth_access_token // string - Group-specific OAuth token
_ghl_group_oauth_refresh_token// string - Group-specific refresh token
_ghl_group_wc_enabled         // bool - WooCommerce integration enabled
_ghl_group_tabs_config        // array - Custom tab visibility/permissions
_ghl_group_revenue_split      // array - Commission/revenue model
```

### 2.2 Product Taxonomy
Register custom taxonomy `group_product` for product isolation:

```php
// wp_term_taxonomy
taxonomy: 'group_product'
term_id: <group_id>
description: 'Products visible only in group {group_name}'
```

### 2.3 Order Meta
Store group attribution in `wp_postmeta`:

```php
_group_id              // int - BuddyBoss group ID
_group_organizer_id    // int - User ID of group organizer
_revenue_split_applied // array - Commission breakdown
```

---

## 3. Context Manager (New Class)

### 3.1 GroupContextManager.php
```php
<?php
namespace GHL_CRM\Integrations\BuddyBoss;

class GroupContextManager {
    private static ?self $instance = null;
    private ?int $active_group_id = null;
    private ?string $active_location_id = null;
    private bool $context_locked = false;

    public static function get_instance(): self {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Detect and set group context from current request
     */
    public function resolve_context(): void {
        // Detect from BP global
        if (function_exists('bp_get_current_group_id')) {
            $group_id = bp_get_current_group_id();
            if ($group_id) {
                $this->set_group_context($group_id);
                return;
            }
        }

        // Detect from WooCommerce checkout session
        if (function_exists('WC') && WC()->session) {
            $group_id = WC()->session->get('_ghl_group_context');
            if ($group_id) {
                $this->set_group_context($group_id);
                return;
            }
        }

        // Detect from query param (for direct links)
        if (isset($_GET['ghl_group_id'])) {
            $group_id = absint($_GET['ghl_group_id']);
            if ($group_id && groups_is_user_member(get_current_user_id(), $group_id)) {
                $this->set_group_context($group_id);
            }
        }
    }

    /**
     * Set group context and inject into Client
     */
    public function set_group_context(int $group_id): void {
        if ($this->context_locked) {
            throw new \Exception('Context already locked - cannot change mid-request');
        }

        $this->active_group_id = $group_id;
        $this->active_location_id = groups_get_groupmeta($group_id, '_ghl_group_location_id', true);

        if (empty($this->active_location_id)) {
            // Group has no GHL sub-account - use global location
            $this->active_location_id = \GHL_CRM\Core\SettingsManager::get_instance()->get_setting('location_id');
        }

        // Inject into Client singleton
        $client = \GHL_CRM\API\Client\Client::get_instance();
        $client->set_location_id($this->active_location_id);

        // If group has custom OAuth tokens, inject those too
        $access_token = groups_get_groupmeta($group_id, '_ghl_group_oauth_access_token', true);
        if (!empty($access_token)) {
            $client->set_token($access_token);
        }

        $this->context_locked = true;
    }

    /**
     * Get active group ID
     */
    public function get_active_group_id(): ?int {
        return $this->active_group_id;
    }

    /**
     * Check if in group context
     */
    public function is_group_context(): bool {
        return null !== $this->active_group_id;
    }

    /**
     * Clear context (for testing or manual reset)
     */
    public function clear_context(): void {
        $this->active_group_id = null;
        $this->active_location_id = null;
        $this->context_locked = false;

        // Reset Client to global location
        $client = \GHL_CRM\API\Client\Client::get_instance();
        $client->reload_settings();
    }
}
```

**Hook into WordPress early:**
```php
add_action('init', function() {
    if (class_exists('BuddyPress')) {
        \GHL_CRM\Integrations\BuddyBoss\GroupContextManager::get_instance()->resolve_context();
    }
}, 5); // Before BP templates load
```

---

## 4. Client.php Modifications

### 4.1 Changes Needed
**NO major refactor needed.** existing `Client.php` already supports:
- ✅ `set_location_id()` - Can be called dynamically
- ✅ `set_token()` - Can inject group-specific tokens
- ✅ `reload_settings()` - Can refresh from SettingsManager

**Only add this method:**
```php
/**
 * Set group-specific OAuth tokens (temporary override)
 */
public function set_group_oauth_tokens(string $access_token, string $refresh_token): void {
    $this->access_token = $access_token;
    $this->refresh_token = $refresh_token;
}
```

---

## 5. WooCommerce Product Isolation

### 5.1 Register Taxonomy
```php
// In BuddyBoss integration loader
register_taxonomy('group_product', 'product', [
    'public' => false,
    'show_ui' => false,
    'hierarchical' => false,
]);
```

### 5.2 Filter Product Queries
```php
add_action('pre_get_posts', function($query) {
    if (!$query->is_main_query() || !is_shop()) {
        return;
    }

    $context = \GHL_CRM\Integrations\BuddyBoss\GroupContextManager::get_instance();
    if ($context->is_group_context()) {
        $group_id = $context->get_active_group_id();
        
        // Show only products tagged with this group
        $tax_query = $query->get('tax_query') ?: [];
        $tax_query[] = [
            'taxonomy' => 'group_product',
            'field' => 'term_id',
            'terms' => $group_id,
        ];
        $query->set('tax_query', $tax_query);
    }
});
```

### 5.3 Hide Global Products in Group Context
```php
add_filter('woocommerce_product_is_visible', function($visible, $product_id) {
    $context = \GHL_CRM\Integrations\BuddyBoss\GroupContextManager::get_instance();
    
    if ($context->is_group_context()) {
        $group_id = $context->get_active_group_id();
        $product_groups = wp_get_object_terms($product_id, 'group_product', ['fields' => 'ids']);
        
        // Only show if product is tagged with current group
        return in_array($group_id, $product_groups);
    }
    
    return $visible;
}, 10, 2);
```

---

## 6. Custom Group Tabs

### 6.1 Register Tabs
```php
add_action('bp_setup_nav', function() {
    if (!bp_is_group()) {
        return;
    }

    $group_id = bp_get_current_group_id();
    $location_id = groups_get_groupmeta($group_id, '_ghl_group_location_id', true);

    if (empty($location_id)) {
        return; // No GHL integration for this group
    }

    // CRM Dashboard Tab
    bp_core_new_subnav_item([
        'name' => 'CRM Dashboard',
        'slug' => 'crm-dashboard',
        'parent_url' => bp_get_group_permalink(groups_get_current_group()),
        'parent_slug' => bp_get_current_group_slug(),
        'screen_function' => 'ghl_group_crm_dashboard_screen',
        'position' => 40,
        'user_has_access' => groups_is_user_admin($group_id, get_current_user_id()),
    ]);

    // Products Tab
    if (groups_get_groupmeta($group_id, '_ghl_group_wc_enabled', true)) {
        bp_core_new_subnav_item([
            'name' => 'Products',
            'slug' => 'products',
            'parent_url' => bp_get_group_permalink(groups_get_current_group()),
            'parent_slug' => bp_get_current_group_slug(),
            'screen_function' => 'ghl_group_products_screen',
            'position' => 41,
        ]);
    }
});
```

### 6.2 Tab Content Rendering
```php
function ghl_group_crm_dashboard_screen() {
    add_action('bp_template_content', function() {
        $context = \GHL_CRM\Integrations\BuddyBoss\GroupContextManager::get_instance();
        $group_id = $context->get_active_group_id();
        $location_id = groups_get_groupmeta($group_id, '_ghl_group_location_id', true);

        // Fetch GHL data using existing ContactResource
        $client = \GHL_CRM\API\Client\Client::get_instance();
        $contact_resource = new \GHL_CRM\API\Resources\ContactResource($client);
        
        try {
            $contacts = $client->get('contacts/', ['limit' => 10]);
            include GHL_CRM_PLUGIN_DIR . 'templates/buddyboss/crm-dashboard.php';
        } catch (\Exception $e) {
            echo '<p>Error loading CRM data: ' . esc_html($e->getMessage()) . '</p>';
        }
    });

    bp_core_load_template('groups/single/plugins');
}
```

---

## 7. Revenue Model & Conflict Resolution

### 7.1 Revenue Split Configuration
Store in group meta:
```php
[
    'model' => 'commission', // or 'full_ownership'
    'organizer_percentage' => 80,
    'platform_percentage' => 20,
]
```

### 7.2 Multi-Group Membership Conflict
**Rule:** User can only be in ONE group context per session.

```php
// When user clicks "Shop" in Group A, set session
WC()->session->set('_ghl_group_context', $group_id);

// If user tries to access Group B shop, show warning:
if ($active_context && $active_context != $requested_group) {
    wp_die('You are currently shopping in another group. Clear your cart to switch groups.');
}
```

---

## 8. Group Admin Settings UI

### 8.1 Meta Box in Group Admin
```php
add_action('bp_groups_admin_meta_boxes', function() {
    add_meta_box(
        'ghl-group-settings',
        'GoHighLevel Integration',
        'ghl_group_settings_meta_box',
        get_current_screen()->id,
        'side',
        'core'
    );
});

function ghl_group_settings_meta_box($item) {
    $group_id = $item->id;
    $location_id = groups_get_groupmeta($group_id, '_ghl_group_location_id', true);
    $wc_enabled = groups_get_groupmeta($group_id, '_ghl_group_wc_enabled', true);
    ?>
    <label>
        GHL Location ID:
        <input type="text" name="ghl_group_location_id" value="<?php echo esc_attr($location_id); ?>" />
    </label>
    <label>
        <input type="checkbox" name="ghl_group_wc_enabled" <?php checked($wc_enabled); ?> />
        Enable WooCommerce Integration
    </label>
    <?php
}
```

---

## 9. Using Existing Resources

### 9.1 ContactResource.php
**No changes needed.** It uses `Client::get_instance()` which already has context-aware `location_id` injected.

```php
// In group tab
$client = \GHL_CRM\API\Client\Client::get_instance(); // Already has group location
$contact_resource = new \GHL_CRM\API\Resources\ContactResource($client);
$contacts = $contact_resource->list(['limit' => 50]); // Fetches from group's location
```

### 9.2 QueueManager.php
**Needs minor enhancement:**

Add `group_id` to queue payload:
```php
$this->add_to_queue(
    'user',
    $user_id,
    'user_register',
    [
        'email' => $email,
        '_group_id' => $group_id, // NEW
    ]
);
```

Then in `QueueProcessor::execute_user_sync()`:
```php
if (!empty($payload['_group_id'])) {
    $context = \GHL_CRM\Integrations\BuddyBoss\GroupContextManager::get_instance();
    $context->set_group_context($payload['_group_id']);
}
```

---

## 10. Migration & Backward Compatibility

### 10.1 Feature Flag
```php
define('GHL_CRM_GROUP_SUBACCOUNTS_ENABLED', true);

// In all group-specific code:
if (!defined('GHL_CRM_GROUP_SUBACCOUNTS_ENABLED') || !GHL_CRM_GROUP_SUBACCOUNTS_ENABLED) {
    return; // Feature disabled
}
```

### 10.2 Existing Groups
- Groups without `_ghl_group_location_id` use global site-wide location (existing behavior)
- No migration needed - opt-in per group

---

## 11. Implementation Checklist

### Phase 1: Foundation (Week 1)
- [ ] Create `GroupContextManager.php`
- [ ] Add group meta keys
- [ ] Hook context resolver into `init`
- [ ] Test context switching

### Phase 2: WooCommerce Isolation (Week 1-2)
- [ ] Register `group_product` taxonomy
- [ ] Filter product queries
- [ ] Add product visibility logic
- [ ] Test cart isolation

### Phase 3: Custom Tabs (Week 2)
- [ ] Register CRM Dashboard tab
- [ ] Register Products tab
- [ ] Create tab templates
- [ ] Fetch GHL data using existing resources

### Phase 4: Admin UI (Week 2-3)
- [ ] Group settings meta box
- [ ] OAuth flow for group organizers
- [ ] Revenue split configuration

### Phase 5: Queue Integration (Week 3)
- [ ] Add `group_id` to queue payloads
- [ ] Update `QueueProcessor` to respect group context
- [ ] Test multi-group sync isolation

### Phase 6: Testing & Documentation (Week 3-4)
- [ ] Test multi-group scenarios
- [ ] Test conflict resolution
- [ ] Write user documentation
- [ ] Create video walkthrough

---

## 12. Risk Mitigation

### 12.1 Context Leak Prevention
**Problem:** API call in Group A accidentally uses Group B's location.

**Solution:**
```php
// After every API call in group context, verify:
if ($context->get_active_group_id() !== $expected_group_id) {
    throw new \Exception('Context mismatch detected!');
}
```

### 12.2 Performance
**Problem:** Extra queries for context resolution.

**Solution:**
- Cache `location_id` in object cache (1-hour TTL)
- Use transients for frequently accessed group meta

---

## 13. Future Enhancements

- [ ] Group-level webhook endpoints (`/webhooks/group/{group_id}/`)
- [ ] Analytics dashboard per group
- [ ] Automated onboarding workflow for new organizers
- [ ] Group-to-group contact transfer tool

---

**Total Estimated Time:** 3-4 weeks for full implementation.

**Files to Create:**
1. `src/Integrations/BuddyBoss/GroupContextManager.php`
2. `src/Integrations/BuddyBoss/GroupSettings.php`
3. `src/Integrations/BuddyBoss/GroupTabs.php`
4. `src/Integrations/WooCommerce/GroupProductIsolation.php`
5. `templates/buddyboss/crm-dashboard.php`
6. `templates/buddyboss/group-products.php`

**Files to Modify:**
1. `src/API/Client/Client.php` - Add `set_group_oauth_tokens()`
2. `src/Sync/QueueManager.php` - Add `group_id` to payloads
3. `src/Sync/QueueProcessor.php` - Inject group context before sync

**No breaking changes to existing plugin functionality.**