import Foundation
import Testing

@testable import NucleicCore

@Suite("ProjectCloner — URL/path math + real clone")
struct ProjectClonerTests {

    // MARK: - repoFolderName (pure)

    @Test func repoFolderNameHandlesCommonURLShapes() {
        #expect(ProjectCloner.repoFolderName(fromGitURL: "https://github.com/org/repo.git") == "repo")
        #expect(ProjectCloner.repoFolderName(fromGitURL: "https://github.com/org/repo") == "repo")
        #expect(ProjectCloner.repoFolderName(fromGitURL: "git@github.com:org/My-Repo.git") == "my-repo")
        #expect(ProjectCloner.repoFolderName(fromGitURL: "ssh://git@host/org/repo.git/") == "repo")
        #expect(ProjectCloner.repoFolderName(fromGitURL: "  https://x/y/cool_proj.git  ") == "cool-proj")
    }

    @Test func repoFolderNameFallsBackWhenEmpty() {
        #expect(ProjectCloner.repoFolderName(fromGitURL: "") == "repo")
        #expect(ProjectCloner.repoFolderName(fromGitURL: ".git") == "repo")
    }

    // MARK: - isControlled (pure)

    @Test func isControlledMatchesUnderControlBase() {
        let base = ProjectCloner.controlBase
        #expect(ProjectCloner.isControlled(base))
        #expect(ProjectCloner.isControlled((base as NSString).appendingPathComponent("repo")))
        #expect(!ProjectCloner.isControlled("/Users/someone/elsewhere/repo"))
        // A sibling sharing a name prefix is not "under" the base.
        #expect(!ProjectCloner.isControlled(base + "-other/repo"))
    }

    // MARK: - uniqueDestination (pure-ish, hits the filesystem)

    @Test func uniqueDestinationAvoidsCollisions() throws {
        let dir = (NSTemporaryDirectory() as NSString)
            .appendingPathComponent("nucleic-clone-\(UUID().uuidString)")
        try FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(atPath: dir) }

        // Free slot → base name.
        #expect(ProjectCloner.uniqueDestination(parent: dir, folder: "repo")
            == (dir as NSString).appendingPathComponent("repo"))

        // Occupy "repo" and "repo-2"; next free is "repo-3".
        try FileManager.default.createDirectory(
            atPath: (dir as NSString).appendingPathComponent("repo"), withIntermediateDirectories: true)
        try FileManager.default.createDirectory(
            atPath: (dir as NSString).appendingPathComponent("repo-2"), withIntermediateDirectories: true)
        #expect(ProjectCloner.uniqueDestination(parent: dir, folder: "repo")
            == (dir as NSString).appendingPathComponent("repo-3"))
    }

    // MARK: - real clone from a local source repo

    @Test func cloneCopiesRepoAndDetectsBranch() async throws {
        let source = try await GitTestRepo(defaultBranch: "trunk")
        defer { source.cleanup() }

        let destDir = (NSTemporaryDirectory() as NSString)
            .appendingPathComponent("nucleic-clonedest-\(UUID().uuidString)")
        defer { try? FileManager.default.removeItem(atPath: destDir) }
        let destination = (destDir as NSString).appendingPathComponent("clone")

        let cloner = ProjectCloner()
        try await cloner.clone(from: source.root, into: destination)

        // The working copy materialized with the source's content.
        #expect(FileManager.default.fileExists(
            atPath: (destination as NSString).appendingPathComponent("README.md")))
        // The detected default branch is what the clone checked out.
        let branch = await cloner.detectDefaultBranch(at: destination)
        #expect(branch.value == "trunk")
    }

    // MARK: - adoptUpstreamRemote (real local clone)

    @Test func adoptUpstreamRemoteRepointsCloneOriginAtSourceUpstream() async throws {
        let source = try await GitTestRepo()
        defer { source.cleanup() }
        // Give the source an upstream `origin` (a fake URL is enough — we never fetch it).
        try await source.run(["remote", "add", "origin", "https://example.com/org/repo.git"])

        let destDir = (NSTemporaryDirectory() as NSString)
            .appendingPathComponent("nucleic-adopt-\(UUID().uuidString)")
        defer { try? FileManager.default.removeItem(atPath: destDir) }
        let clone = (destDir as NSString).appendingPathComponent("clone")

        let cloner = ProjectCloner()
        try await cloner.clone(from: source.root, into: clone)
        // A local clone records `origin` = the source path; adoption repoints it upstream.
        await cloner.adoptUpstreamRemote(fromLocalSource: source.root, into: clone)

        let originURL = try await source.git.checked(["remote", "get-url", "origin"], in: clone)
        #expect(originURL == "https://example.com/org/repo.git")
    }

    @Test func adoptUpstreamRemoteRemovesOriginWhenSourceHasNoUpstream() async throws {
        let source = try await GitTestRepo()  // no `origin` remote
        defer { source.cleanup() }

        let destDir = (NSTemporaryDirectory() as NSString)
            .appendingPathComponent("nucleic-adopt-none-\(UUID().uuidString)")
        defer { try? FileManager.default.removeItem(atPath: destDir) }
        let clone = (destDir as NSString).appendingPathComponent("clone")

        let cloner = ProjectCloner()
        try await cloner.clone(from: source.root, into: clone)
        await cloner.adoptUpstreamRemote(fromLocalSource: source.root, into: clone)

        // With no upstream to adopt, the clone's local-path `origin` is removed rather than
        // left pointing at a directory the user may move or delete.
        let remotes = try await source.git.checked(["remote"], in: clone)
        #expect(remotes.isEmpty)
        // The clone still carries the source's committed history.
        #expect(FileManager.default.fileExists(
            atPath: (clone as NSString).appendingPathComponent("README.md")))
    }
}
