/**
 * Cron Task Service
 *
 * Manages user-defined recurring tasks that run on cron schedules.
 * Unlike scheduled messages (agent-created/cancellable), cron tasks
 * can only be cancelled by user or orchestrator.
 *
 * Storage: Per-team at ~/.crewly/teams/{teamId}/cron-tasks.json
 * Migration: Global ~/.crewly/cron-tasks.json is auto-migrated on first startup.
 *
 * @module services/workflow/cron-task.service
 */
import type { CronTask, CreateCronTaskRequest, UpdateCronTaskRequest } from '../../types/cron-task.types.js';
/**
 * Extract date/time parts from a Date in a specific IANA timezone.
 *
 * Uses Intl.DateTimeFormat to convert the UTC instant into the target
 * timezone, then extracts numeric minute, hour, dayOfWeek, dayOfMonth,
 * and month fields.
 *
 * @param date - The Date object (represents a UTC instant)
 * @param tz - IANA timezone string (e.g. "America/New_York", "Asia/Shanghai")
 * @returns Object with minute, hour, dayOfWeek (0=Sun), dayOfMonth, month (1-12)
 */
export declare function getDatePartsInTimezone(date: Date, tz: string): {
    minute: number;
    hour: number;
    dayOfWeek: number;
    dayOfMonth: number;
    month: number;
};
/**
 * Validate an IANA timezone, falling back to UTC for an invalid/empty one.
 *
 * `new Intl.DateTimeFormat(..., { timeZone })` THROWS `RangeError` on a bad
 * tz. That throw was previously uncaught inside `getNextRunTime`'s minute scan,
 * so a single cron task with a malformed/empty timezone would crash the whole
 * `evaluateTasks` tick and stall EVERY cron. Defaulting to UTC (with a one-time
 * warn) keeps the scheduler resilient to one bad task.
 *
 * @param tz - Candidate IANA timezone
 * @returns The tz if valid, otherwise 'UTC'
 */
export declare function safeTimezone(tz: string | undefined | null): string;
/**
 * Parse a single cron field spec into a set of allowed values.
 * Supports: `*`, single number, ranges (`1-5`), lists (`1,3,5`),
 * and step values (`*\/5`, `10-30/5`).
 *
 * @param spec - The cron field string (e.g. "*", "5", "1-5", "1,3,5", "*\/10")
 * @param min - Minimum allowed value for this field (inclusive)
 * @param max - Maximum allowed value for this field (inclusive)
 * @returns Set of matching integer values, or null for wildcard (match any)
 */
export declare function parseCronField(spec: string, min: number, max: number): Set<number> | null;
/**
 * Parse a cron expression and calculate the next run time.
 * Supports standard 5-field cron: minute hour day-of-month month day-of-week.
 * Each field supports: `*`, numbers, ranges (`1-5`), lists (`1,3,5`),
 * and step values (`*\/5`).
 *
 * All field matching is done in the specified timezone so that a cron like
 * "0 9 * * *" in Asia/Shanghai fires at 09:00 Shanghai time regardless of
 * the server's local timezone.
 *
 * @param cronExpression - Standard 5-field cron expression
 * @param timezone - IANA timezone (e.g. "UTC", "America/New_York")
 * @param after - Calculate next run after this date (defaults to now)
 * @returns ISO string of next run time (always UTC)
 */
export declare function getNextRunTime(cronExpression: string, timezone: string, after?: Date): string;
/**
 * Callback type for checking whether an agent is online.
 * Should return true if the agent is reachable and can receive tasks.
 *
 * @param sessionName - The agent's PTY session name
 * @param teamId - The team ID the agent belongs to
 * @returns True if the agent is online and ready
 */
export type AgentStatusCallback = (sessionName: string, teamId: string) => Promise<boolean>;
/**
 * Callback type for auto-starting an offline agent.
 *
 * @param sessionName - The agent's PTY session name
 * @param teamId - The team ID the agent belongs to
 * @returns True if the agent was successfully started
 */
export type AgentStartCallback = (sessionName: string, teamId: string) => Promise<boolean>;
/**
 * Service for managing cron tasks with per-team scoped storage.
 *
 * Storage layout:
 *   ~/.crewly/teams/{teamId}/cron-tasks.json
 *
 * On first startup, migrates the legacy global file
 * (~/.crewly/cron-tasks.json) by grouping tasks by targetTeamId
 * and writing per-team files. The global file is renamed to
 * cron-tasks.json.migrated to prevent re-migration.
 *
 * Lifecycle:
 * 1. Get singleton via `getInstance()`
 * 2. Wire callbacks: `setExecutionCallback()`, `setAgentStatusCallback()`, `setAgentStartCallback()`
 * 3. Call `migrateGlobalToTeamScoped()` to auto-migrate legacy storage
 * 4. Call `start()` to begin the evaluation loop
 * 5. On startup, call `recalculateAllNextRunTimes()` to self-heal stale values
 */
