/**
 * Lightweight sync structures for series comparison.
 * These interfaces mirror the backend SyncedSeries* types from Book.ts
 * but are used in the frontend for sync status detection.
 */

export interface SyncedSeriesBook {
    bookId: string;
    order: number;
    lastUpdate: number;
}

export interface SyncedSeriesCharacterAttribute {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesCharacter {
    id: string;
    name: string;
    lastUpdate: number;
    attributes: SyncedSeriesCharacterAttribute[];
}

export interface SyncedSeriesWorldElement {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesWorld {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedSeriesWorldElement[];
}

export interface SyncedSeriesLocationSubElement {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesLocationElement {
    id: string;
    name: string;
    lastUpdate: number;
    subElements: SyncedSeriesLocationSubElement[];
}

export interface SyncedSeriesLocation {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedSeriesLocationElement[];
}

export interface SyncedSeriesSpell {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesSpellTag {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeries {
    id: string;
    name: string;
    description: string | null;
    lastUpdate: number;
    books: SyncedSeriesBook[];
    characters: SyncedSeriesCharacter[];
    worlds: SyncedSeriesWorld[];
    locations: SyncedSeriesLocation[];
    spells: SyncedSeriesSpell[];
    spellTags: SyncedSeriesSpellTag[];
}

/**
 * Comparison result containing IDs of changed entities.
 * Used for partial synchronization - only changed entities are transferred.
 */
export interface SeriesSyncCompare {
    id: string;
    books: string[];
    characters: string[];
    characterAttributes: string[];
    worlds: string[];
    worldElements: string[];
    locations: string[];
    locationElements: string[];
    locationSubElements: string[];
    spells: string[];
    spellTags: string[];
}

/**
 * Compares two versions of a series to find changed entities.
 * The "newer" series is compared against the "older" one to find
 * entities that have been added or modified.
 *
 * @param newerSeries - The series version with potentially newer data
 * @param olderSeries - The series version to compare against
 * @returns SeriesSyncCompare with IDs of changed entities, or null if no changes
 */
export function compareSeriesSyncs(newerSeries: SyncedSeries, olderSeries: SyncedSeries): SeriesSyncCompare | null {
    const changedBookIds: string[] = [];
    const changedCharacterIds: string[] = [];
    const changedCharacterAttributeIds: string[] = [];
    const changedWorldIds: string[] = [];
    const changedWorldElementIds: string[] = [];
    const changedLocationIds: string[] = [];
    const changedLocationElementIds: string[] = [];
    const changedLocationSubElementIds: string[] = [];
    const changedSpellIds: string[] = [];
    const changedSpellTagIds: string[] = [];

    // Compare books
    newerSeries.books.forEach((newerBook: SyncedSeriesBook): void => {
        const olderBook: SyncedSeriesBook | undefined = olderSeries.books.find(
            (book: SyncedSeriesBook): boolean => book.bookId === newerBook.bookId
        );
        if (!olderBook || newerBook.lastUpdate > olderBook.lastUpdate) {
            changedBookIds.push(newerBook.bookId);
        }
    });

    // Compare characters and their attributes
    newerSeries.characters.forEach((newerCharacter: SyncedSeriesCharacter): void => {
        const olderCharacter: SyncedSeriesCharacter | undefined = olderSeries.characters.find(
            (character: SyncedSeriesCharacter): boolean => character.id === newerCharacter.id
        );

        if (!olderCharacter) {
            changedCharacterIds.push(newerCharacter.id);
            newerCharacter.attributes.forEach((attr: SyncedSeriesCharacterAttribute): void => {
                changedCharacterAttributeIds.push(attr.id);
            });
        } else if (newerCharacter.lastUpdate > olderCharacter.lastUpdate) {
            changedCharacterIds.push(newerCharacter.id);
        } else {
            // Check attributes even if character hasn't changed
            newerCharacter.attributes.forEach((newerAttr: SyncedSeriesCharacterAttribute): void => {
                const olderAttr: SyncedSeriesCharacterAttribute | undefined = olderCharacter.attributes.find(
                    (attr: SyncedSeriesCharacterAttribute): boolean => attr.id === newerAttr.id
                );
                if (!olderAttr || newerAttr.lastUpdate > olderAttr.lastUpdate) {
                    changedCharacterAttributeIds.push(newerAttr.id);
                }
            });
        }
    });

    // Compare worlds and their elements
    newerSeries.worlds.forEach((newerWorld: SyncedSeriesWorld): void => {
        const olderWorld: SyncedSeriesWorld | undefined = olderSeries.worlds.find(
            (world: SyncedSeriesWorld): boolean => world.id === newerWorld.id
        );

        if (!olderWorld) {
            changedWorldIds.push(newerWorld.id);
            newerWorld.elements.forEach((element: SyncedSeriesWorldElement): void => {
                changedWorldElementIds.push(element.id);
            });
        } else if (newerWorld.lastUpdate > olderWorld.lastUpdate) {
            changedWorldIds.push(newerWorld.id);
        } else {
            // Check elements even if world hasn't changed
            newerWorld.elements.forEach((newerElement: SyncedSeriesWorldElement): void => {
                const olderElement: SyncedSeriesWorldElement | undefined = olderWorld.elements.find(
                    (element: SyncedSeriesWorldElement): boolean => element.id === newerElement.id
                );
                if (!olderElement || newerElement.lastUpdate > olderElement.lastUpdate) {
                    changedWorldElementIds.push(newerElement.id);
                }
            });
        }
    });

    // Compare locations, their elements, and sub-elements
    newerSeries.locations.forEach((newerLocation: SyncedSeriesLocation): void => {
        const olderLocation: SyncedSeriesLocation | undefined = olderSeries.locations.find(
            (location: SyncedSeriesLocation): boolean => location.id === newerLocation.id
        );

        if (!olderLocation) {
            changedLocationIds.push(newerLocation.id);
            newerLocation.elements.forEach((element: SyncedSeriesLocationElement): void => {
                changedLocationElementIds.push(element.id);
                element.subElements.forEach((subElement: SyncedSeriesLocationSubElement): void => {
                    changedLocationSubElementIds.push(subElement.id);
                });
            });
        } else if (newerLocation.lastUpdate > olderLocation.lastUpdate) {
            changedLocationIds.push(newerLocation.id);
        } else {
            // Check elements
            newerLocation.elements.forEach((newerElement: SyncedSeriesLocationElement): void => {
                const olderElement: SyncedSeriesLocationElement | undefined = olderLocation.elements.find(
                    (element: SyncedSeriesLocationElement): boolean => element.id === newerElement.id
                );

                if (!olderElement) {
                    changedLocationElementIds.push(newerElement.id);
                    newerElement.subElements.forEach((subElement: SyncedSeriesLocationSubElement): void => {
                        changedLocationSubElementIds.push(subElement.id);
                    });
                } else if (newerElement.lastUpdate > olderElement.lastUpdate) {
                    changedLocationElementIds.push(newerElement.id);
                } else {
                    // Check sub-elements
                    newerElement.subElements.forEach((newerSubElement: SyncedSeriesLocationSubElement): void => {
                        const olderSubElement: SyncedSeriesLocationSubElement | undefined = olderElement.subElements.find(
                            (subElement: SyncedSeriesLocationSubElement): boolean => subElement.id === newerSubElement.id
                        );
                        if (!olderSubElement || newerSubElement.lastUpdate > olderSubElement.lastUpdate) {
                            changedLocationSubElementIds.push(newerSubElement.id);
                        }
                    });
                }
            });
        }
    });

    // Compare spells
    newerSeries.spells.forEach((newerSpell: SyncedSeriesSpell): void => {
        const olderSpell: SyncedSeriesSpell | undefined = olderSeries.spells.find(
            (spell: SyncedSeriesSpell): boolean => spell.id === newerSpell.id
        );
        if (!olderSpell || newerSpell.lastUpdate > olderSpell.lastUpdate) {
            changedSpellIds.push(newerSpell.id);
        }
    });

    // Compare spell tags
    newerSeries.spellTags.forEach((newerTag: SyncedSeriesSpellTag): void => {
        const olderTag: SyncedSeriesSpellTag | undefined = olderSeries.spellTags.find(
            (tag: SyncedSeriesSpellTag): boolean => tag.id === newerTag.id
        );
        if (!olderTag || newerTag.lastUpdate > olderTag.lastUpdate) {
            changedSpellTagIds.push(newerTag.id);
        }
    });

    // Check if there are any changes
    const hasChanges: boolean =
        changedBookIds.length > 0 ||
        changedCharacterIds.length > 0 ||
        changedCharacterAttributeIds.length > 0 ||
        changedWorldIds.length > 0 ||
        changedWorldElementIds.length > 0 ||
        changedLocationIds.length > 0 ||
        changedLocationElementIds.length > 0 ||
        changedLocationSubElementIds.length > 0 ||
        changedSpellIds.length > 0 ||
        changedSpellTagIds.length > 0;

    if (!hasChanges) {
        return null;
    }

    return {
        id: newerSeries.id,
        books: changedBookIds,
        characters: changedCharacterIds,
        characterAttributes: changedCharacterAttributeIds,
        worlds: changedWorldIds,
        worldElements: changedWorldElementIds,
        locations: changedLocationIds,
        locationElements: changedLocationElementIds,
        locationSubElements: changedLocationSubElementIds,
        spells: changedSpellIds,
        spellTags: changedSpellTagIds
    };
}
