@testitem "resolve (empty)" begin
    include("setup.jl")
    @test CondaPkg.resolve() === nothing
    @test occursin("(empty)", status())
end

@testitem "Null backend" begin
    include("setup.jl")
    if isnull
        @test CondaPkg.backend() == :Null
        @test CondaPkg.activate!(copy(ENV)) == ENV
        @test occursin("Null", status())
        @test_throws ErrorException CondaPkg.envdir()
    else
        @test true
    end
end

@testitem "add/remove channel" begin
    include("setup.jl")
    @test !occursin("conda-forge", status())
    CondaPkg.add_channel("conda-forge")
    @test occursin("conda-forge", status())
    CondaPkg.rm_channel("conda-forge")
    @test !occursin("conda-forge", status())
end

@testitem "install python" begin
    include("setup.jl")
    @test !occursin("python", status())
    CondaPkg.add("python", version = "==3.10.2")
    isnull || CondaPkg.withenv() do
        pythonpath =
            joinpath(CondaPkg.envdir(), Sys.iswindows() ? "python.exe" : "bin/python")
        @test isfile(pythonpath)
    end
    @test occursin("python", status())
end

@testitem "install dev dependencies" begin
    include("setup.jl")
    # test that the dev dependencies in CondaPkg.toml get installed
    CondaPkg.withenv() do
        isnull || run(
            `python -c "import tomli, requests; assert requests.__version__ == '2.32.5'; assert tomli.__version__ == '2.2.1'"`,
        )
    end
    @test true
end

@testitem "install/remove python package" begin
    include("setup.jl")
    CondaPkg.add("python", version = "==3.10.2")
    # verify package isn't already installed
    @test !occursin("six", status())
    CondaPkg.withenv() do
        isnull || @test_throws Exception run(`python -c "import six"`)
    end

    # install package
    CondaPkg.add("six", version = "==1.16.0")
    @test occursin("six", status())
    @test occursin("(==1.16.0)", status())
    CondaPkg.withenv() do
        isnull || run(`python -c "import six"`)
    end
    @test occursin("v1.16.0", status()) == !isnull

    # remove package
    CondaPkg.rm("six")
    @test !occursin("six", status())
    CondaPkg.withenv() do
        isnull || @test_throws Exception run(`python -c "import six"`)
    end
end

@testitem "install/remove multiple python packages" begin
    include("setup.jl")
    CondaPkg.add("python", version = "==3.10.2")
    # verify package isn't already installed
    @test !occursin("jsonlines ", status())
    @test !occursin("cowpy", status())
    CondaPkg.withenv() do
        isnull || @test_throws Exception run(`python -c "import jsonlines"`)
        isnull || @test_throws Exception run(`python -c "import cowpy"`)
    end

    # install multiple packages
    CondaPkg.add(["jsonlines", "cowpy"])
    @test occursin("jsonlines", status())
    @test occursin("cowpy", status())

    # remove multiple packages
    CondaPkg.rm(["jsonlines", "cowpy"])
    @test !occursin("jsonlines ", status())
    @test !occursin("cowpy", status())
    CondaPkg.withenv() do
        isnull || @test_throws Exception run(`python -c "import jsonlines"`)
        isnull || @test_throws Exception run(`python -c "import cowpy"`)
    end
end

@testitem "pip install/remove python package" begin
    @testset "using $kind" for kind in ["pip", "uv"]
        include("setup.jl")
        CondaPkg.add("python", version = "==3.10.2")
        if kind == "uv"
            # TODO: there is an explicit flag for this now
            CondaPkg.add("uv")
        end
        # verify package isn't already installed
        @test !occursin("six", status())
        CondaPkg.withenv() do
            isnull || @test_throws Exception run(`python -c "import six"`)
        end

        # install package
        CondaPkg.add_pip("six", version = "==1.16.0")
        @test occursin("six", status())
        @test occursin("(==1.16.0)", status())
        CondaPkg.withenv() do
            isnull || run(`python -c "import six"`)
        end
        @test occursin("v1.16.0", status()) == !isnull

        # remove package
        CondaPkg.rm_pip("six")
        @test !occursin("six", status())
        CondaPkg.withenv() do
            isnull || @test_throws Exception run(`python -c "import six"`)
        end
    end
end

