test_that("getopt works as expected", {
	# fmt: skip
	spec <- matrix(c(
		"verbose", "v", 2, "integer",
		"help"   , "h", 0, "logical",
		"dummy1" , "d", 0, "logical",
		"dummy2" , "e", 2, "logical",
		"count"  , "c", 1, "integer",
		"mean"   , "m", 1, "double",
		"sd"     , "s", 1, "double",
		"output" , "O", 1, "character"
	), ncol = 4, byrow = TRUE)
	expect_equal(
		sort_list(getopt(spec, c("-c", "-1", "-m", "-1.2"))),
		sort_list(list(count = -1, mean = -1.2))
	)
	expect_equal(
		sort_list(getopt(spec, c("-v", "-m", "3"))),
		sort_list(list(verbose = TRUE, mean = 3))
	)
	expect_equal(
		sort_list(getopt(spec, c("-m", "3", "-v"))),
		sort_list(list(mean = 3, verbose = TRUE))
	)
	expect_equal(
		sort_list(getopt(spec, c("-m", "3", "-v", "2", "-v"))),
		sort_list(list(mean = 3, verbose = TRUE))
	)
	expect_equal(
		sort_list(getopt(spec, c("-O", "-", "-m", "3"))),
		sort_list(list(output = "-", mean = 3))
	)
	expect_equal(
		sort_list(getopt(spec, c("-O", "-", "-m", "3"))),
		sort_list(getopt(spec, c("-m", "3", "-O", "-")))
	)
	expect_equal(sort_list(getopt(spec, c("-de"))), sort_list(getopt(spec, c("-ed"))))
	expect_equal(
		sort_list(getopt(spec, c("-de"))),
		sort_list(list(dummy1 = TRUE, dummy2 = TRUE))
	)
	expect_warning(
		expect_equal(
			sort_list(getopt(spec, c("-de", "1"))),
			sort_list(list(dummy1 = TRUE, dummy2 = NA))
		)
	)
	expect_equal(
		sort_list(getopt(spec, c("--verbose"))),
		sort_list(list(verbose = TRUE))
	)
	expect_equal(
		sort_list(getopt(spec, c("--verbose", "1", "--help"))),
		sort_list(list(verbose = 1L, help = TRUE))
	)
	expect_equal(
		sort_list(getopt(spec, c("--verbose", "--mean", "5"))),
		sort_list(list(verbose = TRUE, mean = 5))
	)
	expect_equal(
		sort_list(getopt(spec, c("--mean=5"))),
		sort_list(list(mean = 5))
	)
	expect_equal(
		sort_list(getopt(spec, c("--verbose", "--mean=5", "--sd", "5"))),
		sort_list(list(verbose = TRUE, mean = 5, sd = 5))
	)
	expect_equal(
		sort_list(getopt(spec, c("--verbose", "--mean=5", "--sd", "5"))),
		sort_list(getopt(spec, c("--mean=5", "--sd", "5", "--verbose")))
	)

	# fmt: skip
	spec <- c(
		"date"     , "d", 1, "character",
		"help"     , "h", 0, "logical",
		"getdata"  , "g", 0, "logical",
		"market"   , "m", 1, "character",
		"threshold", "t", 1, "double"
	)
	spec2 <- matrix(spec, ncol = 4, byrow = TRUE)
	# should give warning is spec is not matrix
	expect_warning(getopt(spec, c("--date", "20080421", "--market", "YM", "--getdata")))
	expect_equal(
		sort_list(getopt(spec2, c("--date", "20080421", "--market", "YM", "--getdata"))),
		sort_list(list(date = "20080421", market = "YM", getdata = TRUE))
	)
	expect_equal(
		sort_list(getopt(spec2, c("--date", "20080421", "--market", "YM", "--getdata"))),
		sort_list(getopt(spec2, c("--date", "20080421", "--getdata", "--market", "YM")))
	)
	expect_snapshot(cat(getopt(spec2, usage = TRUE)))
})
test_that("numeric is cast to double", {
	# Feature reported upstream (optparse) by Miroslav Posta
	spec <- matrix(c("count", "c", 1, "integer"), ncol = 4, byrow = TRUE)
	opt <- getopt(spec, c("-c", "-55"))
	expect_equal(typeof(opt$count), "integer")

	spec <- matrix(c("count", "c", 1, "numeric"), ncol = 4, byrow = TRUE)
	opt <- getopt(spec, c("-c", "-55.0"))
	expect_equal(typeof(opt$count), "double")
})
test_that("action strings work as alternatives to 0/1/2", {
	# fmt: skip
	spec <- matrix(c(
		"foo", "f", "store_true"    , "logical",
		"bar", "b", "store"         , "character",
		"biz", "z", "store_optional", "logical"
	), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, "-f")$foo, TRUE)
	expect_equal(getopt(spec, c("-b", "hello"))$bar, "hello")
	expect_equal(getopt(spec, "-z")$biz, TRUE)
})
test_that("store_false stores FALSE", {
	spec <- matrix(c("verbose", "v", "store_false", "logical"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, "-v")$verbose, FALSE)
	expect_equal(getopt(spec, "--verbose")$verbose, FALSE)
})
test_that("count increments each time flag is used", {
	spec <- matrix(c("verbose", "v", "count", "integer"), ncol = 4, byrow = TRUE)
	expect_null(getopt(spec, character(0))$verbose)
	expect_equal(getopt(spec, "-v")$verbose, 1L)
	expect_equal(getopt(spec, c("-v", "-v", "-v"))$verbose, 3L)
	expect_equal(getopt(spec, "--verbose")$verbose, 1L)
	expect_equal(getopt(spec, c("--verbose", "--verbose"))$verbose, 2L)
})
test_that("append collects repeated flag values into a vector", {
	spec <- matrix(c("path", "p", "append", "character"), ncol = 4, byrow = TRUE)
	expect_null(getopt(spec, character(0))$path)
	expect_equal(getopt(spec, c("-p", "foo"))$path, "foo")
	expect_equal(getopt(spec, c("-p", "foo", "-p", "bar"))$path, c("foo", "bar"))
	expect_equal(getopt(spec, c("--path", "foo", "--path", "bar"))$path, c("foo", "bar"))
	expect_equal(getopt(spec, c("--path=foo", "--path=bar"))$path, c("foo", "bar"))
	expect_snapshot(error = TRUE, getopt(spec, "-p"))
})
test_that("-- separator puts remaining args in getoperand()", {
	spec <- matrix(c("verbose", "v", 0, "logical"), ncol = 4, byrow = TRUE)
	opt <- getopt(spec, c("--verbose", "--", "file1.txt", "file2.txt"))
	expect_true(opt$verbose)
	expect_equal(getoperand(opt), c("file1.txt", "file2.txt"))

	opt <- getopt(spec, c("--verbose", "--"))
	expect_equal(getoperand(opt), character(0))

	opt <- getopt(spec, c("--verbose"))
	expect_null(getoperand(opt))
})
test_that("operand = 'strict' collects non-flag tokens as operands", {
	# fmt: skip
	spec <- matrix(c(
		"verbose", "v", 0, "logical",
		"mean"   , "m", 1, "double"
	), ncol = 4, byrow = TRUE)
	opt <- getopt(spec, c("--verbose", "file1.txt", "--mean", "3", "file2.txt"), operand = "strict")
	expect_true(opt$verbose)
	expect_equal(opt$mean, 3)
	expect_equal(getoperand(opt), c("file1.txt", "file2.txt"))

	# unrecognized flags still error
	expect_snapshot(
		error = TRUE,
		getopt(spec, c("--unknown"), operand = "strict")
	)
	# after-- operands are also included
	opt <- getopt(spec, c("file1.txt", "--", "file2.txt"), operand = "strict")
	expect_equal(getoperand(opt), c("file1.txt", "file2.txt"))
})
test_that("data.frame spec is coerced to matrix", {
	spec <- as.data.frame(matrix(c("count", "c", 1, "integer"), ncol = 4, byrow = TRUE))
	expect_equal(getopt(spec, c("-c", "5"))$count, 5L)
})
test_that("empty strings are handled correctly for mandatory character arguments", {
	# fmt: skip
	spec <- matrix(c(
		"string", "s", 1, "character",
		"number", "9", 1, "numeric"
	), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("--string=foo"))$string, "foo")
	expect_equal(getopt(spec, c("--string="))$string, "")
	expect_equal(getopt(spec, c("-9", "-4"))$number, -4)
	expect_warning(getopt(spec, c("--number=")))
})

