// SPDX-FileCopyrightText: 2026 John Burwell and contributors
// SPDX-License-Identifier: AGPL-3.0-or-later

import Foundation

@MainActor
protocol LocalSourceRuntimeHosting: AnyObject {
    var sources: [MinecraftSource] { get }
    var isShuttingDown: Bool { get }
    var hasActiveScan: Bool { get }

    func source(withID sourceID: URL) -> MinecraftSource?
    func updateAvailability(for sourceID: URL, to newAvailability: SourceAvailability) -> (previous: SourceAvailability, becameAvailable: Bool)
    func queueAutomaticSync(for sourceID: URL, reason: String, debounce: TimeInterval?)
    func currentCollectionSnapshots(for sourceURL: URL, edition: MinecraftEdition) -> [CollectionSnapshot]
}

enum LocalSourceRuntime {
    static func runRefreshLoop(
        on host: LocalSourceRuntimeHosting,
        refreshInterval: TimeInterval,
        accessMethod: SourceAccessMethod
    ) async {
        while !Task.isCancelled && !host.isShuttingDown {
            if host.hasActiveScan {
                do {
                    try await Task.sleep(for: .seconds(refreshInterval))
                } catch {
                    return
                }
                continue
            }

            await refreshSources(on: host, using: accessMethod)

            do {
                try await Task.sleep(for: .seconds(refreshInterval))
            } catch {
                return
            }
        }
    }

    static func refreshSources(
        on host: LocalSourceRuntimeHosting,
        using accessMethod: SourceAccessMethod
    ) async {
        guard !host.hasActiveScan else {
            return
        }

        let localSourceIDs = host.sources
            .filter { $0.origin.kind == .localFolder }
            .map(\.id)

        for sourceID in localSourceIDs {
            guard !Task.isCancelled, !host.isShuttingDown else {
                return
            }

            guard let currentSource = host.source(withID: sourceID) else {
                continue
            }

            let availability = await accessMethod.availability(for: currentSource)
            let transition = host.updateAvailability(for: sourceID, to: availability)

            guard let refreshedSource = host.source(withID: sourceID) else {
                continue
            }

            if transition.becameAvailable {
                host.queueAutomaticSync(
                    for: sourceID,
                    reason: refreshedSource.hasCachedContent
                        ? "Folder available. Refreshing cached library..."
                        : "Folder available. Scanning Minecraft library...",
                    debounce: nil
                )
                continue
            }

            guard refreshedSource.availability == .available, !refreshedSource.isScanning else {
                continue
            }

            if SourceRestoration.needsReconcile(
                refreshedSource,
                currentCollectionSnapshots: host.currentCollectionSnapshots(for:edition:)
            ) {
                host.queueAutomaticSync(
                    for: sourceID,
                    reason: refreshedSource.hasCachedContent
                        ? "Detected changes. Refreshing cached library..."
                        : "Scanning Minecraft library...",
                    debounce: nil
                )
            }
        }
    }
}
