import Foundation

extension Content {

    func needsSave() {
        setModificationTimestamp()

        if saveState == .isSaved {
            update(saveState: .needsSave)
        }
        // Wait a few seconds for a save, to allow additional changes
        // Reduces the number of saves
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5)) {
            self.saveIfNeeded()
        }
    }

    func saveIfNeeded() {
        switch saveState {
        case .isSaved, .savingPausedDueToLoadErrors, .storageNotInitialized, .isSaving:
            return
        case .needsSave, .failedToSave:
            break
        }
        if Date.now.timeIntervalSince(lastModification) < 5 {
            // Additional modification made
            // Wait for next scheduled invocation of saveIfNeeded()
            // if the overall unsaved time is not too long
            if Date.now.timeIntervalSince(lastSave) < 30 {
                return
            }
            print("Saving after 30 seconds of modifications")
        }
        saveUnconditionally()
    }

    func saveUnconditionally() {
        update(saveState: .isSaving)
        guard saveToDisk() else {
            update(saveState: .failedToSave)
            // TODO: Try to save again
            return
        }
        update(saveState: .isSaved)
        setLastSaveTimestamp()
    }

    private func saveToDisk() -> Bool {
        guard storage.contentScope != nil else {
            print("Storage not initialized, not saving content")
            return false
        }

        var failedSaves = 0
        var saves = 0
        let pageSaves = saveChanged(pages)
        failedSaves += pageSaves.unsaved
        saves += pageSaves.saved

        let postSaves = saveChanged(posts)
        failedSaves += postSaves.unsaved
        saves += postSaves.saved

        let tagSaves = saveChanged(tags)
        failedSaves += tagSaves.unsaved
        saves += tagSaves.saved

        failedSaves.increment(!storage.save(settings: settings.data(tagOverview: tagOverview)))

        let fileSaves = saveChanged(files)
        failedSaves += fileSaves.unsaved
        saves += fileSaves.saved

        if saves > 0 {
            print("Saved \(saves) changed items")
        }
        if failedSaves > 0 {
            print("Save partially failed with \(failedSaves) errors")
            return false
        }
        return true
    }

    func removeUnlinkedFiles() -> Bool {
        var success = true
        if !storage.deletePostFiles(notIn: posts.map { $0.identifier }) {
            success = false
        }
        if !storage.deletePageFiles(notIn: pages.map { $0.identifier }) {
            success = false
        }
        if !storage.deleteTagFiles(notIn: tags.map { $0.identifier }) {
            success = false
        }
        if !storage.deleteFileResources(notIn: files.map { $0.identifier }) {
            success = false
        }
        return success
    }

    private func saveChanged<S>(_ items: S) -> (saved: Int, unsaved: Int, unchanged: Int) where S: Sequence, S.Element: StorageItem {
        var failed = 0
        var saved = 0
        var unchanged = 0
        for item in items {
            guard let wasSaved = item.saveIfNeeded() else {
                unchanged += 1
                continue
            }
            if wasSaved {
                saved += 1
            } else {
                failed += 1
            }
        }
        return (saved, failed, unchanged)
    }
}
