# RedAI Zalo SDK - Tag Management Guide

## Tổng quan

**Tag Management** là hệ thống phân loại và quản lý người dùng thông qua các nhãn (tags). Điều này cho phép:

- 🏷️ **User Segmentation** - Phân đoạn khách hàng theo nhiều tiêu chí
- 📊 **Targeted Marketing** - Gửi tin nhắn có mục tiêu cụ thể
- 🎯 **Personalized Content** - Nội dung được cá nhân hóa theo tags
- 📈 **Analytics & Insights** - Phân tích hiệu quả theo từng segment
- 🔄 **Automation** - Tự động gán tags dựa trên hành vi

---

## TagService

Service chính để quản lý tags và tag users.

### Khởi tạo

```typescript
import { ZaloSDK } from "@warriorteam/redai-zalo-sdk";

const zalo = new ZaloSDK({
  appId: "your-app-id",
  appSecret: "your-app-secret"
});

// Access tag service
const tagService = zalo.tag;
```

---

## Quản lý Tags

### 1. Tạo tag mới

```typescript
// Tạo tag mới cho campaign
const newTag = await zalo.tag.createTag(
  accessToken,
  "BLACK_FRIDAY_2024"
);

console.log("Created tag:", newTag.tag_id);
console.log("Tag name:", newTag.tag_name);
```

### 2. Lấy danh sách tags

```typescript
// Lấy tất cả tags hiện có
const tagList = await zalo.tag.getTagList(accessToken);

console.log("Total tags:", tagList.total);
tagList.data.forEach(tag => {
  console.log(`${tag.tag_name}: ${tag.user_count} users`);
});
```

### 3. Xóa tag

```typescript
// Xóa tag không còn sử dụng
await zalo.tag.deleteTag(accessToken, "OLD_CAMPAIGN_TAG");
console.log("Tag deleted successfully");
```

---

## Gán Tags cho Users

### 1. Tag một user

```typescript
// Gán nhiều tags cho một user
await zalo.tag.tagUser(
  accessToken,
  "user-zalo-id",
  ["VIP_CUSTOMER", "ELECTRONICS_BUYER", "PREMIUM"]
);

console.log("User tagged successfully");
```

### 2. Untag user

```typescript
// Bỏ tag khỏi user
await zalo.tag.untagUser(
  accessToken,
  "user-zalo-id", 
  ["OLD_CAMPAIGN", "EXPIRED_SEGMENT"]
);

console.log("Tags removed successfully");
```

### 3. Lấy tags của user

```typescript
// Xem tất cả tags của user
const userTags = await zalo.tag.getUserTags(
  accessToken,
  "user-zalo-id"
);

console.log("User tags:", userTags.map(tag => tag.tag_name));
```

### 4. Lấy users theo tag

```typescript
// Lấy tất cả users có tag cụ thể
const taggedUsers = await zalo.tag.getUsersByTag(
  accessToken,
  "VIP_CUSTOMER",
  {
    offset: 0,
    limit: 50
  }
);

console.log(`Found ${taggedUsers.total} VIP customers`);
taggedUsers.data.forEach(user => {
  console.log(`${user.display_name} - ${user.user_id}`);
});
```

---

## Tag Strategies & Use Cases

### 1. Customer Lifecycle Tags

```typescript
class CustomerLifecycleTagging {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async tagUserByLifecycleStage(userId: string, stage: LifecycleStage) {
    // Remove old lifecycle tags
    const currentTags = await this.zalo.tag.getUserTags(this.accessToken, userId);
    const lifecycleTags = currentTags.filter(tag => 
      tag.tag_name.startsWith('LIFECYCLE_')
    ).map(tag => tag.tag_name);

    if (lifecycleTags.length > 0) {
      await this.zalo.tag.untagUser(this.accessToken, userId, lifecycleTags);
    }

    // Add new lifecycle tag
    const newTag = `LIFECYCLE_${stage.toUpperCase()}`;
    await this.zalo.tag.tagUser(this.accessToken, userId, [newTag]);

    // Add complementary tags based on stage
    const complementaryTags = this.getComplementaryTags(stage);
    if (complementaryTags.length > 0) {
      await this.zalo.tag.tagUser(this.accessToken, userId, complementaryTags);
    }
  }

  private getComplementaryTags(stage: LifecycleStage): string[] {
    const tagMappings = {
      'new_lead': ['NURTURING_NEEDED', 'WELCOME_SERIES'],
      'qualified_lead': ['CONSIDERATION_STAGE', 'SALES_READY'],
      'customer': ['ONBOARDING_NEEDED', 'CROSS_SELL_ELIGIBLE'],
      'repeat_customer': ['LOYALTY_PROGRAM', 'REFERRAL_CANDIDATE'],
      'champion': ['VIP_TREATMENT', 'TESTIMONIAL_CANDIDATE'],
      'at_risk': ['RETENTION_CAMPAIGN', 'WIN_BACK_NEEDED'],
      'churned': ['RE_ENGAGEMENT', 'FEEDBACK_SURVEY']
    };

    return tagMappings[stage] || [];
  }

  async autoTagBasedOnPurchaseHistory(userId: string) {
    const userAnalytics = await this.getUserPurchaseAnalytics(userId);
    const tagsToAdd = [];

    // Value-based tags
    if (userAnalytics.totalSpent > 50000000) {
      tagsToAdd.push('HIGH_VALUE_CUSTOMER');
    } else if (userAnalytics.totalSpent > 10000000) {
      tagsToAdd.push('MEDIUM_VALUE_CUSTOMER');
    }

    // Frequency-based tags
    if (userAnalytics.purchaseCount > 10) {
      tagsToAdd.push('FREQUENT_BUYER');
    }

    // Recency-based tags
    const daysSinceLastPurchase = userAnalytics.daysSinceLastPurchase;
    if (daysSinceLastPurchase > 90) {
      tagsToAdd.push('INACTIVE_BUYER');
    } else if (daysSinceLastPurchase <= 30) {
      tagsToAdd.push('ACTIVE_BUYER');
    }

    // Category preferences
    const topCategory = userAnalytics.topPurchaseCategory;
    if (topCategory) {
      tagsToAdd.push(`CATEGORY_${topCategory.toUpperCase()}`);
    }

    if (tagsToAdd.length > 0) {
      await this.zalo.tag.tagUser(this.accessToken, userId, tagsToAdd);
    }
  }
}
```

