-- 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_remove", 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)

	---Helper to create a wt-managed project with worktrees
	---@param name string project name
	---@param branches string[] branches to create worktrees for
	---@return string project_path
	local function create_wt_project(name, branches)
		local project = temp_dir .. "/" .. name
		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 first_branch = branches[1] or "main"
		git("--git-dir=" .. project .. "/.bare symbolic-ref HEAD refs/heads/" .. first_branch)
		local first_wt = project .. "/" .. first_branch
		git("--git-dir=" .. project .. "/.bare worktree add --orphan -b " .. first_branch .. " -- " .. first_wt)
		git("-C " .. first_wt .. " commit --allow-empty -m 'initial'")

		for i = 2, #branches do
			local branch = branches[i]
			local wt_path = project .. "/" .. branch:gsub("/", "_")
			git("--git-dir=" .. project .. "/.bare worktree add -b " .. branch .. " -- " .. wt_path .. " " .. first_branch)
		end

		return project
	end

	describe("worktree not found", function()
		it("errors when target worktree does not exist", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("no-wt", { "main" })

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

	describe("cwd inside target worktree", function()
		it("errors when trying to remove worktree while inside it", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("inside-wt", { "main", "feature" })
			local feature_wt = project .. "/feature"

			local output, code = wt.run_cmd("cd " .. feature_wt .. " && lua " .. original_cwd .. "/src/main.lua r feature 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("inside") or output:match("cannot remove"))
		end)
	end)

	describe("uncommitted changes", function()
		it("errors when worktree has uncommitted changes without -f", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("dirty-wt", { "main", "dirty" })
			local dirty_wt = project .. "/dirty"

			local f = io.open(dirty_wt .. "/uncommitted.txt", "w")
			if f then
				f:write("dirty file\n")
				f:close()
			end
			git("-C " .. dirty_wt .. " add uncommitted.txt")

			local output, code = wt.run_cmd("cd " .. project .. "/main && lua " .. original_cwd .. "/src/main.lua r dirty 2>&1")
			assert.are_not.equal(0, code)
			assert.is_truthy(output:match("uncommitted") or output:match("%-f") or output:match("force"))
		end)

		it("succeeds with -f when worktree has uncommitted changes", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("force-dirty", { "main", "force-test" })
			local target_wt = project .. "/force-test"

			local f = io.open(target_wt .. "/uncommitted.txt", "w")
			if f then
				f:write("dirty file\n")
				f:close()
			end
			git("-C " .. target_wt .. " add uncommitted.txt")

			local cmd = "cd " .. project .. "/main && lua " .. original_cwd .. "/src/main.lua r force-test -f 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Rr]emoved") or output:match("[Ss]uccess"))

			local check = io.open(target_wt .. "/.git", "r")
			assert.is_nil(check)
		end)
	end)

	describe("branch deletion with -b", function()
		it("refuses to delete branch if it's bare repo HEAD", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("bare-head", { "main", "other" })

			git("--git-dir=" .. project .. "/.bare worktree remove -- " .. project .. "/main")

			git("--git-dir=" .. project .. "/.bare symbolic-ref HEAD refs/heads/main")
			git("--git-dir=" .. project .. "/.bare worktree add -- " .. project .. "/main main")

			local cmd = "cd " .. project .. "/other && lua " .. original_cwd .. "/src/main.lua r main -b 2>&1"
			local output, _ = wt.run_cmd(cmd)
			assert.is_truthy(output:match("HEAD") or output:match("retained") or output:match("cannot delete"))
		end)

		it("refuses to delete branch if checked out in another worktree", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("multi-checkout", { "main", "feature", "other" })

			local cmd = "cd " .. project .. "/other && lua " .. original_cwd .. "/src/main.lua r feature -b 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Rr]emoved"))
			local ref_cmd = "GIT_DIR=" .. project .. "/.bare git show-ref --verify --quiet refs/heads/feature"
			local branch_exists = wt.run_cmd_silent(ref_cmd)
			assert.is_false(branch_exists)
		end)

		it("deletes branch when no conflicts exist", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("clean-delete", { "main", "deleteme" })

			git("--git-dir=" .. project .. "/.bare symbolic-ref HEAD refs/heads/main")

			local cmd = "cd " .. project .. "/main && lua " .. original_cwd .. "/src/main.lua r deleteme -b 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("branch") and output:match("[Rr]emoved"))

			local ref_cmd = "GIT_DIR=" .. project .. "/.bare git show-ref --verify --quiet refs/heads/deleteme"
			local branch_exists = wt.run_cmd_silent(ref_cmd)
			assert.is_false(branch_exists)
		end)
	end)

	describe("successful removal", function()
		it("removes worktree without -b", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("simple-remove", { "main", "removeme" })

			local cmd = "cd " .. project .. "/main && lua " .. original_cwd .. "/src/main.lua r removeme 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Rr]emoved"))

			local check = io.open(project .. "/removeme/.git", "r")
			assert.is_nil(check)

			local ref_cmd = "GIT_DIR=" .. project .. "/.bare git show-ref --verify --quiet refs/heads/removeme"
			local branch_exists = wt.run_cmd_silent(ref_cmd)
			assert.is_true(branch_exists)
		end)

		it("finds worktree by branch regardless of path style", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("path-agnostic", { "main" })

			-- Create worktree at flat-style path even though default config is nested
			local branch = "feature/auth"
			local flat_path = project .. "/feature_auth"
			git("--git-dir=" .. project .. "/.bare worktree add -b " .. branch .. " -- " .. flat_path .. " main")

			-- wt r should find it by branch, not by computed path
			local cmd = "cd " .. project .. "/main && lua " .. original_cwd .. "/src/main.lua r feature/auth 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Rr]emoved"))

			-- Verify worktree is gone
			local check = io.open(flat_path .. "/.git", "r")
			assert.is_nil(check)
		end)

		it("finds worktree at arbitrary path", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("arbitrary-path", { "main" })

			-- Create worktree at completely arbitrary path
			local branch = "my-feature"
			local arbitrary_path = project .. "/some/weird/location"
			os.execute("mkdir -p " .. project .. "/some/weird")
			git("--git-dir=" .. project .. "/.bare worktree add -b " .. branch .. " -- " .. arbitrary_path .. " main")

			local cmd = "cd " .. project .. "/main && lua " .. original_cwd .. "/src/main.lua r my-feature 2>&1"
			local output, code = wt.run_cmd(cmd)
			assert.are.equal(0, code)
			assert.is_truthy(output:match("[Rr]emoved"))
		end)
	end)

	describe("usage errors", function()
		it("errors when no branch argument provided", function()
			if not temp_dir then
				pending("temp_dir not available")
				return
			end
			local project = create_wt_project("no-arg", { "main" })

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