/**
 * Sync Engine — orchestrates push/pull operations between Stoneforge and external services
 *
 * The sync engine coordinates bidirectional synchronization between Stoneforge elements
 * and external services (GitHub, Linear, etc.) using the provider registry and adapters.
 *
 * Core operations:
 * - push(): Push locally-changed linked elements to external services
 * - pull(): Pull externally-changed items into Stoneforge
 * - sync(): Bidirectional sync (push then pull)
 *
 * Change detection:
 * - Push: Query events since lastPushedAt, filter to elements with _externalSync metadata,
 *   compare content hash against lastPushedHash
 * - Pull: Call adapter.listIssuesSince() using global sync cursor from settings,
 *   compare against lastPulledHash
 *
 * Usage:
 * ```typescript
 * import { createSyncEngine } from '@stoneforge/quarry';
 *
 * const engine = createSyncEngine({ api, registry, settings });
 * const result = await engine.push({ all: true });
 * const result = await engine.pull();
 * const result = await engine.sync({ dryRun: true });
 * ```
 */
import type { ExternalSyncResult, ExternalTask, ExternalDocument, ConflictStrategy, SyncAdapterType, ProviderConfig } from '@stoneforge/core';
import type { Element, ElementId, Timestamp, EventFilter, Dependency, DependencyType } from '@stoneforge/core';
import type { ProviderRegistry } from './provider-registry.js';
/**
 * Options for sync operations (push, pull, sync)
 */
export interface SyncOptions {
    /** Report what would change without making changes */
    readonly dryRun?: boolean;
    /** Sync specific tasks only (by element ID) */
    readonly taskIds?: readonly string[];
    /** Sync all linked tasks */
    readonly all?: boolean;
    /** Force push all linked tasks, skipping hash comparison and event query guards */
    readonly force?: boolean;
    /**
     * Filter sync operations to specific adapter types.
     * When set, only elements matching these adapter types will be processed.
     * When omitted or empty, all adapter types are processed (tasks + documents).
     */
    readonly adapterTypes?: readonly SyncAdapterType[];
    /**
     * Include documents that are not in any library.
     * By default, documents without a library parent are excluded from push.
     * Set to true to include them.
     */
    readonly includeNoLibrary?: boolean;
    /**
     * Callback invoked with the total element count before processing begins.
     * Useful for showing progress information or warnings about large sets.
     */
    readonly onBeforeProcess?: (count: number) => void;
    /**
     * Optional progress callback invoked after each batch completes during push.
     * Receives the current count of processed elements and the total count.
     */
    readonly onProgress?: (current: number, total: number) => void;
}
/**
 * Minimal QuarryAPI interface — only the methods the sync engine needs.
 * Avoids coupling to the full QuarryAPI type.
 */
export interface SyncEngineAPI {
    /** Get a single element by ID */
    get<T extends Element>(id: ElementId): Promise<T | null>;
    /** List elements matching a filter */
    list<T extends Element>(filter?: Record<string, unknown>): Promise<T[]>;
    /** Update an element */
    update<T extends Element>(id: ElementId, updates: Partial<T>): Promise<T>;
    /** Create a new element */
    create<T extends Element>(input: Record<string, unknown>): Promise<T>;
    /** List events matching a filter */
    listEvents(filter?: EventFilter): Promise<Array<{
        elementId: ElementId;
        eventType: string;
        createdAt: Timestamp;
    }>>;
    /** Get all dependencies for an element, optionally filtered by type */
    getDependencies(id: ElementId, types?: DependencyType[]): Promise<Dependency[]>;
}
/**
 * Minimal settings interface for sync cursor storage.
 * Uses the generic getSetting/setSetting pattern.
 */
export interface SyncEngineSettings {
    /** Get a setting value by key */
    getSetting(key: string): {
        value: unknown;
    } | undefined;
    /** Set a setting value by key */
    setSetting(key: string, value: unknown): {
        value: unknown;
    };
}
/**
 * Conflict resolver interface — resolves conflicts when both local and remote changed.
 * The actual implementation is in conflict-resolver.ts (separate task).
 * The sync engine uses this interface to decouple from the resolver implementation.
 */