export declare class CronTaskService {
    private static instance;
    private logger;
    /** Root crewly home directory (e.g. ~/.crewly) */
    private crewlyHome;
    private timer;
    private executionCallback;
    private agentStatusCallback;
    private agentStartCallback;
    constructor(crewlyHome?: string);
    /**
     * Get singleton instance.
     */
    static getInstance(): CronTaskService;
    /**
     * Reset singleton (for testing).
     */
    static resetInstance(): void;
    /**
     * Get the store file path for a specific team.
     *
     * @param teamId - Team ID
     * @returns Absolute path to the team's cron-tasks.json
     */
    getStoreFile(teamId: string): string;
    /**
     * Get the legacy global store file path.
     *
     * @returns Absolute path to the global cron-tasks.json
     */
    private getGlobalStoreFile;
    /**
     * Migrate legacy global cron-tasks.json to per-team files.
     *
     * Orchestrator-targeted tasks (targetTeamId === 'orchestrator') are kept
     * in the global file since they don't belong to any specific team.
     * All other tasks are grouped by targetTeamId and written to per-team files.
     *
     * After migration, the global file is rewritten to contain only orchestrator
     * tasks, and a .migrated marker is created to prevent re-running.
     *
     * Idempotent: skips if the .migrated marker already exists or no global file exists.
     *
     * @returns Number of tasks migrated to per-team files (excludes orchestrator tasks kept in global)
     */
    migrateGlobalToTeamScoped(): Promise<number>;
    /**
     * Set the callback invoked when a cron task is due for execution.
     * The callback receives the task and should delegate it to the target agent.
     *
     * @param callback - Async function that executes a cron task
     */
    setExecutionCallback(callback: (task: CronTask) => Promise<void>): void;
    /**
     * Set the callback for checking agent online status.
     * Called before executing a task to verify the target agent is reachable.
     * Should treat both 'active' and 'started' states as online.
     *
     * @param callback - Async function returning true if agent is online
     */
    setAgentStatusCallback(callback: AgentStatusCallback): void;
    /**
     * Set the callback for auto-starting an offline agent.
     * Called when a cron task fires but the target agent is offline.
     *
     * @param callback - Async function that starts an agent, returns true on success
     */
    setAgentStartCallback(callback: AgentStartCallback): void;
    /**
     * Start the cron task evaluation loop.
     * Checks every minute for tasks whose nextRunAt has passed.
     */
    start(): void;
    /**
     * Stop the cron task evaluation loop.
     */
    stop(): void;
    /**
     * Whether the service is running.
     */
    isRunning(): boolean;
    /**
     * Recalculate nextRunAt for all enabled tasks using the stored timezone.
     * This self-heals stale values from older versions that computed nextRunAt
     * in the server's local timezone instead of the task's specified timezone.
     *
     * Should be called once on backend startup, before start().
     *
     * @returns Number of tasks whose nextRunAt was updated
     */
    recalculateAllNextRunTimes(): Promise<number>;
    /**
     * Create a new cron task.
     *
     * @param request - Cron task creation parameters
     * @returns The created cron task
     */
    create(request: CreateCronTaskRequest): Promise<CronTask>;
    /**
     * List all cron tasks across all teams, optionally filtered.
     *
     * @param filter - Optional filter criteria
     * @returns Array of matching cron tasks
     */
    list(filter?: {
        targetAgent?: string;
        enabled?: boolean;
    }): Promise<CronTask[]>;
    /**
     * List cron tasks for a specific team.
     *
     * @param teamId - Team ID to filter by
     * @param filter - Optional additional filter criteria
     * @returns Array of matching cron tasks for the team
     */
    getByTeam(teamId: string, filter?: {
        targetAgent?: string;
        enabled?: boolean;
    }): Promise<CronTask[]>;
    /**
     * Get a single cron task by ID. Searches across all teams.
     *
     * @param id - Cron task ID
     * @returns The cron task or null
     */
    get(id: string): Promise<CronTask | null>;
    /**
     * Update a cron task. Searches across all teams to find the task.
     *
     * @param id - Cron task ID
     * @param updates - Fields to update
     * @returns Updated task or null if not found
     */
    update(id: string, updates: UpdateCronTaskRequest): Promise<CronTask | null>;
    /**
     * Delete a cron task. Searches across all teams to find and remove it.
     *
     * @param id - Cron task ID
     * @returns true if deleted, false if not found
     */
    delete(id: string): Promise<boolean>;
    /**
     * Evaluate all enabled tasks and execute those whose nextRunAt has passed.
     * If an agentStatusCallback is set, checks whether the target agent is online
     * before execution. If the agent is offline and an agentStartCallback is set,
     * attempts to auto-start the agent before executing.
     */
    evaluateTasks(): Promise<void>;
    /**
     * Evaluate a single cron task: check if due, verify agent status, execute.
     * Mutates the task in-place (lastRunAt, nextRunAt).
     *
     * @param task - The cron task to evaluate
     * @param now - Current time for comparison
     * @returns true if the task was modified (needs saving)
     */
    private evaluateSingleTask;
    /**
     * Load cron task store for a specific team.
     *
     * @param teamId - Team ID
     * @returns Team's cron task store (empty if file doesn't exist)
     */
    private loadTeamStore;
    /**
     * Save cron task store for a specific team.
     *
     * @param teamId - Team ID
     * @param store - Store data to persist
     */
    private saveTeamStore;
    /**
     * Load the global cron task store (orchestrator tasks only).
     *
     * @returns Global store (empty if file doesn't exist)
     */
    private loadGlobalStore;
    /**
     * Save the global cron task store (orchestrator tasks only).
     *
     * @param store - Store data to persist
     */
    private saveGlobalStore;
    /**
     * Load all tasks from all team stores plus the global store,
     * aggregated into a single array.
     *
     * @returns All cron tasks across all teams and the global orchestrator store
     */
    private loadAllTasks;
    /**
     * Discover all team IDs that have cron task files.
     *
     * Scans ~/.crewly/teams/ for directories containing cron-tasks.json.
     *
     * @returns Array of team IDs
     */
    private getTeamIds;
}
//# sourceMappingURL=cron-task.service.d.ts.map