export interface SyncedBookTools {
    lastUpdate: number;
}

export interface SyncedBook {
    id: string;
    type: string;
    title: string;
    subTitle: string | null;
    seriesId: string | null;
    lastUpdate: number;
    chapters: SyncedChapter[];
    characters: SyncedCharacter[];
    locations: SyncedLocation[];
    worlds: SyncedWorld[];
    incidents: SyncedIncident[];
    plotPoints: SyncedPlotPoint[];
    issues: SyncedIssue[];
    actSummaries: SyncedActSummary[];
    guideLine: SyncedGuideLine | null;
    aiGuideLine: SyncedAIGuideLine | null;
    bookTools: SyncedBookTools | null;
    spells: SyncedSpell[];
    spellTags: SyncedSpellTag[];
}

export interface SyncedChapter {
    id: string;
    name: string;
    lastUpdate: number;
    contents: SyncedChapterContent[];
    info: SyncedChapterInfo | null;
}

export interface SyncedChapterContent {
    id: string;
    lastUpdate: number;
}

export interface SyncedChapterInfo {
    id: string;
    lastUpdate: number;
}

export interface SyncedCharacter {
    id: string;
    name: string;
    lastUpdate: number;
    attributes: SyncedCharacterAttribute[];
}

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

export interface SyncedLocation {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedLocationElement[];
}

export interface SyncedLocationElement {
    id: string;
    name: string;
    lastUpdate: number;
    subElements: SyncedLocationSubElement[];
}

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

export interface SyncedWorld {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedWorldElement[];
}

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

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

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

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

export interface SyncedActSummary {
    id: string;
    lastUpdate: number;
}

export interface SyncedGuideLine {
    lastUpdate: number;
}

export interface SyncedAIGuideLine {
    lastUpdate: number;
}

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

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

export interface BookSyncCompare {
    id: string;
    chapters: string[];
    chapterContents: string[];
    chapterInfos: string[];
    characters: string[];
    characterAttributes: string[];
    locations: string[];
    locationElements: string[];
    locationSubElements: string[];
    worlds: string[];
    worldElements: string[];
    incidents: string[];
    plotPoints: string[];
    issues: string[];
    actSummaries: string[];
    guideLine: boolean;
    aiGuideLine: boolean;
    bookTools: boolean;
    spells: string[];
    spellTags: string[];
}

