# Article Management Service

The `ArticleService` and `VideoUploadService` provide comprehensive article management capabilities for Zalo Official Account (OA).

## Features

### ArticleService
- ✅ Create normal articles with rich content
- ✅ Create video articles
- ✅ Update existing articles
- ✅ **Update article status only** (new feature)
- ✅ Get article details and lists
- ✅ Remove articles
- ✅ Track article creation/update progress
- ✅ Comprehensive validation

### VideoUploadService
- ✅ Upload video files for articles
- ✅ Check video processing status
- ✅ Wait for upload completion with polling
- ✅ Upload videos from URLs
- ✅ Get video information

## Prerequisites

1. **OA Requirements:**
   - OA must have permission to create articles
   - Access token must have "manage_article" scope
   - OA must have active status and be verified

2. **Content Limits:**
   - Title: max 150 characters
   - Author: max 50 characters (normal articles only)
   - Description: max 300 characters
   - Image size: max 1MB per image
   - Video size: max 50MB per video
   - Video formats: MP4, AVI only

## Usage

### Initialize SDK

```typescript
import { ZaloSDK } from 'redai-zalo-sdk';

const sdk = new ZaloSDK({
  appId: 'your_app_id',
  appSecret: 'your_app_secret',
});

const article = sdk.article;
const videoUpload = sdk.videoUpload;
```

### 1. Create Normal Article

```typescript
import { ArticleType, ArticleStatus, CommentStatus, CoverType, BodyItemType } from 'redai-zalo-sdk';

const normalArticle = {
  type: ArticleType.NORMAL,
  title: "Welcome to Our Service",
  author: "RedAI Team",
  description: "This is a comprehensive guide to our new features",
  cover: {
    cover_type: CoverType.PHOTO,
    photo_url: "https://example.com/cover.jpg",
    status: ArticleStatus.SHOW
  },
  body: [
    {
      type: BodyItemType.TEXT,
      content: "Welcome to our new service! Here's what you need to know..."
    },
    {
      type: BodyItemType.IMAGE,
      url: "https://example.com/image1.jpg"
    },
    {
      type: BodyItemType.TEXT,
      content: "For more information, please contact us."
    }
  ],
  tracking_link: "https://your-website.com/tracking",
  status: ArticleStatus.SHOW,
  comment: CommentStatus.SHOW
};

const result = await article.createNormalArticle(accessToken, normalArticle);
console.log(`Article creation token: ${result.token}`);
```

### 2. Create Video Article

```typescript
const videoArticle = {
  type: ArticleType.VIDEO,
  title: "Product Demo Video",
  description: "Watch our latest product demonstration",
  video_id: "your_video_id", // Get from video upload
  avatar: "https://example.com/thumbnail.jpg",
  status: ArticleStatus.SHOW,
  comment: CommentStatus.SHOW
};

const result = await article.createVideoArticle(accessToken, videoArticle);
console.log(`Video article creation token: ${result.token}`);
```

### 3. Upload Video for Articles

```typescript
// Upload video file
const videoFile = new File([videoBuffer], 'demo.mp4', { type: 'video/mp4' });
const uploadResult = await videoUpload.uploadVideo(accessToken, videoFile);

// Wait for processing completion
const finalResult = await videoUpload.waitForUploadCompletion(
  accessToken, 
  uploadResult.token,
  5 * 60 * 1000, // 5 minutes timeout
  5 * 1000       // 5 seconds polling interval
);

console.log(`Video ID: ${finalResult.video_id}`);
```

### 4. Upload Video from URL

```typescript
const uploadResult = await videoUpload.uploadVideoFromUrl(
  accessToken,
  'https://example.com/video.mp4'
);

const finalResult = await videoUpload.waitForUploadCompletion(
  accessToken,
  uploadResult.token
);
```

### 5. Check Article Creation Progress

```typescript
const progress = await article.checkArticleProcess(accessToken, token);

switch (progress.status) {
  case 'processing':
    console.log('Article is still being processed...');
    break;
  case 'success':
    console.log(`Article created with ID: ${progress.article_id}`);
    break;
  case 'failed':
    console.error(`Article creation failed: ${progress.error_message}`);
    break;
}
```

### 6. Get Article Details

```typescript
const articleDetail = await article.getArticleDetail(accessToken, articleId);
console.log('Article details:', articleDetail);
```

### 7. Get Article List

```typescript
const articleList = await article.getArticleList(accessToken, {
  offset: 0,
  limit: 20,
  type: 'normal' // or 'video'
});

console.log(`Found ${articleList.data.total} articles`);
articleList.data.articles.forEach(article => {
  console.log(`- ${article.title} (${article.total_view} views)`);
});
```

### 8. Update Article

```typescript
const updateData = {
  id: articleId,
  type: ArticleType.NORMAL,
  title: "Updated Article Title",
  author: "Updated Author",
  description: "Updated description",
  cover: {
    cover_type: CoverType.PHOTO,
    photo_url: "https://example.com/new-cover.jpg",
    status: ArticleStatus.SHOW
  },
  body: [
    {
      type: BodyItemType.TEXT,
      content: "Updated content..."
    }
  ],
  status: ArticleStatus.SHOW
};

const updateResult = await article.updateNormalArticle(accessToken, updateData);
console.log(`Update token: ${updateResult.token}`);
```

