# Feature Flag System

The feature flag system enables controlled beta testing of new features in production without requiring a separate test environment.

## Overview

Feature flags allow you to:
- Test new features with a limited audience before full rollout
- Enable/disable features without code changes or redeployment
- Control access per guild, user, or role
- Eliminate environment drift between test and production

## Architecture

### Database Schema

**`feature_flags` table:**
| Column | Type | Description |
|--------|------|-------------|
| `id` | serial | Primary key |
| `name` | varchar(100) | Unique flag identifier |
| `enabled` | boolean | Whether the flag is active |
| `description` | text | Human-readable description |
| `created_at` | timestamp | Creation time |
| `updated_at` | timestamp | Last update time |

**`feature_flag_access` table:**
| Column | Type | Description |
|--------|------|-------------|
| `id` | serial | Primary key |
| `flag_id` | integer | References feature_flags.id |
| `guild_id` | bigint | Guild whitelist (nullable) |
| `user_id` | bigint | User whitelist (nullable) |
| `role_id` | bigint | Role whitelist (nullable) |
| `created_at` | timestamp | Creation time |

### Service Layer

The `featureFlagsService` (`shared/modules/feature-flags/feature-flags.service.ts`) provides:

```typescript
// Check if a flag is globally enabled
await featureFlagsService.isFlagEnabled("trading_system");

// Check if a user has access to a flagged feature
await featureFlagsService.hasAccess("trading_system", {
  guildId: "123456789",
  userId: "987654321",
  memberRoles: ["role1", "role2"]
});

// Create a new feature flag
await featureFlagsService.createFlag("new_feature", "Description");

// Enable/disable a flag
await featureFlagsService.setFlagEnabled("new_feature", true);

// Grant access to users/roles/guilds
await featureFlagsService.grantAccess("new_feature", { userId: "123" });
await featureFlagsService.grantAccess("new_feature", { roleId: "456" });
await featureFlagsService.grantAccess("new_feature", { guildId: "789" });

// List all flags or access records
await featureFlagsService.listFlags();
await featureFlagsService.listAccess("new_feature");
```

## Usage

### Marking a Command as Beta

Add `beta: true` to any command definition:

```typescript
export const newFeature = createCommand({
  data: new SlashCommandBuilder()
    .setName("newfeature")
    .setDescription("A new experimental feature"),
  beta: true,  // Marks this command as a beta feature
  execute: async (interaction) => {
    // Implementation
  },
});
```

By default, the command name is used as the feature flag name. To use a custom flag name:

```typescript
export const trade = createCommand({
  data: new SlashCommandBuilder()
    .setName("trade")
    .setDescription("Trade items with another user"),
  beta: true,
  featureFlag: "trading_system",  // Custom flag name
  execute: async (interaction) => {
    // Implementation
  },
});
```

### Access Control Flow

When a user attempts to use a beta command:

1. **Check if flag exists and is enabled** - Returns false if flag doesn't exist or is disabled
2. **Check guild whitelist** - User's guild has access if `guild_id` matches
3. **Check user whitelist** - User has access if `user_id` matches
4. **Check role whitelist** - User has access if any of their roles match

If none of these conditions are met, the user sees:
> **Beta Feature**
> This feature is currently in beta testing and not available to all users. Stay tuned for the official release!

## Admin Commands

The `/featureflags` command (Administrator only) provides full management:

### Subcommands

| Command | Description |
|---------|-------------|
| `/featureflags list` | List all feature flags with status |
| `/featureflags create <name> [description]` | Create a new flag (disabled by default) |
| `/featureflags delete <name>` | Delete a flag and all access records |
| `/featureflags enable <name>` | Enable a flag globally |
| `/featureflags disable <name>` | Disable a flag globally |
| `/featureflags grant <name> [user\|role]` | Grant access to a user or role |
| `/featureflags revoke <id>` | Revoke access by record ID |
| `/featureflags access <name>` | List all access records for a flag |

### Example Workflow

```
1. Create the flag:
   /featureflags create trading_system "Item trading between users"

2. Grant access to beta testers:
   /featureflags grant trading_system user:@beta_tester
   /featureflags grant trading_system role:@Beta Testers

3. Enable the flag:
   /featureflags enable trading_system

4. View access list:
   /featureflags access trading_system

5. When ready for full release:
   - Remove beta: true from the command
   - Delete the flag: /featureflags delete trading_system
```

## Best Practices

1. **Descriptive Names**: Use snake_case names that clearly describe the feature
2. **Document Flags**: Always add a description when creating flags
3. **Role-Based Access**: Prefer role-based access over user-based for easier management
4. **Clean Up**: Delete flags after features are fully released
5. **Testing**: Always test with a small group before wider rollout

## Implementation Files

| File | Purpose |
|------|---------|
| `shared/db/schema/feature-flags.ts` | Database schema |
| `shared/modules/feature-flags/feature-flags.service.ts` | Service layer |
| `shared/lib/types.ts` | Command interface with beta properties |
| `bot/lib/handlers/CommandHandler.ts` | Beta access check |
| `bot/commands/admin/featureflags.ts` | Admin command |