### 2. Behavioral Tags

```typescript
class BehavioralTagging {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async tagUsersByEngagement() {
    const users = await this.getAllActiveUsers();
    
    for (const user of users) {
      const engagementLevel = await this.calculateEngagementLevel(user.user_id);
      await this.applyEngagementTags(user.user_id, engagementLevel);
    }
  }

  private async calculateEngagementLevel(userId: string): Promise<EngagementLevel> {
    const interactions = await this.zalo.userManagement.getUserInteractions(
      this.accessToken,
      userId,
      { 
        from_time: Date.now() - (30 * 24 * 60 * 60 * 1000), // 30 days
        limit: 1000
      }
    );

    const messagesSent = interactions.filter(i => i.from_user).length;
    const messagesReceived = interactions.filter(i => !i.from_user).length;
    const avgResponseTime = this.calculateAverageResponseTime(interactions);
    
    const score = this.calculateEngagementScore(
      messagesSent,
      messagesReceived, 
      avgResponseTime
    );

    if (score > 80) return 'very_high';
    if (score > 60) return 'high';
    if (score > 40) return 'medium';
    if (score > 20) return 'low';
    return 'very_low';
  }

  private async applyEngagementTags(userId: string, level: EngagementLevel) {
    // Remove old engagement tags
    const engagementTags = [
      'ENGAGEMENT_VERY_HIGH',
      'ENGAGEMENT_HIGH', 
      'ENGAGEMENT_MEDIUM',
      'ENGAGEMENT_LOW',
      'ENGAGEMENT_VERY_LOW'
    ];

    const currentTags = await this.zalo.tag.getUserTags(this.accessToken, userId);
    const oldEngagementTags = currentTags
      .filter(tag => engagementTags.includes(tag.tag_name))
      .map(tag => tag.tag_name);

    if (oldEngagementTags.length > 0) {
      await this.zalo.tag.untagUser(this.accessToken, userId, oldEngagementTags);
    }

    // Add new engagement tag
    const newTag = `ENGAGEMENT_${level.toUpperCase()}`;
    await this.zalo.tag.tagUser(this.accessToken, userId, [newTag]);

    // Add action-based tags
    const actionTags = this.getActionBasedTags(level);
    if (actionTags.length > 0) {
      await this.zalo.tag.tagUser(this.accessToken, userId, actionTags);
    }
  }

  private getActionBasedTags(level: EngagementLevel): string[] {
    const actionMappings = {
      'very_high': ['BRAND_ADVOCATE', 'REFERRAL_READY', 'BETA_TESTER'],
      'high': ['LOYAL_CUSTOMER', 'UPSELL_READY'],
      'medium': ['NURTURING_NEEDED', 'ENGAGEMENT_BOOST'],
      'low': ['RE_ENGAGEMENT_NEEDED'],
      'very_low': ['CHURN_RISK', 'WIN_BACK_CAMPAIGN']
    };

    return actionMappings[level] || [];
  }
}
```

### 3. Campaign Tags

```typescript
class CampaignTagging {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  // Tạo campaign tags tự động
  async createCampaignTags(campaign: Campaign) {
    const campaignTags = [
      `CAMPAIGN_${campaign.id.toUpperCase()}`,
      `CAMPAIGN_${campaign.type.toUpperCase()}`,
      `CAMPAIGN_${campaign.startDate.getFullYear()}_${campaign.startDate.getMonth() + 1}`
    ];

    for (const tagName of campaignTags) {
      try {
        await this.zalo.tag.createTag(this.accessToken, tagName);
        console.log(`Created campaign tag: ${tagName}`);
      } catch (error) {
        if (error.code !== -224) { // Tag already exists
          console.error(`Failed to create tag ${tagName}:`, error);
        }
      }
    }

    return campaignTags;
  }

  // Tag users dựa trên response với campaign
  async tagCampaignResponders(campaignId: string) {
    const campaignMessages = await this.getCampaignMessages(campaignId);
    const responders = new Set<string>();
    const nonResponders = new Set<string>();

    // Identify responders vs non-responders
    for (const message of campaignMessages) {
      const hasResponse = await this.checkUserResponse(message.user_id, message.sent_time);
      
      if (hasResponse) {
        responders.add(message.user_id);
      } else {
        nonResponders.add(message.user_id);
      }
    }

    // Tag responders
    const responderTag = `CAMPAIGN_${campaignId}_RESPONDER`;
    for (const userId of responders) {
      await this.zalo.tag.tagUser(this.accessToken, userId, [responderTag]);
    }

    // Tag non-responders for follow-up
    const nonResponderTag = `CAMPAIGN_${campaignId}_NON_RESPONDER`;
    for (const userId of nonResponders) {
      await this.zalo.tag.tagUser(this.accessToken, userId, [nonResponderTag]);
    }

    return {
      responders: responders.size,
      nonResponders: nonResponders.size,
      responseRate: (responders.size / (responders.size + nonResponders.size) * 100).toFixed(2) + '%'
    };
  }

  // Auto cleanup expired campaign tags
  async cleanupExpiredCampaignTags() {
    const tagList = await this.zalo.tag.getTagList(this.accessToken);
    const campaignTags = tagList.data.filter(tag => 
      tag.tag_name.startsWith('CAMPAIGN_')
    );

    for (const tag of campaignTags) {
      const isExpired = await this.isCampaignExpired(tag.tag_name);
      
      if (isExpired && tag.user_count === 0) {
        await this.zalo.tag.deleteTag(this.accessToken, tag.tag_name);
        console.log(`Deleted expired campaign tag: ${tag.tag_name}`);
      } else if (isExpired) {
        // Move users to archived tag
        const archivedTag = `ARCHIVED_${tag.tag_name}`;
        await this.zalo.tag.createTag(this.accessToken, archivedTag);
        
        const users = await this.zalo.tag.getUsersByTag(this.accessToken, tag.tag_name);
        for (const user of users.data) {
          await this.zalo.tag.tagUser(this.accessToken, user.user_id, [archivedTag]);
          await this.zalo.tag.untagUser(this.accessToken, user.user_id, [tag.tag_name]);
        }
      }
    }
  }
}
```