### 9. Remove Article

```typescript
const removeResult = await article.removeArticle(accessToken, articleId);
console.log(removeResult.message);
```

### 10. Check Video Status

```typescript
const videoStatus = await videoUpload.checkVideoStatus(accessToken, token);

console.log(`Status: ${videoStatus.status}`);
console.log(`Progress: ${videoStatus.convert_percent}%`);

if (videoStatus.video_id) {
  console.log(`Video ID: ${videoStatus.video_id}`);
}
```

## Error Handling

```typescript
import { ZaloSDKError } from 'redai-zalo-sdk';

try {
  const result = await article.createNormalArticle(accessToken, articleData);
  console.log('Success:', result);
} catch (error) {
  if (error instanceof ZaloSDKError) {
    console.error('Zalo API Error:', error.message, error.code);
    console.error('Details:', error.details);
  } else {
    console.error('Unexpected error:', error);
  }
}
```

## Complete Example

```typescript
import { ZaloSDK, ArticleType, CoverType, BodyItemType, ArticleStatus } from 'redai-zalo-sdk';

class ArticleManager {
  private sdk: ZaloSDK;
  
  constructor() {
    this.sdk = new ZaloSDK({
      appId: process.env.ZALO_APP_ID!,
      appSecret: process.env.ZALO_APP_SECRET!,
    });
  }

  async createCompleteArticle(accessToken: string) {
    try {
      // 1. Upload video first (if needed)
      const videoFile = new File([videoBuffer], 'demo.mp4');
      const videoUpload = await this.sdk.videoUpload.uploadVideo(accessToken, videoFile);
      const videoResult = await this.sdk.videoUpload.waitForUploadCompletion(
        accessToken, 
        videoUpload.token
      );

      // 2. Create article with video
      const article = await this.sdk.article.createNormalArticle(accessToken, {
        type: ArticleType.NORMAL,
        title: "Complete Guide",
        author: "RedAI Team",
        description: "A complete guide with video content",
        cover: {
          cover_type: CoverType.PHOTO,
          photo_url: "https://example.com/cover.jpg",
          status: ArticleStatus.SHOW
        },
        body: [
          {
            type: BodyItemType.TEXT,
            content: "Introduction to our service..."
          },
          {
            type: BodyItemType.VIDEO,
            video_id: videoResult.video_id
          },
          {
            type: BodyItemType.TEXT,
            content: "Thank you for watching!"
          }
        ],
        status: ArticleStatus.SHOW
      });

      // 3. Wait for article creation
      const articleProgress = await this.sdk.article.checkArticleProcess(
        accessToken, 
        article.token
      );

      return articleProgress;
    } catch (error) {
      console.error('Article creation error:', error);
      throw error;
    }
  }
}
```

### Update Article Status

Update only the status of an existing article without modifying other content:

```typescript
import { ArticleStatus, CommentStatus } from 'redai-zalo-sdk';

// Update article status to 'show' (publish)
const publishResult = await article.updateArticleStatus(
  accessToken,
  'article_id',
  ArticleStatus.SHOW
);

// Update article status to 'hide' and disable comments
const hideResult = await article.updateArticleStatus(
  accessToken,
  'article_id',
  ArticleStatus.HIDE,
  CommentStatus.HIDE
);

// Check update progress
const progress = await article.checkArticleProcess(
  accessToken,
  publishResult.token
);
```

**Method Signature:**
```typescript
updateArticleStatus(
  accessToken: string,
  articleId: string,
  status: ArticleStatus,
  comment?: CommentStatus
): Promise<ArticleUpdateResponse>
```

**Parameters:**
- `accessToken`: OA access token
- `articleId`: ID of the article to update
- `status`: New article status (`'show'` or `'hide'`)
- `comment`: Optional comment status (`'show'` or `'hide'`)

**How it works:**
1. Retrieves current article details using `getArticleDetail()`
2. Preserves all existing content (title, body, cover, etc.)
3. Updates only the status and optionally comment status
4. Calls the appropriate update method based on article type

**Benefits:**
- ✅ Simple status-only updates without content modification
- ✅ Preserves all existing article data
- ✅ Automatic type detection (normal vs video articles)
- ✅ Optional comment status control
- ✅ Same progress tracking as full updates

## Validation Rules

### Normal Articles
- Title: required, max 150 characters
- Author: required, max 50 characters
- Description: required, max 300 characters
- Cover: required, must specify type and corresponding URL/ID
- Body: required, at least one item

### Video Articles
- Title: required, max 150 characters
- Description: required, max 300 characters
- Video ID: required, must be valid uploaded video
- Avatar: required, thumbnail URL

### Video Files
- Formats: MP4, AVI only
- Size: max 50MB
- Processing: asynchronous, use polling to check status

## API Reference

For detailed API documentation, see the TypeScript definitions in the source code. All methods include comprehensive JSDoc comments with parameter descriptions and return types.