@testitem "pip install/remove python package with extras" begin
    include("setup.jl")
    CondaPkg.add("python", version = "==3.10.2")

    # verify package isn't already installed
    @test !occursin("pydantic", status())
    CondaPkg.withenv() do
        isnull || @test_throws Exception run(`python -c "import pydantic"`)
    end

    # install package without extras
    CondaPkg.add_pip("pydantic", version = "==2.9.2")
    @test occursin("pydantic", status())
    @test occursin("(==2.9.2)", status())
    CondaPkg.withenv() do
        isnull || run(`python -c "import pydantic"`)
        # fails on Windows sometimes - not sure why
        # probably email-validator is still installed from an earlier test
        isnull ||
            Sys.iswindows() ||
            @test_throws Exception run(`python -c "import email_validator"`)
    end
    @test occursin("v2.9.2", status()) == !isnull

    # install package with extras
    CondaPkg.add_pip("pydantic", version = "==2.9.2", extras = ["email"])
    @test occursin("pydantic", status())
    @test occursin("(==2.9.2, [email])", status())
    CondaPkg.withenv() do
        isnull || run(`python -c "import pydantic"`)
        isnull || run(`python -c "import email_validator"`)
    end
    @test occursin("v2.9.2", status()) == !isnull

    # remove package
    CondaPkg.rm_pip("pydantic")
    @test !occursin("pydantic", status())
    CondaPkg.withenv() do
        isnull || @test_throws Exception run(`python -c "import pydantic"`)
    end
end

@testitem "pip install/remove local python package" begin
    @testset "file $file $kwargs" for (file, kwargs) in [
        ("example-python-package", NamedTuple()),
        ("example_python_package-1.0.0-py3-none-any.whl", NamedTuple()),
        ("example_python_package-1.0.0.tar.gz", NamedTuple()),
        ("example-python-package", (editable = true,)),
    ]
        include("setup.jl")
        CondaPkg.add("python", version = "==3.10.2")
        # verify package isn't already installed
        @test !occursin("example-python-package", status())
        CondaPkg.withenv() do
            isnull ||
                @test_throws Exception run(`python -c "import example_python_package"`)
        end

        # install package
        path = "./test/data/$file"
        fullpath = abspath(dirname(CondaPkg.cur_deps_file()), path)
        @assert ispath(fullpath)
        editable = get(kwargs, :editable, false)
        CondaPkg.add_pip("example-python-package", version = "@$path"; kwargs...)
        @test occursin("example-python-package", status())
        if isempty(kwargs)
            @test occursin("(@$path)", status())
        else
            @test occursin("(@$path,", status())
        end
        @test occursin("editable", status()) == editable
        CondaPkg.withenv() do
            isnull || run(`python -c "import example_python_package"`)
        end
        @test occursin("v1.0.0", status()) == !isnull

        # check editability
        if editable && !isnull
            @assert isdir(fullpath)
            added_path = joinpath(fullpath, "src", "example_python_package", "added.py")
            # check a particular submodule does not exist
            rm(added_path; force = true)
            CondaPkg.withenv() do
                @test_throws Exception run(
                    `python -c "from example_python_package.added import foo"`,
                )
            end
            # now add it and check we can import it
            write(added_path, "foo = 12")
            CondaPkg.withenv() do
                run(`python -c "from example_python_package.added import foo"`)
            end
            # remove it again
            rm(added_path; force = true)
            CondaPkg.withenv() do
                @test_throws Exception run(
                    `python -c "from example_python_package.added import foo"`,
                )
            end
        end

        # remove package
        CondaPkg.rm_pip("example-python-package")
        @test !occursin("example-python-package", status())
        CondaPkg.withenv() do
            isnull ||
                @test_throws Exception run(`python -c "import example_python_package"`)
        end
    end
end

@testitem "install/remove executable package" begin
    include("setup.jl")
    if !isnull
        CondaPkg.add("uv", resolve = false)
        CondaPkg.resolve(force = true)
        exe_path = CondaPkg.which("uv")
        @test exe_path !== nothing
        @test isfile(exe_path)
        CondaPkg.rm("uv", resolve = false)
        CondaPkg.resolve(force = true)
        @test !isfile(exe_path)
    end
end

@testitem "install/remove libstdcxx-ng" begin
    include("setup.jl")
    CondaPkg.add("libstdcxx-ng", version = "<=julia", resolve = false)
    CondaPkg.resolve(force = true)
    CondaPkg.rm("libstdcxx-ng", resolve = false)
    CondaPkg.resolve(force = true)
    @test true