---

## Advanced Tag Operations

### 1. Bulk Tag Operations

```typescript
class BulkTagOperations {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async bulkTagUsers(
    userIds: string[],
    tagsToAdd: string[],
    tagsToRemove: string[] = []
  ): Promise<BulkTagResult> {
    const results = {
      successful: 0,
      failed: 0,
      errors: [] as Array<{userId: string, error: string}>
    };

    const batchSize = 5; // Process 5 users at a time to avoid rate limits

    for (let i = 0; i < userIds.length; i += batchSize) {
      const batch = userIds.slice(i, i + batchSize);
      
      const promises = batch.map(async (userId) => {
        try {
          // Remove tags first
          if (tagsToRemove.length > 0) {
            await this.zalo.tag.untagUser(this.accessToken, userId, tagsToRemove);
          }
          
          // Add new tags
          if (tagsToAdd.length > 0) {
            await this.zalo.tag.tagUser(this.accessToken, userId, tagsToAdd);
          }
          
          return { userId, success: true };
        } catch (error) {
          return { userId, success: false, error: error.message };
        }
      });

      const batchResults = await Promise.all(promises);
      
      batchResults.forEach(result => {
        if (result.success) {
          results.successful++;
        } else {
          results.failed++;
          results.errors.push({
            userId: result.userId,
            error: result.error || 'Unknown error'
          });
        }
      });

      // Delay between batches
      if (i + batchSize < userIds.length) {
        await this.delay(1000);
      }
    }

    return results;
  }

  async conditionalBulkTagging(
    condition: TagCondition,
    tagsToApply: string[]
  ): Promise<BulkTagResult> {
    const matchingUsers = await this.findUsersMatchingCondition(condition);
    
    console.log(`Found ${matchingUsers.length} users matching condition`);
    
    return this.bulkTagUsers(
      matchingUsers.map(u => u.user_id),
      tagsToApply
    );
  }

  private async findUsersMatchingCondition(condition: TagCondition): Promise<UserProfile[]> {
    const allUsers = await this.getAllUsers();
    const matchingUsers = [];

    for (const user of allUsers) {
      const matches = await this.evaluateCondition(user, condition);
      if (matches) {
        matchingUsers.push(user);
      }
    }

    return matchingUsers;
  }

  private async evaluateCondition(user: UserProfile, condition: TagCondition): Promise<boolean> {
    switch (condition.type) {
      case 'has_tags':
        const userTags = await this.zalo.tag.getUserTags(this.accessToken, user.user_id);
        const userTagNames = userTags.map(tag => tag.tag_name);
        return condition.tags.every(tag => userTagNames.includes(tag));

      case 'missing_tags':
        const currentTags = await this.zalo.tag.getUserTags(this.accessToken, user.user_id);
        const currentTagNames = currentTags.map(tag => tag.tag_name);
        return condition.tags.some(tag => !currentTagNames.includes(tag));

      case 'purchase_amount':
        const analytics = await this.getUserAnalytics(user.user_id);
        return this.evaluateNumericCondition(analytics.totalSpent, condition.operator, condition.value);

      case 'last_interaction':
        const lastInteraction = await this.getLastInteractionDays(user.user_id);
        return this.evaluateNumericCondition(lastInteraction, condition.operator, condition.value);

      default:
        return false;
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}
```

### 2. Smart Tag Suggestions

