# LuaJob

The `LuaJob` class defines scheduled tasks that execute automatically at specific times or intervals. Use jobs for recurring operations, one-time tasks, and automated workflows.

## Import

```typescript
import { LuaJob } from 'lua-cli';
```

## Overview

Jobs enable:
- Schedule tasks with cron expressions
- Run one-time tasks at specific times
- Execute tasks at fixed intervals
- Access metadata and user context
- Configure timeouts and retries
- Automate recurring operations

## Class Definition

```typescript
class LuaJob {
  constructor(config: LuaJobConfig);
  getName(): string;
  getDescription(): string;
  getSchedule(): JobSchedule;
  getTimeout(): number;
  getRetry(): { maxAttempts: number; backoffSeconds?: number } | undefined;
  getMetadata(): Record<string, any> | undefined;
  execute(job: JobInstance): Promise<any>;
}
```

## Configuration

```typescript
interface LuaJobConfig {
  /** Job name (optional, defaults to 'unnamed-job') */
  name?: string;

  /** Short description of the job (1-2 sentences) */
  description: string;

  /** Schedule configuration - cron, once, or interval */
  schedule: JobSchedule;

  /** Function that executes the job logic */
  execute: (job: JobInstance) => Promise<any>;

  /** Optional timeout in seconds (default: 300) */
  timeout?: number;

  /** Optional retry configuration */
  retry?: {
    maxAttempts: number;
    backoffSeconds?: number;
  };

  /** Optional metadata for the job */
  metadata?: Record<string, any>;
}
```

## Schedule Types

### Cron Schedule

```typescript
{
  type: 'cron',
  expression: string,  // Cron expression
  timezone?: string    // IANA timezone (default: UTC)
}
```

**Cron Expression Format:**
```
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday = 0)
│ │ │ │ │
* * * * *
```

**Examples:**
- `'0 9 * * *'` - Every day at 9:00 AM
- `'0 */6 * * *'` - Every 6 hours
- `'0 9 * * 1'` - Every Monday at 9:00 AM
- `'*/15 * * * *'` - Every 15 minutes
- `'0 0 1 * *'` - First day of every month at midnight

### Once Schedule

```typescript
{
  type: 'once',
  executeAt: Date | string  // When to run (ISO string or Date)
}
```

### Interval Schedule

```typescript
{
  type: 'interval',
  seconds: number  // Seconds between executions
}
```

## JobInstance

The `execute` function receives a `JobInstance`:

```typescript
interface JobInstance {
  /** Get the user who triggered the job */
  user(): Promise<UserDataInstance>;

  /** Access job metadata */
  metadata: Record<string, any>;

  /** Update job metadata */
  updateMetadata(data: Record<string, any>): Promise<void>;

  /** Delete the job */
  delete(): Promise<void>;
}
```

## Complete Examples

### Daily Cleanup Job

```typescript
import { LuaJob, Data } from 'lua-cli';

export const dailyCleanup = new LuaJob({
  name: 'daily-cleanup',
  description: 'Daily cleanup of old data',

  schedule: {
    type: 'cron',
    expression: '0 2 * * *',  // 2 AM every day
    timezone: 'America/New_York'
  },

  timeout: 600,  // 10 minutes

  retry: {
    maxAttempts: 3,
    backoffSeconds: 60
  },

  metadata: {
    retentionDays: 30
  },

  execute: async (job) => {
    const retentionDays = job.metadata.retentionDays || 30;
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - retentionDays);

    // Clean up old temporary data
    const oldEntries = await Data.get('temp_data');
    let deletedCount = 0;

    for (const entry of oldEntries) {
      const createdAt = new Date(entry.data.createdAt);
      if (createdAt < cutoffDate) {
        await Data.delete('temp_data', entry.id);
        deletedCount++;
      }
    }

    // Update job metadata with stats
    await job.updateMetadata({
      lastRun: new Date().toISOString(),
      deletedCount,
      nextRetentionCheck: new Date(cutoffDate).toISOString()
    });

    return {
      success: true,
      deletedCount,
      retentionDays
    };
  }
});
```

### Weekly Report Job

