import { useEffect, useState } from "react";
export function getModifiedFieldKey(field) {
    return [
        field.fieldId,
        field.item.id,
        field.item.language,
        field.item.version,
    ].join(":");
}
export function aggregateModifiedFieldsFromStores(stores) {
    const fieldsByKey = new Map();
    stores.forEach((store) => {
        store.getAllFields().forEach((field) => {
            fieldsByKey.set(getModifiedFieldKey(field), field);
        });
    });
    return Array.from(fieldsByKey.values());
}
export function areModifiedFieldSnapshotsEqual(previous, next) {
    if (previous.length !== next.length)
        return false;
    const previousByKey = new Map(previous.map((field) => [getModifiedFieldKey(field), field]));
    return next.every((field) => previousByKey.get(getModifiedFieldKey(field)) === field);
}
// Field-specific subscription store that doesn't rely on React context
export class FieldModificationStore {
    subscribers = new Map();
    fieldData = new Map();
    // Global subscribers notified whenever any field changes
    allSubscribers = new Set();
    // Cached snapshot for getAllFields stability
    allFieldsSnapshot = [];
    // Notify all-subscribers in a deferred manner
    notifyAll() {
        if (this.allSubscribers.size === 0)
            return;
        setTimeout(() => {
            // Copy to avoid mutation during iteration
            const callbacks = Array.from(this.allSubscribers.values());
            callbacks.forEach((callback) => {
                try {
                    callback();
                }
                catch (error) {
                    // eslint-disable-next-line no-console
                    console.error("Error in FieldModificationStore global subscriber:", error);
                }
            });
        }, 0);
    }
    // Notify subscribers in a deferred manner to avoid triggering React state updates during render
    notify(key, field) {
        const subscribers = this.subscribers.get(key);
        if (!subscribers || subscribers.size === 0)
            return;
        // Defer notifications to the next macrotask to avoid "setState during render" warnings
        setTimeout(() => {
            const subs = this.subscribers.get(key);
            if (!subs)
                return;
            subs.forEach((callback) => callback(field));
        }, 0);
    }
    getFieldKey(fieldId, itemId, language, version) {
        return `${fieldId}:${itemId}:${language}:${version}`;
    }
    // Subscribe to a specific field's changes
    subscribe(fieldId, itemId, language, version, callback) {
        const key = this.getFieldKey(fieldId, itemId, language, version);
        if (!this.subscribers.has(key)) {
            this.subscribers.set(key, new Set());
        }
        this.subscribers.get(key).add(callback);
        // Immediately call with current value
        callback(this.fieldData.get(key));
        // Return unsubscribe function
        return () => {
            const subscribers = this.subscribers.get(key);
            if (subscribers) {
                subscribers.delete(callback);
                if (subscribers.size === 0) {
                    this.subscribers.delete(key);
                }
            }
        };
    }
    // Update a specific field (called from itemsRepository)
    updateField(field) {
        const key = this.getFieldKey(field.fieldId, field.item.id, field.item.language, field.item.version);
        const previousField = this.fieldData.get(key);
        // Check for actual changes to avoid unnecessary re-renders
        const hasChanged = !previousField ||
            previousField.value !== field.value ||
            previousField.isDirty !== field.isDirty ||
            previousField.saveSequence !== field.saveSequence;
        // Always update the stored field data
        this.fieldData.set(key, field);
        if (hasChanged) {
            // Refresh snapshot only when data actually changed
            this.allFieldsSnapshot = Array.from(this.fieldData.values());
            // Notify only subscribers for this specific field
            this.notify(key, field);
            // And notify global subscribers
            this.notifyAll();
        }
    }
    // Remove a field (when it's no longer modified)
    removeField(fieldId, itemId, language, version) {
        const key = this.getFieldKey(fieldId, itemId, language, version);
        this.fieldData.delete(key);
        // Notify subscribers that field is no longer modified
        this.notify(key, undefined);
        // Refresh snapshot
        this.allFieldsSnapshot = Array.from(this.fieldData.values());
        this.notifyAll();
    }
    // Clear all fields
    clear() {
        this.fieldData.clear();
        this.allFieldsSnapshot = [];
        // Notify all subscribers that their fields are no longer modified
        this.subscribers.forEach((_, key) => {
            this.notify(key, undefined);
        });
        this.notifyAll();
    }
    // Get current field data (for context compatibility)
    getAllFields() {
        // Return cached snapshot to ensure referential stability across renders
        return this.allFieldsSnapshot;
    }
    // Bulk update (for when itemsRepository updates multiple fields)
    updateFields(fields) {
        let anyChanged = false;
        // Merge the provided fields without treating omitted keys as removals.
        // Refresh reconciliation often works from a snapshot; deleting missing keys
        // here can wipe edits that landed while the refresh was in flight.
        fields.forEach((field) => {
            const key = this.getFieldKey(field.fieldId, field.item.id, field.item.language, field.item.version);
            const previousField = this.fieldData.get(key);
            const hasChanged = !previousField ||
                previousField.value !== field.value ||
                previousField.isDirty !== field.isDirty ||
                previousField.saveSequence !== field.saveSequence;
            if (hasChanged) {
                this.fieldData.set(key, field);
                this.notify(key, field);
                anyChanged = true;
            }
        });
        if (anyChanged) {
            this.allFieldsSnapshot = Array.from(this.fieldData.values());
            this.notifyAll();
        }
    }
    // Lookup a specific field without subscribing
    getField(fieldId, itemId, language, version) {
        const key = this.getFieldKey(fieldId, itemId, language, version);
        return this.fieldData.get(key);
    }
    // Clear the dirty state for a field (used for error recovery)
    // This prevents fields from getting stuck in "saving" state when an error occurs
    clearDirtyState(fieldId, itemId, language, version) {
        const key = this.getFieldKey(fieldId, itemId, language, version);
        const field = this.fieldData.get(key);
        if (field && field.isDirty) {
            const updatedField = { ...field, isDirty: false };
            this.fieldData.set(key, updatedField);
            this.allFieldsSnapshot = Array.from(this.fieldData.values());
            this.notify(key, updatedField);
            this.notifyAll();
        }
    }
    // Subscribe to any change across any field (for useSyncExternalStore)
    subscribeAll(callback) {
        this.allSubscribers.add(callback);
        // Call back immediately to provide initial snapshot alignment
        setTimeout(() => callback(), 0);
        return () => {
            this.allSubscribers.delete(callback);
        };
    }
}
// Global store instance
const fieldModificationStore = new FieldModificationStore();
// Hook for field-specific subscriptions - only rerenders when THIS field changes
export function useFieldModification(fieldId, itemId, language, version, store = fieldModificationStore) {
    const [modifiedField, setModifiedField] = useState(undefined);
    const [isModified, setIsModified] = useState(false);
    const [isSaved, setIsSaved] = useState(false);
    useEffect(() => {
        const unsubscribe = store.subscribe(fieldId, itemId, language, version, (field) => {
            setModifiedField(field);
            setIsModified(!!field);
            // isSaved logic:
            // - If field exists and is not dirty → saved = true (field was modified and saved)
            // - If field exists and is dirty → saved = false (field was modified but not saved)
            // - If field doesn't exist → saved = false (field never modified, no indicator shown)
            setIsSaved(field ? !field.isDirty : false);
        });
        return unsubscribe;
    }, [fieldId, itemId, language, version, store]);
    return { modifiedField, isModified, isSaved };
}
// Export store for itemsRepository to use
export { fieldModificationStore };
//# sourceMappingURL=fieldModificationStore.js.map