```typescript
class SmartTagSuggestions {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async suggestTagsForUser(userId: string): Promise<TagSuggestion[]> {
    const userProfile = await this.zalo.userManagement.getUserProfile(
      this.accessToken,
      userId
    );

    const userInteractions = await this.zalo.userManagement.getUserInteractions(
      this.accessToken,
      userId,
      { limit: 500 }
    );

    const suggestions = [];

    // Behavioral suggestions
    const behaviorSuggestions = await this.suggestBehavioralTags(userInteractions);
    suggestions.push(...behaviorSuggestions);

    // Value-based suggestions
    const valueSuggestions = await this.suggestValueBasedTags(userInteractions);
    suggestions.push(...valueSuggestions);

    // Interest-based suggestions
    const interestSuggestions = await this.suggestInterestBasedTags(userInteractions);
    suggestions.push(...interestSuggestions);

    // Lifecycle suggestions
    const lifecycleSuggestions = await this.suggestLifecycleTags(userProfile, userInteractions);
    suggestions.push(...lifecycleSuggestions);

    return suggestions.sort((a, b) => b.confidence - a.confidence).slice(0, 10);
  }

  private async suggestBehavioralTags(interactions: any[]): Promise<TagSuggestion[]> {
    const suggestions = [];

    // Analyze response patterns
    const responseTime = this.calculateAverageResponseTime(interactions);
    if (responseTime < 300000) { // 5 minutes
      suggestions.push({
        tag: 'QUICK_RESPONDER',
        confidence: 0.9,
        reason: 'User responds quickly to messages'
      });
    }

    // Analyze interaction frequency
    const monthlyInteractions = this.countInteractionsInPeriod(interactions, 30);
    if (monthlyInteractions > 20) {
      suggestions.push({
        tag: 'HIGHLY_ENGAGED',
        confidence: 0.8,
        reason: 'High interaction frequency'
      });
    }

    // Analyze message types
    const questionCount = interactions.filter(i => 
      i.message && i.message.includes('?')
    ).length;
    
    if (questionCount > interactions.length * 0.3) {
      suggestions.push({
        tag: 'INQUISITIVE',
        confidence: 0.7,
        reason: 'Asks many questions'
      });
    }

    return suggestions;
  }

  private async suggestValueBasedTags(interactions: any[]): Promise<TagSuggestion[]> {
    const suggestions = [];
    
    const purchases = interactions.filter(i => i.type === 'purchase');
    if (purchases.length === 0) return suggestions;

    const totalValue = purchases.reduce((sum, p) => sum + (p.value || 0), 0);
    const avgOrderValue = totalValue / purchases.length;

    if (totalValue > 50000000) {
      suggestions.push({
        tag: 'HIGH_VALUE_CUSTOMER',
        confidence: 0.95,
        reason: `Total purchases: ${this.formatCurrency(totalValue)}`
      });
    } else if (totalValue > 10000000) {
      suggestions.push({
        tag: 'MEDIUM_VALUE_CUSTOMER', 
        confidence: 0.85,
        reason: `Total purchases: ${this.formatCurrency(totalValue)}`
      });
    }

    if (avgOrderValue > 5000000) {
      suggestions.push({
        tag: 'PREMIUM_BUYER',
        confidence: 0.8,
        reason: `Average order: ${this.formatCurrency(avgOrderValue)}`
      });
    }

    return suggestions;
  }

  async autoApplySuggestedTags(userId: string, minConfidence: number = 0.8) {
    const suggestions = await this.suggestTagsForUser(userId);
    const highConfidenceTags = suggestions
      .filter(s => s.confidence >= minConfidence)
      .map(s => s.tag);

    if (highConfidenceTags.length > 0) {
      await this.zalo.tag.tagUser(this.accessToken, userId, highConfidenceTags);
      
      console.log(`Auto-applied ${highConfidenceTags.length} tags to user ${userId}:`);
      highConfidenceTags.forEach(tag => console.log(`  - ${tag}`));
    }
  }
}
```

---

## Tag Analytics & Insights

### 1. Tag Performance Analytics

```typescript
class TagAnalytics {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async analyzeTagPerformance(tagName: string, period: number = 30): Promise<TagAnalytics> {
    const taggedUsers = await this.zalo.tag.getUsersByTag(
      this.accessToken,
      tagName,
      { limit: 1000 }
    );

    const analytics = {
      totalUsers: taggedUsers.total,
      activeUsers: 0,
      totalInteractions: 0,
      totalRevenue: 0,
      avgEngagement: 0,
      conversionRate: 0,
      retentionRate: 0
    };

    const cutoffTime = Date.now() - (period * 24 * 60 * 60 * 1000);
    let totalEngagement = 0;
    let conversions = 0;

    for (const user of taggedUsers.data) {
      const userAnalytics = await this.zalo.userManagement.getUserAnalytics(
        this.accessToken,
        user.user_id
      );

      const interactions = await this.zalo.userManagement.getUserInteractions(
        this.accessToken,
        user.user_id,
        { from_time: cutoffTime, limit: 1000 }
      );

      if (interactions.length > 0) {
        analytics.activeUsers++;
        analytics.totalInteractions += interactions.length;
        
        const engagement = this.calculateEngagementScore(interactions);
        totalEngagement += engagement;

        // Check for conversions (purchases) in the period
        const purchases = interactions.filter(i => i.type === 'purchase');
        if (purchases.length > 0) {
          conversions++;
          const revenue = purchases.reduce((sum, p) => sum + (p.value || 0), 0);
          analytics.totalRevenue += revenue;
        }
      }
    }

    analytics.avgEngagement = analytics.activeUsers > 0 
      ? totalEngagement / analytics.activeUsers 
      : 0;
    
    analytics.conversionRate = analytics.activeUsers > 0
      ? conversions / analytics.activeUsers
      : 0;

    analytics.retentionRate = analytics.totalUsers > 0
      ? analytics.activeUsers / analytics.totalUsers
      : 0;

    return analytics;
  }

  async compareTagPerformance(tags: string[]): Promise<TagComparisonReport> {
    const comparisons = [];

    for (const tag of tags) {
      const performance = await this.analyzeTagPerformance(tag);
      comparisons.push({
        tag,
        ...performance
      });
    }

    // Sort by total revenue descending
    comparisons.sort((a, b) => b.totalRevenue - a.totalRevenue);

    return {
      period: '30 days',
      comparisons,
      insights: this.generateComparisonInsights(comparisons)
    };
  }

  private generateComparisonInsights(comparisons: any[]): string[] {
    const insights = [];

    // Find best performing tag by revenue
    if (comparisons.length > 0) {
      const topTag = comparisons[0];
      insights.push(`${topTag.tag} is the highest revenue generating segment with ${this.formatCurrency(topTag.totalRevenue)}`);
    }

    // Find most engaged segment
    const mostEngaged = comparisons.reduce((prev, current) => 
      current.avgEngagement > prev.avgEngagement ? current : prev
    );
    insights.push(`${mostEngaged.tag} has the highest engagement with ${mostEngaged.avgEngagement.toFixed(2)} score`);

    // Find best conversion rate
    const bestConversion = comparisons.reduce((prev, current) => 
      current.conversionRate > prev.conversionRate ? current : prev
    );
    insights.push(`${bestConversion.tag} has the best conversion rate at ${(bestConversion.conversionRate * 100).toFixed(2)}%`);

    return insights;
  }

  async generateTagInsights(): Promise<TagInsights> {
    const tagList = await this.zalo.tag.getTagList(this.accessToken);
    
    const insights = {
      totalTags: tagList.total,
      mostPopularTags: [],
      underutilizedTags: [],
      orphanedTags: [],
      recommendedCleanup: []
    };

    // Find most popular tags
    const sortedByUsers = tagList.data.sort((a, b) => b.user_count - a.user_count);
    insights.mostPopularTags = sortedByUsers.slice(0, 10);

    // Find underutilized tags
    insights.underutilizedTags = tagList.data.filter(tag => 
      tag.user_count > 0 && tag.user_count < 5
    );

    // Find orphaned tags (no users)
    insights.orphanedTags = tagList.data.filter(tag => tag.user_count === 0);

    // Recommend cleanup
    insights.recommendedCleanup = [
      ...insights.orphanedTags.map(tag => ({
        tag: tag.tag_name,
        reason: 'No users tagged',
        action: 'Delete'
      })),
      ...insights.underutilizedTags
        .filter(tag => tag.tag_name.includes('CAMPAIGN_'))
        .map(tag => ({
          tag: tag.tag_name,
          reason: 'Old campaign with few users',
          action: 'Archive or delete'
        }))
    ];

    return insights;
  }
}
```