end

@testitem "install/remove libstdcxx" begin
    include("setup.jl")
    CondaPkg.add("libstdcxx", version = "<=julia", resolve = false)
    CondaPkg.resolve(force = true)
    CondaPkg.rm("libstdcxx", resolve = false)
    CondaPkg.resolve(force = true)
    @test true
end

@testitem "install/remove openssl" begin
    include("setup.jl")
    CondaPkg.add("openssl", version = "<=julia", resolve = false)
    CondaPkg.resolve(force = true)
    CondaPkg.rm("openssl", resolve = false)
    CondaPkg.resolve(force = true)
    @test true
end

@testitem "install cpython" begin
    @testset "$version" for version in ["3.12.*", "3.13.*"]
        include("setup.jl")
        CondaPkg.add("python", version = version, build = "**cpython**")
        @test occursin("python", status())
        @test occursin("($version, build=**cpython**)", status())
    end
end

@testitem "install non-existent package" begin
    include("setup.jl")

    if !isnull
        # First add a package to ensure we are in a resolved state
        CondaPkg.add("python", version = "==3.10.2")

        # Verify clean state
        @test !occursin("Not Resolved", status())
        @test !occursin("nonexistentpackage123xyz", status())
        @test occursin("python", status())

        # Try to add non-existent package and verify it throws
        @test_throws Exception CondaPkg.add("nonexistentpackage123xyz")

        # Verify that resolving failed
        @test !CondaPkg.STATE.resolved

        # is_resolved() re-checks that the old deps are still valid (except Pixi)
        @test CondaPkg.is_resolved() == !ispixi

        # Verify the deps file was reverted
        @test !occursin("nonexistentpackage123xyz", status())
        @test occursin("python", status())
    end
end

@testitem "external conda env" begin
    include("setup.jl")
    dn = string(tempname(), backend, Sys.KERNEL, VERSION)
    if !isnull && !ispixi
        CondaPkg.STATE.test_preferences["env"] = dn
        # create empty env
        CondaPkg.resolve()
        @test !occursin("ca-certificates", status())
        # add a package to specs and install it
        CondaPkg.add("ca-certificates"; interactive = true, force = true)  # force: spurious windows failures
        @test occursin("ca-certificates", status())
        CondaPkg.withenv() do
            @test isfile(
                CondaPkg.envdir(Sys.iswindows() ? "Library" : "", "ssl", "cacert.pem"),
            )
        end
        # remove a package from specs, it must remain installed because we use a shared centralized env
        CondaPkg.rm("ca-certificates"; interactive = true, force = true)
        @test !occursin("ca-certificates", status())  # removed from specs ...
        CondaPkg.withenv() do  # ... but still installed (shared env might be used by specs from alternate julia versions)
            @test isfile(
                CondaPkg.envdir(Sys.iswindows() ? "Library" : "", "ssl", "cacert.pem"),
            )
        end
    end
end

@testitem "shared env" begin
    include("setup.jl")
    if !isnull && !ispixi
        CondaPkg.STATE.test_preferences["env"] = "@my_env"
        CondaPkg.add("python"; force = true)
        @test CondaPkg.envdir() ==
              joinpath(Base.DEPOT_PATH[1], "conda_environments", "my_env")
        @test isfile(CondaPkg.envdir(Sys.iswindows() ? "python.exe" : "bin/python"))
    end
    if !isnull && !ispixi
        CondaPkg.STATE.test_preferences["env"] = "@/some/absolute/path"
        @test_throws ErrorException CondaPkg.add("python"; force = true)
    end
end

@testitem "update" begin
    include("setup.jl")
    CondaPkg.update()
    @test CondaPkg.is_resolved()
end

@testitem "gc" begin
    include("setup.jl")
    testgc && CondaPkg.gc()
    @test true
end

@testitem "validation" begin
    include("setup.jl")
    @test_throws Exception CondaPkg.add("!invalid!package!")
    @test_throws Exception CondaPkg.add_pip("!invalid!package!")
    @test_throws Exception CondaPkg.add("valid-package", version = "*invalid*version*")
    @test_throws Exception CondaPkg.add_pip("valid-package", version = "*invalid*version*")
    @test_throws Exception CondaPkg.add_channel("")
    @test !occursin("valid", status())
end
