import Foundation
import Testing

@testable import NucleicCore

@Suite("GitDirResolver — worktree git-dir resolution for sandbox mounts")
struct GitDirResolverTests {
    /// A linked worktree's `.git` is a file pointing at `<repo>/.git/worktrees/<name>`, whose
    /// common dir is the sibling `<repo>/.git`. The resolver must land on that common dir — matching
    /// what `git rev-parse --git-common-dir` reports — so the sandbox mounts the right path.
    @Test func resolvesLinkedWorktreeToCommonDir() async throws {
        let repo = try await GitTestRepo()
        defer { repo.cleanup() }
        let wtPath = (repo.container as NSString).appendingPathComponent("linked-wt")
        try await repo.git.checked(
            ["worktree", "add", "-b", "feature", wtPath, "main"], in: repo.root)

        // Ground truth from git itself (absolute when run from within a linked worktree).
        let expected = try await repo.git.checked(
            ["rev-parse", "--git-common-dir"], in: wtPath)

        let resolved = GitDirResolver.commonDir(forWorktree: wtPath)
        #expect(resolved == (expected as NSString).standardizingPath)
        // The common dir is the repo's `.git`, OUTSIDE the worktree subtree.
        #expect(resolved == (repo.root as NSString).appendingPathComponent(".git"))
        #expect(!(resolved!).hasPrefix(wtPath + "/"))
    }

    /// For a linked worktree the mount is emitted (common dir is a sibling) at an identical
    /// host→container path, defaulting to writable so the sandbox can commit/merge.
    @Test func emitsMountForLinkedWorktree() async throws {
        let repo = try await GitTestRepo()
        defer { repo.cleanup() }
        let wtPath = (repo.container as NSString).appendingPathComponent("linked-wt")
        try await repo.git.checked(
            ["worktree", "add", "-b", "feature", wtPath, "main"], in: repo.root)

        let mount = GitDirResolver.commonDirMount(forWorktree: wtPath)
        let expected = (repo.root as NSString).appendingPathComponent(".git")
        #expect(mount?.host == expected)
        #expect(mount?.container == expected)  // identical path — absolute pointers resolve
        #expect(mount?.readOnly == false)
        #expect(GitDirResolver.commonDirMount(forWorktree: wtPath, readOnly: true)?.readOnly == true)
    }

    /// A normal checkout keeps `.git` inside the worktree, already covered by the worktree mount —
    /// so no extra mount is emitted (though `commonDir` still points at the inner `.git`).
    @Test func noMountForNormalCheckout() async throws {
        let repo = try await GitTestRepo()
        defer { repo.cleanup() }

        let common = GitDirResolver.commonDir(forWorktree: repo.root)
        #expect(common == (repo.root as NSString).appendingPathComponent(".git"))
        #expect(GitDirResolver.commonDirMount(forWorktree: repo.root) == nil)
    }

    /// A path that isn't a git repo has no resolvable git dir and emits no mount.
    @Test func nilForNonRepo() throws {
        let dir = (NSTemporaryDirectory() as NSString)
            .appendingPathComponent("nucleic-nonrepo-\(UUID().uuidString)")
        try FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(atPath: dir) }

        #expect(GitDirResolver.commonDir(forWorktree: dir) == nil)
        #expect(GitDirResolver.commonDirMount(forWorktree: dir) == nil)
    }

    // MARK: - Pure pointer parsing

    @Test func parsesAbsoluteGitdirPointer() {
        let parsed = GitDirResolver.parseGitdirPointer(
            "gitdir: /repo/.git/worktrees/w\n", relativeTo: "/anything")
        #expect(parsed == "/repo/.git/worktrees/w")
    }

    @Test func resolvesRelativeGitdirPointerAgainstBase() {
        let parsed = GitDirResolver.parseGitdirPointer(
            "gitdir: ../../.git/worktrees/w", relativeTo: "/a/b/wt")
        #expect(parsed == "/a/.git/worktrees/w")
    }

    @Test func ignoresGarbagePointer() {
        #expect(GitDirResolver.parseGitdirPointer("not a pointer", relativeTo: "/x") == nil)
        #expect(GitDirResolver.parseGitdirPointer("gitdir:   ", relativeTo: "/x") == nil)
    }
}