### 2. Tag-based Campaign Analysis

```typescript
class TagCampaignAnalytics {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async analyzeCampaignByTags(
    campaignId: string,
    targetTags: string[]
  ): Promise<CampaignTagAnalytics> {
    const results = new Map<string, TagCampaignMetrics>();

    for (const tag of targetTags) {
      const taggedUsers = await this.zalo.tag.getUsersByTag(
        this.accessToken,
        tag,
        { limit: 1000 }
      );

      const campaignMetrics = await this.analyzeCampaignForSegment(
        campaignId,
        taggedUsers.data
      );

      results.set(tag, {
        segmentSize: taggedUsers.total,
        ...campaignMetrics
      });
    }

    return {
      campaignId,
      tagPerformance: Array.from(results.entries()),
      overallInsights: this.generateCampaignInsights(results)
    };
  }

  private async analyzeCampaignForSegment(
    campaignId: string,
    users: UserProfile[]
  ): Promise<CampaignMetrics> {
    let messagesSent = 0;
    let messagesRead = 0;
    let responses = 0;
    let conversions = 0;
    let totalRevenue = 0;

    for (const user of users) {
      const campaignInteractions = await this.getCampaignInteractionsForUser(
        campaignId,
        user.user_id
      );

      if (campaignInteractions.length > 0) {
        messagesSent++;
        
        // Check if messages were read
        const readMessages = campaignInteractions.filter(i => i.read);
        if (readMessages.length > 0) {
          messagesRead++;
        }

        // Check for responses
        const userResponses = campaignInteractions.filter(i => i.from_user);
        if (userResponses.length > 0) {
          responses++;
        }

        // Check for conversions
        const purchases = campaignInteractions.filter(i => i.type === 'purchase');
        if (purchases.length > 0) {
          conversions++;
          totalRevenue += purchases.reduce((sum, p) => sum + (p.value || 0), 0);
        }
      }
    }

    return {
      messagesSent,
      deliveryRate: messagesSent / users.length,
      readRate: messagesRead / messagesSent,
      responseRate: responses / messagesSent,
      conversionRate: conversions / messagesSent,
      totalRevenue,
      revenuePerUser: totalRevenue / users.length
    };
  }

  async optimizeTagsForCampaign(
    historicalCampaignData: CampaignHistory[]
  ): Promise<TagOptimizationRecommendations> {
    const tagPerformanceMap = new Map<string, TagPerformanceHistory>();

    // Analyze historical performance
    for (const campaign of historicalCampaignData) {
      const tagAnalytics = await this.analyzeCampaignByTags(
        campaign.id,
        campaign.targetTags
      );

      for (const [tag, metrics] of tagAnalytics.tagPerformance) {
        if (!tagPerformanceMap.has(tag)) {
          tagPerformanceMap.set(tag, {
            campaigns: [],
            avgConversionRate: 0,
            avgRevenuePerUser: 0,
            consistency: 0
          });
        }

        const tagHistory = tagPerformanceMap.get(tag)!;
        tagHistory.campaigns.push(metrics);
      }
    }

    // Calculate averages and consistency
    for (const [tag, history] of tagPerformanceMap) {
      const campaigns = history.campaigns;
      history.avgConversionRate = campaigns.reduce((sum, c) => sum + c.conversionRate, 0) / campaigns.length;
      history.avgRevenuePerUser = campaigns.reduce((sum, c) => sum + c.revenuePerUser, 0) / campaigns.length;
      history.consistency = 1 - this.calculateVariance(campaigns.map(c => c.conversionRate));
    }

    // Generate recommendations
    const sortedTags = Array.from(tagPerformanceMap.entries())
      .sort(([,a], [,b]) => b.avgRevenuePerUser - a.avgRevenuePerUser);

    return {
      topPerformingTags: sortedTags.slice(0, 5).map(([tag, perf]) => ({
        tag,
        avgConversionRate: perf.avgConversionRate,
        avgRevenuePerUser: perf.avgRevenuePerUser,
        recommendation: 'Prioritize for high-value campaigns'
      })),
      consistentTags: sortedTags
        .filter(([,perf]) => perf.consistency > 0.8)
        .slice(0, 5)
        .map(([tag, perf]) => ({
          tag,
          consistency: perf.consistency,
          recommendation: 'Reliable for predictable results'
        })),
      underperformingTags: sortedTags
        .slice(-5)
        .map(([tag, perf]) => ({
          tag,
          avgConversionRate: perf.avgConversionRate,
          recommendation: 'Consider re-segmenting or exclusion'
        }))
    };
  }
}
```