test_that("negative numbers are handled correctly", {
	# Issue if negative number preceded by space instead of '=' reported by Roman Zenka
	spec <- matrix(c("count", "c", 1, "integer"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("-c", "5"))$count, 5)
	spec <- matrix(c("count", "c", 1, "integer"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("-c", "-5"))$count, -5)
	spec <- matrix(c("count", "c", 1, "integer"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("--count=-1E5"))$count, -1E5)
	spec <- matrix(c("count", "c", 1, "double"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("-c", "-5"))$count, -5)
	spec <- matrix(c("count", "c", 1, "double"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("-c", "-1e5"))$count, -1e5)
	spec <- matrix(c("count", "c", 1, "double"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("--count=-1.2e5"))$count, -1.2e5)
	spec <- matrix(c("count", "c", 1, "double"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("--count", "-1e5"))$count, -1e5)
})

test_that("more helpful warnings upon incorrect input", {
	# Give more pointed warning upon wildly incorrect input
	expect_snapshot({
		spec <- matrix(c("count", "c", 1, "integer"), ncol = 4, byrow = TRUE)
		getopt(spec, c("-c", "hello"))$count
	})

	expect_snapshot(
		{
			spec <- NULL
			getopt(spec, "")

			spec <- c("foo", "f", 0)
			getopt(spec, "")

			spec <- matrix(c("foo", "f", 0, "integer"), ncol = 2)
			getopt(spec, "")

			spec <- matrix(c("foo", "f", 0, "integer", "bar", "b", 0, "integer"), ncol = 8)
			getopt(spec, "")

			spec <- matrix(c("foo", "f", "store_sideways", "logical"), ncol = 4, byrow = TRUE)
			getopt(spec, "")

			spec <- matrix(c("foo", "f", 0, "bignum"), ncol = 4, byrow = TRUE)
			getopt(spec, "")
		},
		error = TRUE
	)
})