export interface SyncConflictResolver {
    /**
     * Resolve a conflict between local and remote versions.
     *
     * @param localElement - The local Stoneforge element
     * @param remoteItem - The external task/document/item (must have updatedAt)
     * @param strategy - The conflict strategy to apply
     * @returns Resolution result indicating which side wins, or 'manual' for unresolved
     */
    resolve(localElement: Element, remoteItem: ExternalTask | ExternalDocument, strategy: ConflictStrategy): ConflictResolution;
}
/**
 * Result of conflict resolution
 */
export interface ConflictResolution {
    /** Which side won ('local', 'remote', or 'manual' for unresolved) */
    readonly winner: 'local' | 'remote' | 'manual';
    /** Whether the conflict was auto-resolved */
    readonly resolved: boolean;
}
/**
 * Configuration for the sync engine
 */
export interface SyncEngineConfig {
    /** QuarryAPI instance for element CRUD + events */
    readonly api: SyncEngineAPI;
    /** Provider registry for looking up providers and adapters */
    readonly registry: ProviderRegistry;
    /** Settings service for sync cursor storage */
    readonly settings?: SyncEngineSettings;
    /** Conflict resolver (optional — defaults to last-write-wins) */
    readonly conflictResolver?: SyncConflictResolver;
    /** Default conflict strategy */
    readonly defaultConflictStrategy?: ConflictStrategy;
    /** Default provider config (for pull — specifies which provider/project to sync) */
    readonly providerConfigs?: readonly ProviderConfig[];
}
/** Number of documents to push concurrently (balances throughput vs API rate limits) */
declare const PUSH_CONCURRENCY = 3;
/**
 * Sync Engine — coordinates push/pull operations between Stoneforge and external services.
 *
 * The engine is stateless — all state is stored in element metadata (_externalSync)
 * and settings (sync cursors). Each operation reads current state, computes changes,
 * and writes updated state.
 */