---

## Automated Tag Management

### 1. Auto-tagging Rules Engine

```typescript
class AutoTaggingEngine {
  private rules: TaggingRule[] = [];

  constructor(private zalo: ZaloSDK, private accessToken: string) {
    this.initializeDefaultRules();
  }

  addRule(rule: TaggingRule): void {
    this.rules.push({
      ...rule,
      id: this.generateRuleId(),
      createdAt: Date.now()
    });
  }

  async processUser(userId: string): Promise<TaggingResult> {
    const userProfile = await this.zalo.userManagement.getUserProfile(
      this.accessToken,
      userId
    );

    const userAnalytics = await this.zalo.userManagement.getUserAnalytics(
      this.accessToken,
      userId
    );

    const appliedRules = [];
    const tagsAdded = [];
    const tagsRemoved = [];

    for (const rule of this.rules) {
      if (await this.evaluateRule(rule, userProfile, userAnalytics)) {
        const result = await this.applyRule(rule, userId);
        
        appliedRules.push(rule.id);
        tagsAdded.push(...result.tagsAdded);
        tagsRemoved.push(...result.tagsRemoved);
      }
    }

    return {
      userId,
      appliedRules,
      tagsAdded,
      tagsRemoved,
      timestamp: Date.now()
    };
  }

  private async evaluateRule(
    rule: TaggingRule,
    userProfile: UserProfile,
    userAnalytics: any
  ): Promise<boolean> {
    for (const condition of rule.conditions) {
      const result = await this.evaluateCondition(condition, userProfile, userAnalytics);
      
      if (rule.operator === 'AND' && !result) {
        return false;
      } else if (rule.operator === 'OR' && result) {
        return true;
      }
    }

    return rule.operator === 'AND';
  }

  private async evaluateCondition(
    condition: TagCondition,
    userProfile: UserProfile,
    userAnalytics: any
  ): Promise<boolean> {
    switch (condition.type) {
      case 'total_spent':
        return this.compareValues(
          userAnalytics.total_spent || 0,
          condition.operator,
          condition.value
        );

      case 'days_since_last_purchase':
        return this.compareValues(
          userAnalytics.days_since_last_purchase || 999,
          condition.operator,
          condition.value
        );

      case 'engagement_score':
        return this.compareValues(
          userAnalytics.engagement_score || 0,
          condition.operator,
          condition.value
        );

      case 'has_tag':
        const userTags = await this.zalo.tag.getUserTags(this.accessToken, userProfile.user_id);
        const tagNames = userTags.map(tag => tag.tag_name);
        return tagNames.includes(condition.value);

      case 'purchase_category':
        const topCategory = userAnalytics.top_purchase_category;
        return topCategory === condition.value;

      case 'interaction_frequency':
        return this.compareValues(
          userAnalytics.monthly_interactions || 0,
          condition.operator,
          condition.value
        );

      default:
        return false;
    }
  }

  private async applyRule(rule: TaggingRule, userId: string): Promise<RuleApplication> {
    const tagsAdded = [];
    const tagsRemoved = [];

    // Add tags
    if (rule.actions.addTags && rule.actions.addTags.length > 0) {
      await this.zalo.tag.tagUser(this.accessToken, userId, rule.actions.addTags);
      tagsAdded.push(...rule.actions.addTags);
    }

    // Remove tags
    if (rule.actions.removeTags && rule.actions.removeTags.length > 0) {
      await this.zalo.tag.untagUser(this.accessToken, userId, rule.actions.removeTags);
      tagsRemoved.push(...rule.actions.removeTags);
    }

    return { tagsAdded, tagsRemoved };
  }

  private initializeDefaultRules(): void {
    // VIP Customer Rule
    this.addRule({
      name: 'VIP Customer Auto-Tag',
      conditions: [
        { type: 'total_spent', operator: 'gte', value: 50000000 },
        { type: 'engagement_score', operator: 'gte', value: 70 }
      ],
      operator: 'AND',
      actions: {
        addTags: ['VIP_CUSTOMER', 'HIGH_VALUE'],
        removeTags: ['STANDARD_CUSTOMER']
      },
      active: true
    });

    // Churn Risk Rule
    this.addRule({
      name: 'Churn Risk Detection',
      conditions: [
        { type: 'days_since_last_purchase', operator: 'gte', value: 90 },
        { type: 'engagement_score', operator: 'lte', value: 30 }
      ],
      operator: 'AND',
      actions: {
        addTags: ['CHURN_RISK', 'RE_ENGAGEMENT_NEEDED'],
        removeTags: ['ACTIVE_CUSTOMER']
      },
      active: true
    });

    // Frequent Buyer Rule
    this.addRule({
      name: 'Frequent Buyer',
      conditions: [
        { type: 'interaction_frequency', operator: 'gte', value: 15 },
        { type: 'days_since_last_purchase', operator: 'lte', value: 30 }
      ],
      operator: 'AND',
      actions: {
        addTags: ['FREQUENT_BUYER', 'LOYALTY_PROGRAM_ELIGIBLE']
      },
      active: true
    });
  }

  // Batch process all users
  async processBatch(userIds?: string[]): Promise<BatchTaggingResult> {
    const usersToProcess = userIds || await this.getAllActiveUserIds();
    const results = [];
    const errors = [];

    console.log(`Processing ${usersToProcess.length} users with ${this.rules.length} rules`);

    for (const userId of usersToProcess) {
      try {
        const result = await this.processUser(userId);
        results.push(result);
      } catch (error) {
        errors.push({ userId, error: error.message });
      }
    }

    return {
      totalProcessed: usersToProcess.length,
      successful: results.length,
      failed: errors.length,
      results,
      errors
    };
  }
}
```