test_that("don't throw error if multiple matches match one argument fully", {
	# test if partial name matches fully,
	# still throw error if multiple matches and doesn't match both fully
	# feature request from Jonas Zimmermann
	# fmt: skip
	spec <- matrix(c(
		"foo"   , "f", 0, "logical",
		"foobar", "b", 0, "logical",
		"biz"   , "z", 0, "logical"
	), ncol = 4, byrow = TRUE)
	expect_error(getopt(spec, c("--fo")))
	expect_equal(getopt(spec, c("--foo")), sort_list(list(foo = TRUE)), ignore_attr = TRUE)
})

test_that("sort_list works as expected", {
	expect_equal(sort_list(list(a = 3, b = 2)), sort_list(list(b = 2, a = 3)))
	expect_false(identical(sort_list(list(b = 3, a = 2)), list(b = 3, a = 2)))
	expect_false(identical(
		sort_list(list(b = list(b = 3, c = 2), a = 2)),
		list(b = list(c = 2, b = 3), a = 2)
	))
})

test_that("Use h flag for non help", {
	spec <- matrix(c("foo", "h", 0, "logical"), ncol = 4, byrow = TRUE)
	expect_equal(getopt(spec, c("-h")), sort_list(list(foo = TRUE)), ignore_attr = TRUE)

	spec <- matrix(c("foo", "h", 0, "logical", "help", "h", 0, "logical"), ncol = 4, byrow = TRUE)
	expect_error(getopt(spec, c("-h")), "redundant short names for flags")

	spec <- matrix(c("foo", "f", 0, "logical", "foo", "h", 0, "logical"), ncol = 4, byrow = TRUE)
	expect_error(getopt(spec, c("-h")), "redundant long names for flags")
})

test_that("Optional usage strings work as expected", {
	# fmt: skip
	spec <- matrix(c(
		"foo"   , "f", 0, "logical"  , "foo usage",
		"foobar", "b", 1, "character", "foobar usage",
		"biz"   , "z", 2, "logical"  , "biz usage",
		"number", "n", 1, "numeric"  , "number usage",
		"help"  , "h", 0, "logical"  , "help"
	), ncol = 5, byrow = TRUE)
	expect_snapshot(cat(getopt(spec, usage = TRUE)))
})

test_that("tests to get coverage up", {
	# fmt: skip
	spec <- matrix(c(
		"foo"   , "f", 0, "logical"  , "foo usage",
		"foobar", "b", 1, "character", "foobar usage",
		"biz"   , "z", 2, "logical"  , "biz usage",
		"number", "n", 1, "numeric"  , "number usage",
		"help"  , "h", 0, "logical"  , "help"
	), ncol = 5, byrow = TRUE)
	expect_error(getopt(spec, "--whodunit"), 'long flag "whodunit" is invalid')

	expect_error(getopt(spec, "--foo=4"), 'long flag "foo" accepts no arguments')

	# expect_error(getopt(spec, c("--foo", "4")), 'long flag "foo" accepts no arguments') # nolint

	expect_equal(getopt(spec, "--biz", "4"), sort_list(list(biz = TRUE)), ignore_attr = TRUE)

	expect_warning(getopt(spec, c("-n", "bar")), paste("double expected, got", dQuote("bar")))

	expect_warning(getopt(spec, c("--number=bar")), paste("double expected, got", dQuote("bar")))

	expect_error(getopt(spec, "-n"), 'flag `n` requires an argument')

	expect_error(getopt(spec, "-p"), 'short flag "p" is invalid')

	expect_error(getopt(spec, "-nh"), 'flag `n` requires an argument')
	expect_error(getopt(spec, "-fn"), 'flag `n` requires an argument')

	expect_error(
		getopt(spec, c("--number", 3, 4)),
		'"4" is not a valid option, or does not support an argument'
	)
	expect_error(getopt(spec, c("-b")), 'flag `b` requires an argument')
	expect_error(getopt(spec, c("--foobar")), 'flag `foobar` requires an argument')
	expect_error(getopt(spec, c("--foobar", "--help")), 'flag `foobar` requires an argument')

	expect_warning(getopt(spec, c("-n", "-")), paste("double expected, got", dQuote("-")))
})
test_that("debug output", {
	spec <- matrix(
		c("foo", "f", 0L, "logical", "bar", "b", 1L, "character", "num", "n", 2L, "double"),
		ncol = 4L,
		byrow = TRUE
	)
	expect_snapshot(. <- getopt(spec, c("--foo", "--bar", "baz"), debug = TRUE))
	expect_snapshot(. <- getopt(spec, c("-fn", "2"), debug = TRUE))
	expect_snapshot(. <- getopt(spec, c("-b", "-"), debug = TRUE))
	expect_snapshot(. <- getopt(spec, c("--foo", "--", "file1.txt", "file2.txt"), debug = TRUE))
})
