using Test

import Neptune.ExpressionExplorer: SymbolsState, compute_symbolreferences, FunctionNameSignaturePair

@testset "Method signatures" begin


disjoint(x,y) = isempty(x ∩ y)

function mutually_disjoint(x, xs...)
    all(xs) do y
        disjoint(x,y)
    end && mutually_disjoint(xs...)
end
mutually_disjoint(x) = true

function methods_can_coexist(defs::Expr...)
    symstates = compute_symbolreferences.(defs)
    funcnamesigs = [keys(syms.funcdefs) for syms in symstates]
    mutually_disjoint(funcnamesigs...)
end


@testset "Different method signatures across cells" begin
    @test methods_can_coexist(
        :(f(x, y) = 1),
        :(f(x) = 2),
    )
    @test methods_can_coexist(
        :(f(x::A) = 1),
        :(f(x::B) = 2),
    )
    @test methods_can_coexist(
        :(f(x, y=1) = 1),
        :(f(x, y) = 2),
    )
    @test methods_can_coexist(
        :(f(x::e(f{g})=3) = 1),
        :(f(x::h where i) = 2),
    )
    @test methods_can_coexist(
        :(f(x::Tuple{X,T} where T) = 1),
        :(f(x::Tuple{X,T}) = 2),
    )
    @test methods_can_coexist(
        :(f(x::A) where A = 1),
        :(f(x::A) = 2),
    )
    @test methods_can_coexist(
        :(f(x::String, y) = 1),
        :(f(x, y::String) = 2),
    )
    @test methods_can_coexist(
        :(f(x::A) = 1),
        :(f(x::B) = 2),
    )
    @test methods_can_coexist(
        :(f(x, y...) = 1),
        :(f(x) = 2),
    )
    @test methods_can_coexist(
        :(f(x, y::Z...) = 1),
        :(f(x, y::X...) = 2),
    )

    # what is this called again?
    @test methods_can_coexist(
        :(f(x::T) where T = 1),
        :(f(x) = 2),
    )
end

@testset "Identical method signatures across cells" begin
    @test !methods_can_coexist(
        :(f(x) = 3),
        :(f(y) = 3),
    )
    @test !methods_can_coexist(
        :(f(x) = 3),
        :(f(y; z) = 3),
    )
    @test !methods_can_coexist(
        :(f(x) = 3),
        :(f(y::Any) = 3),
    )
    # function using built in type synonyms
    # like Int and Int64
    @assert string(Int) == "Int64" || string(Int) == "Int32"
    @test_broken !methods_can_coexist(
        :(f(x::Int) = 3),
        Meta.parse("f(x::$(string(Int))) = 4"),
    )

    # multiple methods per cell
    @test !methods_can_coexist(
        :(f(x::Int) = 1; f(x::String) = 2),
        :(f(x::String) = 3; f(x::Vector) = 4),
    )

    # methods only differing in key word arguments
    @test !methods_can_coexist(
        :(f() = 1),
        :(f(; x) = 3),
    )
end
end