### 2. Scheduled Tag Maintenance

```typescript
class TagMaintenanceScheduler {
  constructor(private zalo: ZaloSDK, private accessToken: string) {}

  async runDailyMaintenance(): Promise<MaintenanceReport> {
    console.log('Starting daily tag maintenance...');
    
    const tasks = [
      this.cleanupOrphanedTags(),
      this.archiveExpiredCampaignTags(),
      this.updateLifecycleTags(),
      this.refreshEngagementTags(),
      this.validateTagConsistency()
    ];

    const results = await Promise.allSettled(tasks);
    
    return {
      timestamp: Date.now(),
      tasks: results.map((result, index) => ({
        task: ['cleanup', 'archive', 'lifecycle', 'engagement', 'validate'][index],
        status: result.status,
        result: result.status === 'fulfilled' ? result.value : result.reason
      }))
    };
  }

  private async cleanupOrphanedTags(): Promise<CleanupResult> {
    const tagList = await this.zalo.tag.getTagList(this.accessToken);
    const orphanedTags = tagList.data.filter(tag => tag.user_count === 0);
    
    let deleted = 0;
    for (const tag of orphanedTags) {
      try {
        await this.zalo.tag.deleteTag(this.accessToken, tag.tag_name);
        deleted++;
      } catch (error) {
        console.error(`Failed to delete orphaned tag ${tag.tag_name}:`, error);
      }
    }

    return { deleted, total: orphanedTags.length };
  }

  private async archiveExpiredCampaignTags(): Promise<ArchiveResult> {
    const tagList = await this.zalo.tag.getTagList(this.accessToken);
    const campaignTags = tagList.data.filter(tag => 
      tag.tag_name.startsWith('CAMPAIGN_') && 
      this.isCampaignExpired(tag.tag_name)
    );

    let archived = 0;
    for (const tag of campaignTags) {
      try {
        const archivedTagName = `ARCHIVED_${tag.tag_name}`;
        await this.zalo.tag.createTag(this.accessToken, archivedTagName);

        // Move users to archived tag
        const users = await this.zalo.tag.getUsersByTag(this.accessToken, tag.tag_name);
        for (const user of users.data) {
          await this.zalo.tag.tagUser(this.accessToken, user.user_id, [archivedTagName]);
          await this.zalo.tag.untagUser(this.accessToken, user.user_id, [tag.tag_name]);
        }

        await this.zalo.tag.deleteTag(this.accessToken, tag.tag_name);
        archived++;
      } catch (error) {
        console.error(`Failed to archive campaign tag ${tag.tag_name}:`, error);
      }
    }

    return { archived, total: campaignTags.length };
  }

  private async updateLifecycleTags(): Promise<UpdateResult> {
    const lifecycleTagging = new CustomerLifecycleTagging(this.zalo, this.accessToken);
    const allUsers = await this.getAllActiveUsers();
    
    let updated = 0;
    for (const user of allUsers) {
      try {
        const currentStage = await this.determineLifecycleStage(user.user_id);
        await lifecycleTagging.tagUserByLifecycleStage(user.user_id, currentStage);
        updated++;
      } catch (error) {
        console.error(`Failed to update lifecycle tags for user ${user.user_id}:`, error);
      }
    }

    return { updated, total: allUsers.length };
  }

  private isCampaignExpired(tagName: string): boolean {
    // Extract date from campaign tag name
    const dateMatch = tagName.match(/(\d{4})_(\d{1,2})/);
    if (!dateMatch) return false;

    const [, year, month] = dateMatch;
    const campaignDate = new Date(parseInt(year), parseInt(month) - 1);
    const threeMonthsAgo = new Date();
    threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);

    return campaignDate < threeMonthsAgo;
  }
}
```

---

## Best Practices

### 1. Tag Naming Conventions

```typescript
// ✅ Good tag naming patterns
const tagNamingPatterns = {
  // Category_SubCategory_Qualifier
  customer_segments: [
    'CUSTOMER_VIP_DIAMOND',
    'CUSTOMER_VIP_GOLD',
    'CUSTOMER_REGULAR_ACTIVE',
    'CUSTOMER_REGULAR_INACTIVE'
  ],
  
  // Action_Target_Timeframe
  campaigns: [
    'CAMPAIGN_BLACKFRIDAY_2024',
    'CAMPAIGN_NEWPRODUCT_Q1',
    'CAMPAIGN_RETENTION_MONTHLY'
  ],
  
  // Behavior_Level_Context
  engagement: [
    'ENGAGEMENT_HIGH_RECENT',
    'ENGAGEMENT_LOW_ATRISK',
    'ENGAGEMENT_NONE_CHURNED'
  ],
  
  // Lifecycle_Stage_Status
  lifecycle: [
    'LIFECYCLE_PROSPECT_NEW',
    'LIFECYCLE_CUSTOMER_ACTIVE',
    'LIFECYCLE_CUSTOMER_REPEAT'
  ]
};

// ❌ Avoid these patterns
const badTagNames = [
  'tag1', 'customers', 'important', 'temp', 'test'
];
```