```typescript
import { LuaJob, User, Data, AI } from 'lua-cli';

export const weeklyReport = new LuaJob({
  name: 'weekly-report',
  description: 'Generate and send weekly activity report',

  schedule: {
    type: 'cron',
    expression: '0 9 * * 1',  // Every Monday at 9 AM
    timezone: 'UTC'
  },

  execute: async (job) => {
    const user = await job.user();

    // Get activity data for the past week
    const activities = await Data.get('user_activities');
    const weekAgo = new Date();
    weekAgo.setDate(weekAgo.getDate() - 7);

    const recentActivities = activities.filter(a =>
      new Date(a.data.timestamp) > weekAgo
    );

    // Generate AI summary
    const summary = await AI.generate(
      'Summarize this week\'s activity in 2-3 bullet points.',
      [{
        type: 'text',
        text: JSON.stringify(recentActivities.map(a => a.data))
      }]
    );

    // Send report to user
    await user.send([{
      type: 'text',
      text: `📊 Weekly Activity Report\n\n${summary}\n\nTotal activities: ${recentActivities.length}`
    }]);

    return {
      success: true,
      activitiesCount: recentActivities.length
    };
  }
});
```

### System Health Check

```typescript
import { LuaJob, Data } from 'lua-cli';

export const healthCheck = new LuaJob({
  name: 'health-check',
  description: 'System health monitoring',

  schedule: {
    type: 'interval',
    seconds: 300  // Every 5 minutes
  },

  timeout: 30,  // 30 seconds

  execute: async (job) => {
    const checks = {
      database: false,
      api: false,
      storage: false
    };

    // Check database
    try {
      await Data.get('health_check');
      checks.database = true;
    } catch (error) {
      console.error('Database check failed:', error);
    }

    // Check external API
    try {
      const response = await fetch(process.env.API_URL!);
      checks.api = response.ok;
    } catch (error) {
      console.error('API check failed:', error);
    }

    // Check storage
    try {
      await Data.create('health_check', {
        timestamp: new Date().toISOString(),
        status: 'ok'
      });
      checks.storage = true;
    } catch (error) {
      console.error('Storage check failed:', error);
    }

    const allHealthy = Object.values(checks).every(c => c);

    // Log health status
    await Data.create('health_logs', {
      timestamp: new Date().toISOString(),
      checks,
      healthy: allHealthy
    });

    // Alert if unhealthy
    if (!allHealthy) {
      const user = await job.user();
      await user.send([{
        type: 'text',
        text: `⚠️ System health check failed!\n${JSON.stringify(checks, null, 2)}`
      }]);
    }

    return {
      success: true,
      healthy: allHealthy,
      checks
    };
  }
});
```

### Birthday Reminder Job

```typescript
import { LuaJob, Data, User } from 'lua-cli';

export const birthdayReminder = new LuaJob({
  name: 'birthday-reminder',
  description: 'Send birthday reminders',

  schedule: {
    type: 'cron',
    expression: '0 9 * * *',  // Every day at 9 AM
  },

  execute: async (job) => {
    const today = new Date();
    const todayStr = `${today.getMonth() + 1}-${today.getDate()}`;

    // Get all users with birthdays
    const users = await Data.get('users');
    let remindersSent = 0;

    for (const userEntry of users) {
      const birthday = userEntry.data.birthday;
      if (!birthday) continue;

      // Check if birthday is today
      const birthdayDate = new Date(birthday);
      const birthdayStr = `${birthdayDate.getMonth() + 1}-${birthdayDate.getDate()}`;

      if (birthdayStr === todayStr) {
        // Send birthday message
        const user = await User.get(userEntry.data.userId);
        await user.send([{
          type: 'text',
          text: `🎉 Happy Birthday, ${userEntry.data.name}! Wishing you an amazing day!`
        }]);

        remindersSent++;
      }
    }

    return {
      success: true,
      remindersSent,
      date: today.toISOString()
    };
  }
});
```

### Data Sync Job

```typescript
import { LuaJob, Data } from 'lua-cli';
import axios from 'axios';

export const dataSyncJob = new LuaJob({
  name: 'data-sync',
  description: 'Sync data with external system',

  schedule: {
    type: 'interval',
    seconds: 3600  // Every hour
  },

  retry: {
    maxAttempts: 5,
    backoffSeconds: 300  // 5 minutes
  },

  metadata: {
    lastSyncId: null,
    totalSynced: 0
  },

  execute: async (job) => {
    const apiUrl = process.env.EXTERNAL_API_URL!;
    const apiKey = process.env.EXTERNAL_API_KEY!;

    try {
      // Fetch updates since last sync
      const response = await axios.get(`${apiUrl}/updates`, {
        headers: { 'Authorization': `Bearer ${apiKey}` },
        params: { since: job.metadata.lastSyncId }
      });

      const updates = response.data;
      let syncedCount = 0;

      // Process each update
      for (const update of updates) {
        await Data.create('synced_data', {
          externalId: update.id,
          data: update.data,
          syncedAt: new Date().toISOString()
        });
        syncedCount++;
      }

      // Update job metadata
      const lastId = updates.length > 0 ? updates[updates.length - 1].id : job.metadata.lastSyncId;
      await job.updateMetadata({
        lastSyncId: lastId,
        totalSynced: job.metadata.totalSynced + syncedCount,
        lastSyncAt: new Date().toISOString()
      });

      return {
        success: true,
        syncedCount,
        lastId
      };
    } catch (error) {
      console.error('Sync failed:', error);
      throw error;  // Will trigger retry
    }
  }
});
```

