/**
 * Conflict Detection and Resolution Module
 *
 * Detects conflicts during external sync (both local and remote changed since
 * last sync) and resolves them using configurable strategies:
 *
 * - last_write_wins: Compare updatedAt timestamps, most recent wins
 * - local_wins: Stoneforge version always wins
 * - remote_wins: External version always wins
 * - manual: Tag element with sync-conflict for human resolution
 *
 * Supports field-level merge: when different fields changed on each side,
 * both changes are applied automatically. Only same-field changes are conflicts.
 *
 * @module conflict-resolver
 */
import type { Element, Timestamp, ConflictStrategy, ExternalSyncState, ExternalSyncConflict, ExternalTask, TaskFieldMapConfig } from '@stoneforge/core';
/**
 * Information about a detected conflict between local and remote versions.
 * Includes details about which fields changed on each side.
 */
export interface ConflictInfo {
    /** Local element ID */
    readonly elementId: string;
    /** External item ID */
    readonly externalId: string;
    /** Provider name */
    readonly provider: string;
    /** Project/repository */
    readonly project: string;
    /** Local element's updatedAt timestamp */
    readonly localUpdatedAt: Timestamp;
    /** Remote item's updatedAt timestamp */
    readonly remoteUpdatedAt: string;
    /** Fields that changed locally (by localField name) */
    readonly localChangedFields: readonly string[];
    /** Fields that changed remotely (by externalField name) */
    readonly remoteChangedFields: readonly string[];
    /** Fields that changed on BOTH sides (true conflict) */
    readonly conflictingFields: readonly string[];
    /** Whether field-level merge can resolve some changes automatically */
    readonly canFieldMerge: boolean;
}
/**
 * Result of resolving a conflict. Contains the changes to apply
 * to the local element and/or the external item.
 */
export interface ResolvedChanges {
    /** The conflict that was resolved */
    readonly conflict: ConflictInfo;
    /** Strategy used for resolution */
    readonly strategy: ConflictStrategy;
    /** Whether the conflict was fully resolved (false if manual) */
    readonly resolved: boolean;
    /** Which side won, if resolved automatically */
    readonly winner?: 'local' | 'remote' | 'merged';
    /** Field values to apply to the local element (from remote or merged) */
    readonly localUpdates?: Record<string, unknown>;
    /** Field values to push to the external system (from local or merged) */
    readonly remoteUpdates?: Record<string, unknown>;
    /** For manual resolution: both versions stored for human review */
    readonly manualConflict?: {
        readonly local: Record<string, unknown>;
        readonly remote: Record<string, unknown>;
    };
}
/**
 * Options for conflict detection, allowing callers to provide
 * field-level change context.
 */
export interface DetectConflictOptions {
    /** Field map config for field-level analysis (optional) */
    readonly fieldMapConfig?: TaskFieldMapConfig;
    /** Snapshot of local element at last push (for field-level diff) */
    readonly localBaseline?: Record<string, unknown>;
    /** Snapshot of remote item at last pull (for field-level diff) */
    readonly remoteBaseline?: Record<string, unknown>;
}
/**
 * Detect whether a conflict exists between a local element and a remote item.
 *
 * A conflict exists when BOTH of these are true:
 * - Local changed: current element content hash differs from lastPushedHash
 * - Remote changed: current external item hash differs from lastPulledHash
 *
 * If a fieldMapConfig is provided along with baselines, performs field-level
 * analysis to determine which specific fields conflict.
 *
 * @param localElement - The current local element
 * @param remoteItem - The current external item
 * @param syncState - The element's external sync state (from metadata._externalSync)
 * @param options - Optional field-level detection options
 * @returns ConflictInfo if both sides changed, or null if no conflict
 */
export declare function detectConflict(localElement: Element, remoteItem: ExternalTask, syncState: ExternalSyncState, options?: DetectConflictOptions): ConflictInfo | null;
/**
 * Compute a deterministic hash for an external item's content fields.
 * Used for change detection on the remote side.
 *
 * Normalizes the item to a consistent shape for hashing:
 * sorted keys, JSON stringified, SHA256.
 */
export declare function computeExternalItemHash(item: ExternalTask): string;
/**
 * Resolve a conflict using the specified strategy.
 *
 * Strategies:
 * - last_write_wins: Compare updatedAt timestamps, most recent wins
 * - local_wins: Stoneforge version always takes precedence
 * - remote_wins: External version always takes precedence
 * - manual: Mark for human resolution
 *
 * When field-level merge is possible (different fields changed on each side),
 * non-conflicting fields are merged automatically regardless of strategy.
 * The strategy only applies to fields that actually conflict (same field
 * changed on both sides).
 *
 * @param conflict - The detected conflict info
 * @param strategy - The resolution strategy to apply
 * @param localElement - Current local element
 * @param remoteItem - Current remote item
 * @param options - Optional field map config for field-level merge
 * @returns ResolvedChanges describing what to apply
 */
export declare function resolveConflict(conflict: ConflictInfo, strategy: ConflictStrategy, localElement?: Element, remoteItem?: ExternalTask, options?: {
    fieldMapConfig?: TaskFieldMapConfig;
}): ResolvedChanges;
/** Tag applied to elements with unresolved sync conflicts */
export declare const SYNC_CONFLICT_TAG = "sync-conflict";
/**
 * Apply manual conflict resolution metadata to an element.
 * Tags the element with 'sync-conflict' and stores both versions
 * in metadata._externalSync.conflict.
 *
 * @param element - The element to mark as conflicted
 * @param conflict - The conflict info
 * @param localValues - Local field values at time of conflict
 * @param remoteValues - Remote field values at time of conflict
 * @returns Updated metadata and tags to apply to the element
 */
export declare function applyManualConflict(element: Element, conflict: ConflictInfo, localValues: Record<string, unknown>, remoteValues: Record<string, unknown>): {
    metadata: Record<string, unknown>;
    tags: string[];
};
/**
 * Resolve a manual conflict by choosing a side.
 * Clears the sync-conflict tag and conflict metadata.
 *
 * Returns the updates to apply to the element. The caller is
 * responsible for applying these and pushing/pulling as needed.
 *
 * @param element - The element with the conflict
 * @param keep - Which side to keep: 'local' or 'remote'
 * @returns Updated metadata, tags, and field values from the chosen side
 */
export declare function resolveManualConflict(element: Element, keep: 'local' | 'remote'): {
    metadata: Record<string, unknown>;
    tags: string[];
    fieldValues: Record<string, unknown>;
};
/**
 * Convert a ConflictInfo and ResolvedChanges into an ExternalSyncConflict
 * suitable for inclusion in sync result reports.
 *
 * @param conflict - The detected conflict info
 * @param resolved - The resolution result
 * @returns ExternalSyncConflict for reporting
 */
export declare function toExternalSyncConflict(conflict: ConflictInfo, resolved: ResolvedChanges): ExternalSyncConflict;
//# sourceMappingURL=conflict-resolver.d.ts.map