-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
--
-- SPDX-License-Identifier: GPL-3.0-or-later

package.path = package.path .. ";./?.lua"
local wt = dofile("src/main.lua")
local helper = require("spec.test_helper")
local git = helper.git

describe("cmd_init", function()
	local temp_dir
	local original_cwd

	setup(function()
		local handle = io.popen("pwd")
		if handle then
			original_cwd = handle:read("*l")
			handle:close()
		end
		handle = io.popen("mktemp -d")
		if handle then
			temp_dir = handle:read("*l")
			handle:close()
		end
	end)

	teardown(function()
		if original_cwd then
			os.execute("cd " .. original_cwd)
		end
		if temp_dir then
			os.execute("rm -rf " .. temp_dir)
		end
	end)

	describe("already wt-managed repository", function()
		it("reports already using wt structure when .git file points to .bare", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/already-wt"
			os.execute("mkdir -p " .. project .. "/.bare")
			git("init --bare " .. project .. "/.bare")
			local f = io.open(project .. "/.git", "w")
			if f then
				f:write("gitdir: ./.bare\n")
				f:close()
			end

			local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init")
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Aa]lready"))
		end)
	end)

	describe("inside a worktree", function()
		it("errors when run from inside a worktree", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/wt-project"
			local worktree = project .. "/main"
			os.execute("mkdir -p " .. project .. "/.bare")
			git("init --bare " .. project .. "/.bare")
			os.execute("mkdir -p " .. worktree)
			local f = io.open(worktree .. "/.git", "w")
			if f then
				f:write("gitdir: ../.bare/worktrees/main\n")
				f:close()
			end

			local output, code = wt.run_cmd("cd " .. worktree .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("worktree") or output:match("not a git"))
		end)
	end)

	describe("not a git repository", function()
		it("errors when run in non-git directory", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/not-git"
			os.execute("mkdir -p " .. project)

			local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("not a git"))
		end)
	end)

	describe(".bare exists but no .git", function()
		it("errors and suggests creating .git file", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/bare-only"
			os.execute("mkdir -p " .. project .. "/.bare")
			git("init --bare " .. project .. "/.bare")

			local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("%.git") or output:match("gitdir"))
		end)
	end)

	describe("existing git worktrees", function()
		it("errors when .git/worktrees/ exists", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/has-worktrees"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")
			os.execute("mkdir -p " .. project .. "/.git/worktrees/feature-branch")

			local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("worktree"))
		end)
	end)

	describe("dirty repository", function()
		it("errors when uncommitted changes exist", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/dirty-repo"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")
			local f = io.open(project .. "/dirty.txt", "w")
			if f then
				f:write("uncommitted\n")
				f:close()
			end
			git("-C " .. project .. " add dirty.txt")

			local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("uncommitted") or output:match("stash") or output:match("commit"))
		end)
	end)

	describe("--dry-run", function()
		it("prints plan without modifying filesystem", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/dry-run-test"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Dd]ry run") or output:match("planned"))

			local git_still_dir = io.open(project .. "/.git/HEAD", "r")
			assert.is_not_nil(git_still_dir)
			if git_still_dir then
				git_still_dir:close()
			end

			local bare_exists = io.open(project .. "/.bare/HEAD", "r")
			assert.is_nil(bare_exists)
		end)

		it("lists orphaned files that would be removed", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/dry-orphans"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local f = io.open(project .. "/README.md", "w")
			if f then
				f:write("# Test\n")
				f:close()
			end
			git("-C " .. project .. " add README.md")
			git("-C " .. project .. " commit -m 'add readme'")

			local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("README") or output:match("orphan"))
		end)

		it("shows target worktree path", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/dry-worktree"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("worktree") or output:match("main") or output:match("master"))
		end)
	end)

	describe("-y/--yes flag", function()
		it("bypasses confirmation prompt", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/yes-test"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local output, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Cc]onverted") or output:match("[Bb]are"))

			local bare_exists = io.open(project .. "/.bare/HEAD", "r")
			assert.is_not_nil(bare_exists)
			if bare_exists then
				bare_exists:close()
			end

			local git_is_file = io.open(project .. "/.git", "r")
			if git_is_file then
				local content = git_is_file:read("*a")
				git_is_file:close()
				assert.is_truthy(content:match("gitdir"))
			end
		end)
	end)

	describe("successful conversion", function()
		it("moves .git to .bare", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/convert-test"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local _, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
			assert.are.equal(0, code)

			local bare_head = io.open(project .. "/.bare/HEAD", "r")
			assert.is_not_nil(bare_head)
			if bare_head then
				bare_head:close()
			end
		end)

		it("creates .git file pointing to .bare", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/git-file-test"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local _, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
			assert.are.equal(0, code)

			local git_file = io.open(project .. "/.git", "r")
			assert.is_not_nil(git_file)
			if git_file then
				local content = git_file:read("*a")
				git_file:close()
				assert.is_truthy(content:match("gitdir:.*%.bare"))
			end
		end)

		it("creates worktree for default branch", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/worktree-created"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")

			local cmd = "cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1"
			local _, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)

			local worktrees_output, _ = wt.run_cmd("GIT_DIR=" .. project .. "/.bare git worktree list")
			assert.is_truthy(worktrees_output:match("main") or worktrees_output:match("master"))
		end)

		it("removes orphaned files from root", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/orphan-cleanup"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			local f = io.open(project .. "/tracked.txt", "w")
			if f then
				f:write("tracked\n")
				f:close()
			end
			git("-C " .. project .. " add tracked.txt")
			git("-C " .. project .. " commit -m 'add file'")

			local _, code = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init -y 2>&1")
			assert.are.equal(0, code)

			local orphan_check = io.open(project .. "/tracked.txt", "r")
			assert.is_nil(orphan_check)
		end)
	end)

	describe("warnings", function()
		it("warns about submodules", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = temp_dir .. "/submodule-warn"
			os.execute("mkdir -p " .. project)
			git("init " .. project)
			git("-C " .. project .. " commit --allow-empty -m 'initial'")
			local f = io.open(project .. "/.gitmodules", "w")
			if f then
				f:write("[submodule \"lib\"]\n\tpath = lib\n\turl = https://example.com/lib.git\n")
				f:close()
			end
			git("-C " .. project .. " add .gitmodules")
			git("-C " .. project .. " commit -m 'add submodules'")

			local output, _ = wt.run_cmd("cd " .. project .. " && lua " .. original_cwd .. "/src/main.lua init --dry-run 2>&1")
			assert.is_truthy(output:match("submodule") or output:match("[Ww]arning"))
		end)
	end)
end)