export function compareBookSyncs(newerBook: SyncedBook, olderBook: SyncedBook): BookSyncCompare | null {
    const changedChapterIds: string[] = [];
    const changedChapterContentIds: string[] = [];
    const changedChapterInfoIds: string[] = [];
    const changedCharacterIds: string[] = [];
    const changedCharacterAttributeIds: string[] = [];
    const changedLocationIds: string[] = [];
    const changedLocationElementIds: string[] = [];
    const changedLocationSubElementIds: string[] = [];
    const changedWorldIds: string[] = [];
    const changedWorldElementIds: string[] = [];
    const changedIncidentIds: string[] = [];
    const changedPlotPointIds: string[] = [];
    const changedIssueIds: string[] = [];
    const changedActSummaryIds: string[] = [];
    const changedSpellIds: string[] = [];
    const changedSpellTagIds: string[] = [];
    let guideLineChanged: boolean = false;
    let aiGuideLineChanged: boolean = false;
    let bookToolsChanged: boolean = false;

    newerBook.chapters.forEach((newerChapter: SyncedChapter): void => {
        const olderChapter: SyncedChapter | undefined = olderBook.chapters.find((chapter: SyncedChapter): boolean => chapter.id === newerChapter.id);

        if (!olderChapter) {
            changedChapterIds.push(newerChapter.id);
            newerChapter.contents.forEach((content: SyncedChapterContent): void => {
                changedChapterContentIds.push(content.id);
            });
            if (newerChapter.info) {
                changedChapterInfoIds.push(newerChapter.info.id);
            }
        } else if (newerChapter.lastUpdate > olderChapter.lastUpdate) {
            changedChapterIds.push(newerChapter.id);
        } else {
            newerChapter.contents.forEach((newerContent: SyncedChapterContent): void => {
                const olderContent: SyncedChapterContent | undefined = olderChapter.contents.find((content: SyncedChapterContent): boolean => content.id === newerContent.id);
                if (!olderContent || newerContent.lastUpdate > olderContent.lastUpdate) {
                    changedChapterContentIds.push(newerContent.id);
                }
            });

            if (newerChapter.info && olderChapter.info) {
                if (newerChapter.info.lastUpdate > olderChapter.info.lastUpdate) {
                    changedChapterInfoIds.push(newerChapter.info.id);
                }
            } else if (newerChapter.info && !olderChapter.info) {
                changedChapterInfoIds.push(newerChapter.info.id);
            }
        }
    });

    newerBook.characters.forEach((newerCharacter: SyncedCharacter): void => {
        const olderCharacter: SyncedCharacter | undefined = olderBook.characters.find((character: SyncedCharacter): boolean => character.id === newerCharacter.id);

        if (!olderCharacter) {
            changedCharacterIds.push(newerCharacter.id);
            newerCharacter.attributes.forEach((attribute: SyncedCharacterAttribute): void => {
                changedCharacterAttributeIds.push(attribute.id);
            });
        } else if (newerCharacter.lastUpdate > olderCharacter.lastUpdate) {
            changedCharacterIds.push(newerCharacter.id);
        } else {
            newerCharacter.attributes.forEach((newerAttribute: SyncedCharacterAttribute): void => {
                const olderAttribute: SyncedCharacterAttribute | undefined = olderCharacter.attributes.find((attribute: SyncedCharacterAttribute): boolean => attribute.id === newerAttribute.id);
                if (!olderAttribute || newerAttribute.lastUpdate > olderAttribute.lastUpdate) {
                    changedCharacterAttributeIds.push(newerAttribute.id);
                }
            });
        }
    });

    newerBook.locations.forEach((newerLocation: SyncedLocation): void => {
        const olderLocation: SyncedLocation | undefined = olderBook.locations.find((location: SyncedLocation): boolean => location.id === newerLocation.id);

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

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

    newerBook.worlds.forEach((newerWorld: SyncedWorld): void => {
        const olderWorld: SyncedWorld | undefined = olderBook.worlds.find((world: SyncedWorld): boolean => world.id === newerWorld.id);

        if (!olderWorld) {
            changedWorldIds.push(newerWorld.id);
            newerWorld.elements.forEach((element: SyncedWorldElement): void => {
                changedWorldElementIds.push(element.id);
            });
        } else if (newerWorld.lastUpdate > olderWorld.lastUpdate) {
            changedWorldIds.push(newerWorld.id);
        } else {
            newerWorld.elements.forEach((newerElement: SyncedWorldElement): void => {
                const olderElement: SyncedWorldElement | undefined = olderWorld.elements.find((element: SyncedWorldElement): boolean => element.id === newerElement.id);
                if (!olderElement || newerElement.lastUpdate > olderElement.lastUpdate) {
                    changedWorldElementIds.push(newerElement.id);
                }
            });
        }
    });

    newerBook.incidents.forEach((newerIncident: SyncedIncident): void => {
        const olderIncident: SyncedIncident | undefined = olderBook.incidents.find((incident: SyncedIncident): boolean => incident.id === newerIncident.id);
        if (!olderIncident || newerIncident.lastUpdate > olderIncident.lastUpdate) {
            changedIncidentIds.push(newerIncident.id);
        }
    });

    newerBook.plotPoints.forEach((newerPlotPoint: SyncedPlotPoint): void => {
        const olderPlotPoint: SyncedPlotPoint | undefined = olderBook.plotPoints.find((plotPoint: SyncedPlotPoint): boolean => plotPoint.id === newerPlotPoint.id);
        if (!olderPlotPoint || newerPlotPoint.lastUpdate > olderPlotPoint.lastUpdate) {
            changedPlotPointIds.push(newerPlotPoint.id);
        }
    });

    newerBook.issues.forEach((newerIssue: SyncedIssue): void => {
        const olderIssue: SyncedIssue | undefined = olderBook.issues.find((issue: SyncedIssue): boolean => issue.id === newerIssue.id);
        if (!olderIssue || newerIssue.lastUpdate > olderIssue.lastUpdate) {
            changedIssueIds.push(newerIssue.id);
        }
    });

    newerBook.actSummaries.forEach((newerActSummary: SyncedActSummary): void => {
        const olderActSummary: SyncedActSummary | undefined = olderBook.actSummaries.find((actSummary: SyncedActSummary): boolean => actSummary.id === newerActSummary.id);
        if (!olderActSummary || newerActSummary.lastUpdate > olderActSummary.lastUpdate) {
            changedActSummaryIds.push(newerActSummary.id);
        }
    });

    if (newerBook.guideLine && olderBook.guideLine) {
        guideLineChanged = newerBook.guideLine.lastUpdate > olderBook.guideLine.lastUpdate;
    } else if (newerBook.guideLine && !olderBook.guideLine) {
        guideLineChanged = true;
    }

    if (newerBook.aiGuideLine && olderBook.aiGuideLine) {
        aiGuideLineChanged = newerBook.aiGuideLine.lastUpdate > olderBook.aiGuideLine.lastUpdate;
    } else if (newerBook.aiGuideLine && !olderBook.aiGuideLine) {
        aiGuideLineChanged = true;
    }

    if (newerBook.bookTools && olderBook.bookTools) {
        bookToolsChanged = newerBook.bookTools.lastUpdate > olderBook.bookTools.lastUpdate;
    } else if (newerBook.bookTools && !olderBook.bookTools) {
        bookToolsChanged = true;
    }

    newerBook.spellTags.forEach((newerSpellTag: SyncedSpellTag): void => {
        const olderSpellTag: SyncedSpellTag | undefined = olderBook.spellTags.find((spellTag: SyncedSpellTag): boolean => spellTag.id === newerSpellTag.id);
        if (!olderSpellTag || newerSpellTag.lastUpdate > olderSpellTag.lastUpdate) {
            changedSpellTagIds.push(newerSpellTag.id);
        }
    });

    newerBook.spells.forEach((newerSpell: SyncedSpell): void => {
        const olderSpell: SyncedSpell | undefined = olderBook.spells.find((spell: SyncedSpell): boolean => spell.id === newerSpell.id);
        if (!olderSpell || newerSpell.lastUpdate > olderSpell.lastUpdate) {
            changedSpellIds.push(newerSpell.id);
        }
    });

    const hasChanges: boolean =
        changedChapterIds.length > 0 ||
        changedChapterContentIds.length > 0 ||
        changedChapterInfoIds.length > 0 ||
        changedCharacterIds.length > 0 ||
        changedCharacterAttributeIds.length > 0 ||
        changedLocationIds.length > 0 ||
        changedLocationElementIds.length > 0 ||
        changedLocationSubElementIds.length > 0 ||
        changedWorldIds.length > 0 ||
        changedWorldElementIds.length > 0 ||
        changedIncidentIds.length > 0 ||
        changedPlotPointIds.length > 0 ||
        changedIssueIds.length > 0 ||
        changedActSummaryIds.length > 0 ||
        changedSpellIds.length > 0 ||
        changedSpellTagIds.length > 0 ||
        guideLineChanged ||
        aiGuideLineChanged ||
        bookToolsChanged;

    if (!hasChanges) {
        return null;
    }

    return {
        id: newerBook.id,
        chapters: changedChapterIds,
        chapterContents: changedChapterContentIds,
        chapterInfos: changedChapterInfoIds,
        characters: changedCharacterIds,
        characterAttributes: changedCharacterAttributeIds,
        locations: changedLocationIds,
        locationElements: changedLocationElementIds,
        locationSubElements: changedLocationSubElementIds,
        worlds: changedWorldIds,
        worldElements: changedWorldElementIds,
        incidents: changedIncidentIds,
        plotPoints: changedPlotPointIds,
        issues: changedIssueIds,
        actSummaries: changedActSummaryIds,
        guideLine: guideLineChanged,
        aiGuideLine: aiGuideLineChanged,
        bookTools: bookToolsChanged,
        spells: changedSpellIds,
        spellTags: changedSpellTagIds
    };
}