### 2. Tag Architecture Best Practices

```typescript
class TagArchitectureBestPractices {
  // Use hierarchical tagging
  static getTagHierarchy(): TagHierarchy {
    return {
      'CUSTOMER': {
        'CUSTOMER_VALUE': ['HIGH', 'MEDIUM', 'LOW'],
        'CUSTOMER_STATUS': ['ACTIVE', 'INACTIVE', 'CHURNED'],
        'CUSTOMER_TYPE': ['B2B', 'B2C', 'ENTERPRISE']
      },
      'ENGAGEMENT': {
        'ENGAGEMENT_LEVEL': ['VERY_HIGH', 'HIGH', 'MEDIUM', 'LOW'],
        'ENGAGEMENT_CHANNEL': ['ZALO', 'EMAIL', 'SMS', 'PHONE']
      },
      'LIFECYCLE': {
        'LIFECYCLE_STAGE': ['AWARENESS', 'CONSIDERATION', 'PURCHASE', 'RETENTION', 'ADVOCACY']
      }
    };
  }

  // Implement mutual exclusivity where appropriate
  static getMutuallyExclusiveTags(): MutuallyExclusiveGroups {
    return [
      ['CUSTOMER_VALUE_HIGH', 'CUSTOMER_VALUE_MEDIUM', 'CUSTOMER_VALUE_LOW'],
      ['LIFECYCLE_AWARENESS', 'LIFECYCLE_CONSIDERATION', 'LIFECYCLE_PURCHASE', 'LIFECYCLE_RETENTION'],
      ['ENGAGEMENT_VERY_HIGH', 'ENGAGEMENT_HIGH', 'ENGAGEMENT_MEDIUM', 'ENGAGEMENT_LOW']
    ];
  }

  // Tag governance rules
  static getTagGovernanceRules(): TagGovernanceRules {
    return {
      maxTagsPerUser: 25,
      requireApprovalForTags: ['VIP_*', 'ENTERPRISE_*'],
      autoExpirationTags: ['CAMPAIGN_*', 'PROMOTION_*'],
      protectedTags: ['SYSTEM_*', 'ADMIN_*'],
      requiredTags: ['LIFECYCLE_*', 'CUSTOMER_VALUE_*']
    };
  }
}
```

---

## Testing Tag Management

### 1. Unit Tests

```typescript
// tag-management.test.ts
import { ZaloSDK } from '@warriorteam/redai-zalo-sdk';

describe('Tag Management', () => {
  const zalo = new ZaloSDK({
    appId: 'test_app_id',
    appSecret: 'test_app_secret'
  });

  it('should create and delete tags', async () => {
    const mockTagResponse = {
      error: 0,
      message: 'Success',
      data: { tag_id: 'test_tag_id', tag_name: 'TEST_TAG' }
    };

    jest.spyOn(zalo.tag, 'createTag').mockResolvedValue(mockTagResponse);
    jest.spyOn(zalo.tag, 'deleteTag').mockResolvedValue({ error: 0 });

    // Test tag creation
    const result = await zalo.tag.createTag('test_token', 'TEST_TAG');
    expect(result.tag_name).toBe('TEST_TAG');

    // Test tag deletion
    await expect(zalo.tag.deleteTag('test_token', 'TEST_TAG')).resolves.not.toThrow();
  });

  it('should tag and untag users', async () => {
    jest.spyOn(zalo.tag, 'tagUser').mockResolvedValue({ error: 0 });
    jest.spyOn(zalo.tag, 'untagUser').mockResolvedValue({ error: 0 });

    await expect(zalo.tag.tagUser('test_token', 'user123', ['TAG1', 'TAG2'])).resolves.not.toThrow();
    await expect(zalo.tag.untagUser('test_token', 'user123', ['TAG1'])).resolves.not.toThrow();
  });
});
```

---

## Troubleshooting

### Common Issues

**Q: "Tag already exists" error**
```
A: Kiểm tra danh sách tags trước khi tạo mới.
   Sử dụng try-catch để handle lỗi này gracefully.
```

**Q: User không được tag thành công**
```
A: Kiểm tra:
   - User ID có hợp lệ không
   - User có còn follow OA không  
   - Tag name có đúng format không
   - Có đủ quyền để tag user không
```

**Q: Bulk tagging bị rate limited**
```
A: Implement batch processing với delays:
   - Process 5-10 users per batch
   - Add 1-2 second delays between batches
   - Implement exponential backoff for retries
```

---

## Next Steps

Sau khi nắm vững Tag Management:

1. **[Video Upload](./VIDEO_UPLOAD.md)** - Upload và manage media content
2. **[Error Handling](./ERROR_HANDLING.md)** - Comprehensive error handling  
3. **[Group Management](./GROUP_MANAGEMENT.md)** - Zalo group operations
4. **[Webhook Events](./WEBHOOK_EVENTS.md)** - Real-time event processing

Tham khảo **[API Reference](./API_REFERENCE.md)** để biết chi tiết về tất cả tag management methods.