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

-- Load main.lua as a module (it exports functions when required)
package.path = package.path .. ";./?.lua"
local wt = dofile("src/main.lua")

describe("escape_pattern", function()
	it("escapes percent sign", function()
		assert.are.equal("100%%", wt.escape_pattern("100%"))
	end)

	it("escapes dot (wildcard)", function()
		assert.are.equal("file%.txt", wt.escape_pattern("file.txt"))
	end)

	it("escapes hyphen", function()
		assert.are.equal("my%-branch", wt.escape_pattern("my-branch"))
	end)

	it("escapes brackets", function()
		assert.are.equal("%[test%]", wt.escape_pattern("[test]"))
	end)

	it("escapes parentheses", function()
		assert.are.equal("func%(%)", wt.escape_pattern("func()"))
	end)

	it("escapes caret and dollar", function()
		assert.are.equal("%^start end%$", wt.escape_pattern("^start end$"))
	end)

	it("escapes asterisk and question mark", function()
		assert.are.equal("glob%*%?", wt.escape_pattern("glob*?"))
	end)

	it("escapes plus sign", function()
		assert.are.equal("a%+b", wt.escape_pattern("a+b"))
	end)

	it("leaves alphanumeric and slashes unchanged", function()
		assert.are.equal("feature/foo/bar123", wt.escape_pattern("feature/foo/bar123"))
	end)
end)

describe("parse_branch_remotes", function()
	describe("simple branches", function()
		it("parses single remote", function()
			local output = "  origin/main\n"
			local remotes = wt.parse_branch_remotes(output, "main")
			assert.are.same({ "origin" }, remotes)
		end)

		it("parses multiple remotes", function()
			local output = "  origin/main\n  upstream/main\n"
			local remotes = wt.parse_branch_remotes(output, "main")
			assert.are.same({ "origin", "upstream" }, remotes)
		end)

		it("returns empty for no matches", function()
			local output = "  origin/develop\n"
			local remotes = wt.parse_branch_remotes(output, "main")
			assert.are.same({}, remotes)
		end)

		it("handles empty output", function()
			local remotes = wt.parse_branch_remotes("", "main")
			assert.are.same({}, remotes)
		end)
	end)

	describe("slashy branches (the tricky case)", function()
		it("correctly parses feature/foo on origin", function()
			local output = "  origin/feature/foo\n"
			local remotes = wt.parse_branch_remotes(output, "feature/foo")
			assert.are.same({ "origin" }, remotes)
		end)

		it("correctly parses deeply nested branch", function()
			local output = "  upstream/user/alice/feature/auth\n"
			local remotes = wt.parse_branch_remotes(output, "user/alice/feature/auth")
			assert.are.same({ "upstream" }, remotes)
		end)

		it("handles multiple remotes with slashy branch", function()
			local output = "  origin/feature/auth\n  upstream/feature/auth\n"
			local remotes = wt.parse_branch_remotes(output, "feature/auth")
			assert.are.same({ "origin", "upstream" }, remotes)
		end)

		it("does not confuse similar branch names", function()
			local output = "  origin/feature/foo\n  origin/feature/foobar\n"
			local remotes = wt.parse_branch_remotes(output, "feature/foo")
			assert.are.same({ "origin" }, remotes)
		end)
	end)

	describe("special characters in branch names", function()
		it("handles dots in branch name", function()
			local output = "  origin/release/1.2.3\n"
			local remotes = wt.parse_branch_remotes(output, "release/1.2.3")
			assert.are.same({ "origin" }, remotes)
		end)

		it("handles hyphens in branch name", function()
			local output = "  origin/fix/bug-123-thing\n"
			local remotes = wt.parse_branch_remotes(output, "fix/bug-123-thing")
			assert.are.same({ "origin" }, remotes)
		end)

		it("handles underscores", function()
			local output = "  origin/my_feature_branch\n"
			local remotes = wt.parse_branch_remotes(output, "my_feature_branch")
			assert.are.same({ "origin" }, remotes)
		end)
	end)

	describe("edge cases", function()
		it("handles extra whitespace", function()
			local output = "    origin/main   \n"
			local remotes = wt.parse_branch_remotes(output, "main")
			assert.are.same({ "origin" }, remotes)
		end)

		it("ignores HEAD pointer lines", function()
			-- git branch -r sometimes shows: origin/HEAD -> origin/main
			local output = "  origin/HEAD -> origin/main\n  origin/main\n"
			local remotes = wt.parse_branch_remotes(output, "main")
			assert.are.same({ "origin" }, remotes)
		end)

		it("handles branch name that looks like remote/branch", function()
			local output = "  upstream/origin/main\n"
			local remotes = wt.parse_branch_remotes(output, "origin/main")
			assert.are.same({ "upstream" }, remotes)
		end)
	end)
end)

