import { FSWatcher } from 'fs';
import { Team, TeamMember, Project, Ticket, TicketFilter, ScheduledCheck, ScheduledMessage, MessageDeliveryLog } from '../../types/index.js';
import { type AgentStatus, type WorkingStatus, type RuntimeType } from '../../constants.js';
/**
 * Storage-level events emitted after a team is persisted or removed.
 * Subscribers (e.g. {@link TeamTriggerReconciler}) use these to converge
 * side systems without pulling StorageService into a cross-cutting tangle.
 */
export type StorageEvent = {
    kind: 'team-saved';
    team: Team;
} | {
    kind: 'team-deleted';
    teamId: string;
};
export type StorageListener = (event: StorageEvent) => Promise<void> | void;
export declare class StorageService {
    private static instance;
    private static instanceHome;
    private crewlyHome;
    /** @deprecated Use teamsDir instead - kept for migration */
    private teamsFile;
    /** Directory containing individual team files */
    private teamsDir;
    /** Orchestrator status file */
    private orchestratorFile;
    private projectsFile;
    private runtimeFile;
    private scheduledMessagesFile;
    private deliveryLogsFile;
    private recurringChecksFile;
    private oneTimeChecksFile;
    private logger;
    /** Flag to track if migration has been performed */
    private migrationDone;
    /** Debounce timer for teams backup to avoid I/O storms */
    private backupDebounceTimer;
    private readonly BACKUP_DEBOUNCE_MS;
    /**
     * Listeners registered via {@link onStorageEvent}. Empty by default so
     * existing call-sites pay zero cost; listeners are fired-and-forgotten
     * to keep save/delete latency unchanged.
     */
    private listeners;
    constructor(crewlyHome?: string);
    /**
     * Get singleton instance of StorageService to prevent multiple instances
     * from interfering with each other's file operations
     */
    static getInstance(crewlyHome?: string): StorageService;
    /**
     * Clear singleton instance (useful for testing)
     */
    static clearInstance(): void;
    /**
     * Get the crewly home directory path.
     * Used by SchedulerService to locate orphaned temp files for cleanup (#217).
     *
     * @returns Absolute path to the crewly home directory (e.g. ~/.crewly)
     */
    getCrewlyHome(): string;
    /**
     * Boot-time state invariant check (C1 of the 2026-05-03 persistence-fix
     * spec). Compares the current on-disk team count to the latest backup
     * snapshot. If the live state is empty but a non-empty backup exists,
     * the invariant has been violated — the backend is refusing to boot
     * rather than serving an "empty teams" UI that the user might "fix"
     * by re-creating teams against the wiped state.
     *
     * Operators can override via `CREWLY_FORCE_EMPTY_BOOT=1` env var for
     * legitimate first-boot or explicit-reset scenarios.
     *
     * Best-effort: failures inside the backup-status read path log a
     * warning and do NOT block boot — the invariant should never become a
     * boot-time hard dependency itself.
     *
     * @throws {StateInvariantViolation} when live teams are empty but the
     *   most-recent backup snapshot has data, and the override env var
     *   is not set.
     */
    verifyStateInvariantOnBoot(): Promise<void>;
    /**
     * Ensures the crewly home and teams directories exist, creating them if necessary
     */
    private ensureDirectories;
    /**
     * Get the directory path for a team.
     *
     * @param teamId - The team ID
     * @returns Path to the team directory
     */
    getTeamDir(teamId: string): string;
    /**
     * Get the prompts directory path for a team.
     *
     * @param teamId - The team ID
     * @returns Path to the team's prompts directory
     */
    getTeamPromptsDir(teamId: string): string;
    /**
     * Get the prompt file path for a team member.
     *
     * @param teamId - The team ID
     * @param memberId - The member ID
     * @returns Path to the member's prompt file
     */
    getMemberPromptPath(teamId: string, memberId: string): string;
    /**
     * Get the orchestrator prompt file path.
     *
     * @returns Path to the orchestrator's prompt file
     */
    getOrchestratorPromptPath(): string;
    /**
     * Migrate from old storage formats to new directory structure.
     * Handles: teams.json -> teams/{team-id}/config.json
     *          teams/{team-id}.json -> teams/{team-id}/config.json
     *          teams/orchestrator.json -> teams/orchestrator/config.json
     */
    private migrateFromLegacyTeamsFile;
    /**
     * Migrate flat team files (teams/{team-id}.json) to directory structure (teams/{team-id}/config.json).
     */
    private migrateFlatTeamFiles;
    /**
     * Migrate a team to the new directory structure.
     * Creates teams/{team-id}/config.json and teams/{team-id}/prompts/{member-id}.md
     */
    private migrateTeamToDirectory;
    /**
     * Creates default orchestrator configuration object
     * @returns Default orchestrator settings with inactive status
     */
    private createDefaultOrchestrator;
    /**
     * Ensures a storage file exists and is valid JSON, creating or recovering it if needed
     * @param filePath - Path to the storage file
     * @param defaultContent - Default content to use if file needs to be created/recovered
     */
    private ensureFile;
    /**
     * Get all teams from team directories.
     * Each team is stored as teams/{teamId}/config.json for isolation.
     *
     * @returns Array of all teams
     */
    getTeams(): Promise<Team[]>;
    /**
     * Save a team to its directory structure.
     * Each team is stored as teams/{teamId}/config.json for isolation and resilience.
     * Member prompts are saved to teams/{teamId}/prompts/{memberId}.md
     *
     * @param team - The team to save
     */
    saveTeam(team: Team): Promise<void>;
    /**
     * Save a member's prompt file.
     *
     * @param teamId - The team ID
     * @param memberId - The member ID
     * @param prompt - The prompt content
     */
    saveMemberPrompt(teamId: string, memberId: string, prompt: string): Promise<void>;
    /**
     * Get a member's prompt from file.
     *
     * @param teamId - The team ID
     * @param memberId - The member ID
     * @returns The prompt content or null if not found
     */
    getMemberPrompt(teamId: string, memberId: string): Promise<string | null>;
    /**
     * Find a team member by their ID across all teams.
     *
     * @param memberId - Member UUID
     * @returns Member object or null if not found
     */
    getMemberById(memberId: string): Promise<TeamMember | null>;
    /**
     * Find a team member by their session name.
     *
     * @param sessionName - The session name to search for
     * @returns Object with team and member, or null if not found
     */
    findMemberBySessionName(sessionName: string): Promise<{
        team: Team;
        member: TeamMember;
    } | null>;
    /**
     * Save the orchestrator's prompt file.
     *
     * @param prompt - The prompt content
     */
    saveOrchestratorPrompt(prompt: string): Promise<void>;
    /**
     * Get the orchestrator's prompt from file.
     *
     * @returns The prompt content or null if not found
     */
    getOrchestratorPrompt(): Promise<string | null>;
    /**
     * Delete a team by removing its directory.
     *
     * @param id - The team ID to delete
     */
    deleteTeam(id: string): Promise<void>;
    /**
     * Register a listener for storage lifecycle events. Returns an unsubscribe
     * function. Listeners are invoked fire-and-forget; errors are logged and
     * never propagated to the save/delete caller.
     *
     * @param listener - Callback invoked for every saved/deleted team
     * @returns Unsubscribe function
     */
    onStorageEvent(listener: StorageListener): () => void;
    /**
     * Fire all registered listeners without awaiting their completion. Any
     * error inside a listener is isolated to that listener.
     */
    private emit;
    getProjects(): Promise<Project[]>;
    addProject(projectPath: string): Promise<Project>;
    saveProject(project: Project): Promise<void>;
    deleteProject(id: string): Promise<void>;
    getTickets(projectPath: string, filter?: TicketFilter): Promise<Ticket[]>;
    saveTicket(projectPath: string, ticket: Ticket): Promise<void>;
    deleteTicket(projectPath: string, ticketId: string): Promise<void>;
    private parseTicketYAML;
    watchProject(projectPath: string): FSWatcher;
    getRuntimeState(): Promise<any>;
    saveRuntimeState(state: any): Promise<void>;
    /**
     * Create template files for a new project
     */
    private createProjectTemplateFiles;
    getScheduledMessages(): Promise<ScheduledMessage[]>;
    saveScheduledMessage(scheduledMessage: ScheduledMessage): Promise<void>;
    getScheduledMessage(id: string): Promise<ScheduledMessage | undefined>;
    deleteScheduledMessage(id: string): Promise<boolean>;
    /**
     * Get all persisted recurring checks.
     *
     * @returns Array of persisted ScheduledCheck entries
     */
    getRecurringChecks(): Promise<ScheduledCheck[]>;
    /**
     * Save or update a recurring check entry on disk.
     *
     * @param check - The ScheduledCheck to persist
     */
    saveRecurringCheck(check: ScheduledCheck): Promise<void>;
    /**
     * Delete a persisted recurring check by ID.
     *
     * @param id - The check ID to delete
     * @returns true if the check was found and deleted
     */
    deleteRecurringCheck(id: string): Promise<boolean>;
    /**
     * Clear all persisted recurring checks.
     */
    clearRecurringChecks(): Promise<void>;
    /**
     * Get all persisted one-time checks.
     *
     * @returns Array of persisted ScheduledCheck entries
     */
    getOneTimeChecks(): Promise<ScheduledCheck[]>;
    /**
     * Save or update a one-time check entry on disk.
     *
     * @param check - The ScheduledCheck to persist
     */
    saveOneTimeCheck(check: ScheduledCheck): Promise<void>;
    /**
     * Delete a persisted one-time check by ID.
     *
     * @param id - The check ID to delete
     * @returns true if the check was found and deleted
     */
    deleteOneTimeCheck(id: string): Promise<boolean>;
    /**
     * Clear all persisted one-time checks.
     */
    clearOneTimeChecks(): Promise<void>;
    getDeliveryLogs(): Promise<MessageDeliveryLog[]>;
    saveDeliveryLog(log: MessageDeliveryLog): Promise<void>;
    clearDeliveryLogs(): Promise<void>;
    /**
     * Get orchestrator status from dedicated orchestrator.json file.
     *
     * @returns Orchestrator status object or null if not found
     */
    getOrchestratorStatus(): Promise<{
        sessionName: string;
        agentStatus: AgentStatus;
        workingStatus: WorkingStatus;
        runtimeType: RuntimeType;
        modelId?: string;
        createdAt: string;
        updatedAt: string;
    } | null>;
    /**
     * Update agent status for orchestrator or any team member.
     * Orchestrator status is stored in teams/orchestrator.json.
     * Team member status is stored in the respective team's file.
     *
     * @param sessionName - Session name of the agent (CREWLY_CONSTANTS.SESSIONS.ORCHESTRATOR_NAME for orchestrator)
     * @param status - New agent status (CREWLY_CONSTANTS.AGENT_STATUSES.INACTIVE | CREWLY_CONSTANTS.AGENT_STATUSES.ACTIVATING | CREWLY_CONSTANTS.AGENT_STATUSES.ACTIVE)
     */
    updateAgentStatus(sessionName: string, status: AgentStatus, dropoutReason?: TeamMember['dropoutReason']): Promise<void>;
    /**
     * Update team member runtime type
     */
    updateTeamMemberRuntimeType(teamId: string, memberId: string, runtimeType: RuntimeType): Promise<void>;
    /**
     * Update orchestrator runtime type in teams/orchestrator.json.
     *
     * @param runtimeType - The runtime type to set
     */
    updateOrchestratorRuntimeType(runtimeType: RuntimeType): Promise<void>;
    /**
     * Update orchestrator model ID in teams/orchestrator.json.
     *
     * The orchestrator's `modelId` controls which AI model the in-process
     * Crewly Agent runtime uses (e.g. `deepseek/deepseek-chat`,
     * `google/gemini-3-flash-preview`). Pass `undefined` to clear the field
     * and fall back to {@link CREWLY_AGENT_DEFAULTS}.DEFAULT_MODEL.
     *
     * Other orchestrator fields (runtimeType, agentStatus, workingStatus) are
     * preserved by reading the existing config first and merging.
     *
     * @param modelId - Format: "provider/modelId" (e.g. "google/gemini-3-flash-preview"), or undefined to clear
     */
    updateOrchestratorModelId(modelId: string | undefined): Promise<void>;
    /**
     * Update the teams backup file asynchronously with debouncing.
     *
     * When many teams are saved in rapid succession (e.g. ActivityMonitor
     * marking 14 stale agents), each saveTeam() call used to trigger a
     * full getTeams() + backup, flooding the libuv thread pool with I/O
     * and starving other requests. Debouncing collapses the burst into a
     * single backup operation.
     */
    private updateTeamsBackup;
    /**
     * @deprecated Use updateAgentStatus instead
     */
    updateOrchestratorStatus(status: string): Promise<void>;
}
//# sourceMappingURL=storage.service.d.ts.map