export declare class SyncEngine {
    private readonly api;
    private readonly registry;
    private readonly settings;
    private readonly conflictResolver;
    private readonly defaultStrategy;
    private readonly providerConfigs;
    constructor(config: SyncEngineConfig);
    /**
     * Push locally-changed linked elements to external services.
     *
     * Algorithm:
     * 1. Find elements with _externalSync metadata
     * 2. For each element, query events since lastPushedAt
     * 3. Compare current content hash against lastPushedHash
     * 4. If hash differs, push changes to external service via adapter
     * 5. Update _externalSync metadata with new timestamp and hash
     *
     * @param options - Push options (dryRun, taskIds, all)
     * @returns Aggregated sync result across all providers
     */
    push(options?: SyncOptions): Promise<ExternalSyncResult>;
    /**
     * Process a single element for push — validates sync state, checks skip conditions,
     * and routes to the appropriate push method (pushElement or pushDocument).
     *
     * Extracted from the push() loop body to enable concurrent execution via Promise.allSettled.
     *
     * @returns 'pushed' if changes were sent, 'skipped' if element was skipped
     */
    private pushSingleElement;
    /**
     * Push a single element to its external service.
     *
     * @returns 'pushed' if changes were sent, 'skipped' if no changes detected
     */
    private pushElement;
    /**
     * Push a single document to its external service.
     *
     * Parallel to pushElement(), but uses document-specific conversion
     * (documentToExternalDocumentInput) and the DocumentSyncAdapter.
     *
     * @returns 'pushed' if changes were sent, 'skipped' if no changes detected
     */
    private pushDocument;
    /**
     * Pull externally-changed items into Stoneforge.
     *
     * Algorithm:
     * 1. For each configured provider, get the sync cursor (last poll timestamp)
     * 2. Call adapter.listIssuesSince(project, cursor) to find changed items
     * 3. For each changed item:
     *    a. If linked to a local element, compare against lastPulledHash
     *    b. If unlinked and options.all is set, create a new Stoneforge task
     *    c. If both local and remote changed, use conflict resolver
     * 4. Update sync cursors and element metadata
     *
     * @param options - Pull options (dryRun, taskIds, all)
     * @returns Aggregated sync result
     */
    pull(options?: SyncOptions): Promise<ExternalSyncResult>;
    /**
     * Pull changes from a specific provider.
     */
    private pullFromProvider;
    /**
     * Pull a single external item into Stoneforge.
     *
     * @returns 'pulled' if local element was updated, 'skipped' if no changes,
     *          'created' if a new task was created
     */
    private pullItem;
    /**
     * Pull document changes from a specific provider.
     * Parallel to pullFromProvider(), but uses the DocumentSyncAdapter.
     */
    private pullDocumentsFromProvider;
    /**
     * Pull a single external document into Stoneforge.
     * Parallel to pullItem(), but uses document-specific conversion.
     *
     * @returns 'pulled' if local element was updated, 'skipped' if no changes,
     *          'created' if a new document was created
     */
    private pullDocumentItem;
    /**
     * Create a new Stoneforge document from an unlinked external document.
     */
    private createDocumentFromExternal;
    /**
     * Bidirectional sync — push then pull.
     *
     * Runs push first to send local changes, then pull to receive remote changes.
     * Results are merged from both operations.
     *
     * @param options - Sync options (dryRun, taskIds, all)
     * @returns Merged sync result
     */
    sync(options?: SyncOptions): Promise<ExternalSyncResult>;
    /**
     * Find elements that are linked to external services and match the given options.
     *
     * - If taskIds is specified, returns only those tasks (that have _externalSync)
     * - If all is true, returns all tasks with _externalSync metadata
     * - Otherwise, returns tasks with _externalSync that have been updated
     */
    private findLinkedElements;
    /**
     * Find elements linked to a specific provider and project.
     */
    private findLinkedElementsForProvider;
    /**
     * Convert an ExternalTask to partial updates for applying to a local element.
     *
     * Delegates to externalTaskToTaskUpdates() from task-sync-adapter.ts, which
     * uses the provider's TaskSyncFieldMapConfig for correct status, priority,
     * taskType, and tag mapping. The provider is looked up from item.provider.
     *
     * @param item - The external task to convert
     * @param existingTask - The existing local task (for diff mode), or undefined
     * @returns Partial<Task> with the mapped fields
     */
    private externalItemToUpdates;
    /**
     * Create a new Stoneforge task from an unlinked external item.
     */
    private createTaskFromExternal;
    /**
     * Sync the description (body) from an external item to the local task's
     * description document.
     *
     * - If the task already has a descriptionRef, compare the body against the
     *   existing document content and update only if changed.
     * - If the task has no descriptionRef and the body is non-empty, create a
     *   new document and return the descriptionRef to link.
     * - If the body is empty/undefined, skip (don't delete existing descriptions).
     *
     * @returns Partial update with descriptionRef if a new document was created,
     *          or empty object if no descriptionRef change is needed.
     */
    private syncDescriptionFromExternal;
    /**
     * Create a new description document for a task.
     *
     * Uses the same pattern as the server-side task creation:
     * - contentType: 'markdown'
     * - category: 'task-description' tag
     * - createdBy: 'system'
     *
     * @param body - The description content
     * @param taskTitle - The task title (used in the document title)
     * @returns The DocumentId of the created document
     */
    private createDescriptionDocument;
    /**
     * Get the sync cursor for a provider+project+adapterType.
     * Returns a timestamp indicating the last time we polled this combination.
     */
    private getSyncCursor;
    /**
     * Update the sync cursor for a provider+project+adapterType.
     */
    private setSyncCursor;
    /**
     * Get a TaskSyncAdapter for a given provider name.
     */
    private getTaskAdapter;
    /**
     * Get a DocumentSyncAdapter for a given provider name.
     */
    private getDocumentAdapter;
    /**
     * Get the ProviderConfig for a given provider name.
     */
    private getProviderConfig;
    /**
     * Get the primary provider name (for result reporting).
     */
    private getPrimaryProvider;
    /**
     * Get the primary project (for result reporting).
     */
    private getPrimaryProject;
}
/**
 * Create a new SyncEngine instance.
 *
 * @param config - Sync engine configuration
 * @returns A new SyncEngine instance
 *
 * @example
 * ```typescript
 * const engine = createSyncEngine({
 *   api: quarryApi,
 *   registry: providerRegistry,
 *   settings: settingsService,
 * });
 *
 * // Push locally-changed tasks
 * const pushResult = await engine.push({ all: true });
 *
 * // Pull externally-changed items
 * const pullResult = await engine.pull();
 *
 * // Bidirectional sync
 * const syncResult = await engine.sync({ dryRun: true });
 * ```
 */
export declare function createSyncEngine(config: SyncEngineConfig): SyncEngine;
export { PUSH_CONCURRENCY };
//# sourceMappingURL=sync-engine.d.ts.map