import Foundation
import Testing
import NucleicProtocol

@testable import NucleicCore

/// The durable mesh-dispatch queue (Covalence): a chat asked to run on Covalence with no eligible
/// runner is parked and retried, and cleaned up when its project goes away. The dispatching
/// device is NEVER its own candidate ("Covalence" means "anywhere on the mesh but here"), so a
/// peerless host is always the "nowhere to place it now" state.
@MainActor
@Suite("Mesh dispatch queue", .serialized, .isolatedContainerSettings)
struct MeshDispatchQueueTests {
    private func makeStore(repo: GitTestRepo) -> AppStore {
        let now: @Sendable () -> Date = { Date(timeIntervalSince1970: 1_700_000_000) }
        let database = try! GRDBMetadataStore(path: nil)
        let worktrees = GitWorktreeManager(now: now)
        let transcriptsDir = URL(fileURLWithPath: (repo.container as NSString)
            .appendingPathComponent("transcripts"))
        return AppStore(
            database: database, worktrees: worktrees, transcriptsDir: transcriptsDir, now: now
        ) { _ in
            ScriptedBackend { e, _ in
                e.emit(.runFinished(RunFinished(outcome: .completed)))
            }
        }
    }

    @Test func parksWhenNoRunnerAndClearsOnProjectDelete() async throws {
        let repo = try await GitTestRepo()
        defer { repo.cleanup() }
        let store = makeStore(repo: repo)
        let project = try #require(
            await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main"))

        // No peers ⇒ nowhere to place it ⇒ durably queued (nil landing).
        let landing = await store.dispatchChatToMesh(project: project, message: "run the suite")
        #expect(landing == nil)
        #expect(store.queuedMeshDispatchCount == 1)

        // A retry with still no runner leaves it queued.
        await store.retryQueuedMeshDispatches()
        #expect(store.queuedMeshDispatchCount == 1)

        // Deleting the project drops its parked dispatch.
        await store.deleteProject(project.id)
        #expect(store.queuedMeshDispatchCount == 0)
    }

    /// Covalence never selects the dispatching device: even a host that ACCEPTS inbound mesh
    /// dispatches (the default) won't run its own Covalence job locally — with no peers, the chat
    /// parks in the durable queue instead of quietly landing "here". ("Here" is the composer's
    /// separate, explicit run target.)
    @Test func neverLandsOnTheDispatchingDeviceItself() async throws {
        let repo = try await GitTestRepo()
        defer { repo.cleanup() }
        UserDefaults.standard.removeObject(forKey: MeshDispatchSettings.acceptsKey)
        let store = makeStore(repo: repo)
        let project = try #require(
            await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main"))
        let landing = await store.dispatchChatToMesh(project: project, message: "go elsewhere")
        #expect(landing == nil)
        #expect(store.queuedMeshDispatchCount == 1)
        // Nothing started locally.
        #expect(await store.sessionSummaries().isEmpty)
    }
}