### One-Time Welcome Email

```typescript
import { LuaJob, User } from 'lua-cli';

export const sendWelcomeEmail = new LuaJob({
  name: 'welcome-email',
  description: 'Send welcome email to new user',

  schedule: {
    type: 'once',
    executeAt: new Date(Date.now() + 3600000)  // 1 hour from now
  },

  metadata: {
    userId: 'user_123',
    userName: 'John Doe'
  },

  execute: async (job) => {
    const user = await User.get(job.metadata.userId);

    await user.send([{
      type: 'text',
      text: `Welcome to our platform, ${job.metadata.userName}! 🎉\n\nWe're excited to have you here. Here are some things you can do:\n\n• Explore our features\n• Complete your profile\n• Connect with the community\n\nIf you have any questions, just ask!`
    }]);

    // Delete job after execution (one-time only)
    await job.delete();

    return {
      success: true,
      userId: job.metadata.userId
    };
  }
});
```

## Best Practices

### ✅ DO

- Use appropriate schedule types for your use case
- Set reasonable timeouts for long-running jobs
- Configure retries for critical jobs
- Store important data in job metadata
- Log job execution results
- Handle errors gracefully
- Use timezones for user-facing scheduled jobs
- Clean up one-time jobs after execution

### ❌ DON'T

- Don't use very short intervals (< 1 minute) without good reason
- Don't perform blocking operations
- Don't forget to handle timezone differences
- Don't ignore job execution failures
- Don't store large data in metadata
- Don't create jobs without timeout limits
- Don't skip error logging

## Cron Expression Examples

```typescript
// Every minute
'* * * * *'

// Every 5 minutes
'*/5 * * * *'

// Every hour at minute 0
'0 * * * *'

// Every day at midnight
'0 0 * * *'

// Every day at 3:30 PM
'30 15 * * *'

// Every Monday at 9 AM
'0 9 * * 1'

// Every weekday at 6 PM
'0 18 * * 1-5'

// First day of every month
'0 0 1 * *'

// Last day of every month (not directly supported, use workaround)
'0 0 28-31 * *'

// Every 6 hours
'0 */6 * * *'
```

## Error Handling

```typescript
export const robustJob = new LuaJob({
  name: 'robust-job',
  description: 'Job with comprehensive error handling',

  schedule: {
    type: 'cron',
    expression: '0 * * * *'  // Every hour
  },

  retry: {
    maxAttempts: 3,
    backoffSeconds: 60
  },

  execute: async (job) => {
    try {
      // Perform job logic
      const result = await performTask();

      // Update success metadata
      await job.updateMetadata({
        lastSuccess: new Date().toISOString(),
        consecutiveFailures: 0
      });

      return {
        success: true,
        result
      };
    } catch (error) {
      // Track failures
      const failures = (job.metadata.consecutiveFailures || 0) + 1;
      await job.updateMetadata({
        lastFailure: new Date().toISOString(),
        lastError: error.message,
        consecutiveFailures: failures
      });

      // Alert after 3 consecutive failures
      if (failures >= 3) {
        const user = await job.user();
        await user.send([{
          type: 'text',
          text: `⚠️ Job "${job.metadata.name}" has failed 3 times in a row. Last error: ${error.message}`
        }]);
      }

      throw error;  // Re-throw to trigger retry
    }
  }
});
```

## Related APIs

- [Jobs API](./Jobs.md) - Dynamic job creation from tools
- [User API](./User.md) - Access user context in jobs
- [Data API](./Data.md) - Store job results and logs

## See Also

- [Cron Expression Reference](https://crontab.guru/)
- [Job Scheduling Guide](../guides/JobScheduling.md)
- [Automation Patterns](../guides/Automation.md)