describe("parse_worktree_list", function()
	describe("basic parsing", function()
		it("parses single worktree with branch", function()
			local output = [[worktree /home/user/project/main
HEAD abc123def456
branch refs/heads/main
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal(1, #worktrees)
			assert.are.equal("/home/user/project/main", worktrees[1].path)
			assert.are.equal("main", worktrees[1].branch)
			assert.are.equal("abc123def456", worktrees[1].head)
		end)

		it("parses multiple worktrees", function()
			local output = [[worktree /home/user/project/.bare
bare

worktree /home/user/project/main
HEAD abc123
branch refs/heads/main

worktree /home/user/project/feature/auth
HEAD def456
branch refs/heads/feature/auth
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal(3, #worktrees)

			assert.are.equal("/home/user/project/.bare", worktrees[1].path)
			assert.is_true(worktrees[1].bare)

			assert.are.equal("/home/user/project/main", worktrees[2].path)
			assert.are.equal("main", worktrees[2].branch)

			assert.are.equal("/home/user/project/feature/auth", worktrees[3].path)
			assert.are.equal("feature/auth", worktrees[3].branch)
		end)
	end)

	describe("special states", function()
		it("parses detached HEAD worktree", function()
			local output = [[worktree /home/user/project/detached
HEAD abc123
detached
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal(1, #worktrees)
			assert.is_true(worktrees[1].detached)
			assert.is_nil(worktrees[1].branch)
		end)

		it("parses bare repository entry", function()
			local output = [[worktree /home/user/project/.bare
bare
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal(1, #worktrees)
			assert.is_true(worktrees[1].bare)
		end)
	end)

	describe("branch name handling", function()
		it("strips refs/heads/ prefix", function()
			local output = [[worktree /path
HEAD abc
branch refs/heads/my-branch
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal("my-branch", worktrees[1].branch)
		end)

		it("handles slashy branch names", function()
			local output = [[worktree /path
HEAD abc
branch refs/heads/feature/auth/oauth2
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal("feature/auth/oauth2", worktrees[1].branch)
		end)
	end)

	describe("edge cases", function()
		it("handles empty output", function()
			local worktrees = wt.parse_worktree_list("")
			assert.are.same({}, worktrees)
		end)

		it("handles paths with spaces", function()
			local output = [[worktree /home/user/my project/main
HEAD abc
branch refs/heads/main
]]
			local worktrees = wt.parse_worktree_list(output)
			assert.are.equal("/home/user/my project/main", worktrees[1].path)
		end)
	end)
end)

describe("path_inside", function()
	describe("exact matches", function()
		it("returns true for identical paths", function()
			assert.is_true(wt.path_inside("/home/user", "/home/user"))
		end)

		it("returns true when both have trailing slashes", function()
			assert.is_true(wt.path_inside("/home/user/", "/home/user/"))
		end)

		it("returns true with mixed trailing slashes", function()
			assert.is_true(wt.path_inside("/home/user", "/home/user/"))
			assert.is_true(wt.path_inside("/home/user/", "/home/user"))
		end)
	end)

	describe("containment", function()
		it("returns true for direct child", function()
			assert.is_true(wt.path_inside("/home/user/project", "/home/user"))
		end)

		it("returns true for deep nesting", function()
			assert.is_true(wt.path_inside("/home/user/project/src/lib", "/home/user"))
		end)

		it("returns false for sibling", function()
			assert.is_false(wt.path_inside("/home/alice", "/home/bob"))
		end)

		it("returns false for parent", function()
			assert.is_false(wt.path_inside("/home", "/home/user"))
		end)

		it("returns false for unrelated paths", function()
			assert.is_false(wt.path_inside("/var/log", "/home/user"))
		end)
	end)

	describe("prefix edge cases", function()
		it("does not match partial directory names", function()
			-- /home/user-backup is NOT inside /home/user
			assert.is_false(wt.path_inside("/home/user-backup", "/home/user"))
		end)

		it("does not match substring prefixes", function()
			assert.is_false(wt.path_inside("/home/username", "/home/user"))
		end)

		it("correctly handles similar names", function()
			assert.is_false(wt.path_inside("/project-old/src", "/project"))
			assert.is_true(wt.path_inside("/project/src", "/project"))
		end)
	end)

	describe("root paths", function()
		it("everything is inside root", function()
			assert.is_true(wt.path_inside("/home/user", "/"))
		end)

		it("root is inside root", function()
			assert.is_true(wt.path_inside("/", "/"))
		end)
	end)
end)

describe("summarize_hooks", function()
	describe("single hook type", function()
		it("summarizes copy hooks", function()
			local hooks = { copy = { ".env", "Makefile" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("copy: .env, Makefile", summary)
		end)

		it("summarizes symlink hooks", function()
			local hooks = { symlink = { "node_modules" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("symlink: node_modules", summary)
		end)

		it("summarizes run hooks", function()
			local hooks = { run = { "npm install", "make setup" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("run: npm install, make setup", summary)
		end)
	end)

	describe("multiple hook types", function()
		it("combines all types with semicolons", function()
			local hooks = {
				copy = { ".env" },
				symlink = { "node_modules" },
				run = { "npm install" },
			}
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("copy: .env; symlink: node_modules; run: npm install", summary)
		end)
	end)

	describe("truncation", function()
		it("shows first 3 items with count for copy", function()
			local hooks = { copy = { "a", "b", "c", "d", "e" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("copy: a, b, c (+2 more)", summary)
		end)

		it("shows first 3 items with count for symlink", function()
			local hooks = { symlink = { "1", "2", "3", "4" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("symlink: 1, 2, 3 (+1 more)", summary)
		end)

		it("shows first 3 items with count for run", function()
			local hooks = { run = { "cmd1", "cmd2", "cmd3", "cmd4", "cmd5", "cmd6" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("run: cmd1, cmd2, cmd3 (+3 more)", summary)
		end)

		it("does not add suffix for exactly 3 items", function()
			local hooks = { copy = { "a", "b", "c" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("copy: a, b, c", summary)
		end)
	end)

	describe("edge cases", function()
		it("returns empty string for empty hooks", function()
			local summary = wt.summarize_hooks({})
			assert.are.equal("", summary)
		end)

		it("returns empty string for nil hooks table", function()
			local summary = wt.summarize_hooks({})
			assert.are.equal("", summary)
		end)

		it("skips empty arrays", function()
			local hooks = { copy = {}, run = { "npm install" } }
			local summary = wt.summarize_hooks(hooks)
			assert.are.equal("run: npm install", summary)
		end)
	end)
end)
