using Test
using YiemAgent
using YiemAgent.toolRegistry
using YiemAgent.type

# Path to the real tools directory
TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")

@testset "loadTools with ToolStore" begin

    # ------------------------------------------------------------------ #
    # 1. loadTools throws on non-existent directory                      #
    # ------------------------------------------------------------------ #
    store = ToolStore(name="test1")
    @test_throws ArgumentError loadTools(store, "/nonexistent/dir/that/does/not/exist")

    # ------------------------------------------------------------------ #
    # 2. loadTools throws if a .jl file does not define getTool()        #
    #    Must run BEFORE any other loadTools call (getTool binding       #
    #    persists in module scope after include()).                      #
    # ------------------------------------------------------------------ #
    bad_dir = mktempdir()
    write(joinpath(bad_dir, "noTool.jl"), "x = 42\n")
    @test_throws ArgumentError loadTools(store, bad_dir)

    # ------------------------------------------------------------------ #
    # 3. loadTools loads actual tool files from src/tools/               #
    # ------------------------------------------------------------------ #
    store2 = ToolStore(name="test2")
    loaded = loadTools(store2, TOOLS_DIR)
    @test !isempty(loaded)
    @test length(loaded) == 3

    names = [k for k in keys(loaded)]
    @test "getTime" in names
    @test "getWeather" in names
    @test "writeTool" in names

    # ------------------------------------------------------------------ #
    # 4. loadTools returns tools sorted alphabetically by filename       #
    #    (getTime.jl < getWeather.jl < writeTool.jl)                      #
    #    because 'T' < 'W' in ASCII                                       #
    # ------------------------------------------------------------------ #
    @test collect(keys(loaded))[1] == "getTime"
    @test collect(keys(loaded))[2] == "getWeather"
    @test collect(keys(loaded))[3] == "writeTool"

    # ------------------------------------------------------------------ #
    # 5. Verify loaded tool fields are correct                           #
    # ------------------------------------------------------------------ #
    # getTime
    time_tool = loaded["getTime"]
    @test time_tool.name == "getTime"
    @test time_tool.label == "Time Lookup"
    @test time_tool.validateRequiredArgs !== nothing
    @test time_tool.parallelToolExecute == false
    @test time_tool.inputSchema["required"] == Any[]

    # getWeather
    weather = loaded["getWeather"]
    @test weather.name == "getWeather"
    @test weather.label == "Weather Lookup"
    @test weather.execute !== nothing
    @test weather.parallelToolExecute == false
    @test weather.inputSchema["required"] == ["city"]

    # writeTool
    wt = loaded["writeTool"]
    @test wt.name == "writeTool"
    @test wt.label == "Create Tool"
    @test wt.execute !== nothing
    @test "name" in wt.inputSchema["required"]
    @test "executeCode" in wt.inputSchema["required"]

    # ------------------------------------------------------------------ #
    # 6. Tool execution returns valid results                            #
    # ------------------------------------------------------------------ #
    sig = nothing
    op = x -> x  # no-op partial result callback

    # execute getTime
    result_t = time_tool.execute("call-1", Dict{String,Any}("city" => "Tokyo"), sig, op)
    @test result_t isa agentToolResult
    @test result_t.content[1] isa textContent
    @test occursin("Tokyo", result_t.content[1].text)

    # execute getTime with timezone
    result_tz = time_tool.execute("call-2", Dict{String,Any}("timezone" => "America/New_York"), sig, op)
    @test result_tz isa agentToolResult
    @test occursin("America/New_York", result_tz.content[1].text)

    # execute getWeather
    result_w = weather.execute("call-3", Dict{String,Any}("city" => "Bangkok"), sig, op)
    @test result_w isa agentToolResult
    @test result_w.content[1] isa textContent
    @test occursin("Bangkok", result_w.content[1].text)

    # execute getWeather with units
    result_w2 = weather.execute("call-4", Dict{String,Any}("city" => "London", "units" => "fahrenheit"), sig, op)
    @test occursin("72°F", result_w2.content[1].text)

    # ------------------------------------------------------------------ #
    # 7. getTools / registerTool / clearTools (per-store isolation)      #
    # ------------------------------------------------------------------ #
    store3 = ToolStore(name="test3")
    registry_tools = getTools(store3)
    @test isempty(registry_tools)

    # listTool is not auto-registered anymore — each store starts empty
    # Register tools manually
    registerTool(store3, loaded["getTime"])
    registerTool(store3, loaded["getWeather"])
    registerTool(store3, loaded["writeTool"])

    reg = getTools(store3)
    @test !isempty(reg)
    @test "getTime" in keys(reg)
    @test "getWeather" in keys(reg)
    @test "writeTool" in keys(reg)
    @test collect(keys(reg))[1] == "getTime"
    @test collect(keys(reg))[2] == "getWeather"
    @test collect(keys(reg))[3] == "writeTool"

    clearTools(store3)
    @test isempty(getTools(store3))

    test_tool = agentTool(
        name = "manualTool",
        label = "Manual Tool",
        description = "Registered manually",
        inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]),
        execute = (toolCallId, args, signal, onPartialResult) ->
            agentToolResult([textContent("manual")], Dict{Any,Any}(), nothing, false),
        prepareArguments = nothing,
        validateRequiredArgs = nothing,
        parallelToolExecute = true
    )
    registerTool(store3, test_tool)
    reg = getTools(store3)
    @test haskey(reg, "manualTool")
    @test length(reg) == 1
    @test reg["manualTool"].parallelToolExecute == true

    # ------------------------------------------------------------------ #
    # 8. getTools returns direct reference (mutations affect registry)   #
    # ------------------------------------------------------------------ #
    copy1 = getTools(store3)
    copy2 = getTools(store3)
    @test copy1 === copy2  # same reference, not a deep copy
    empty!(copy1)
    @test isempty(getTools(store3))  # mutation propagates

    # ------------------------------------------------------------------ #
    # 9. Per-store isolation — two stores don't share tools              #
    # ------------------------------------------------------------------ #
    storeA = ToolStore(name="isolationA")
    storeB = ToolStore(name="isolationB")

    registerTool(storeA, loaded["getTime"])
    registerTool(storeB, loaded["getWeather"])

    regA = getTools(storeA)
    regB = getTools(storeB)

    @test "getTime" in keys(regA)
    @test "getWeather" ∉ keys(regA)
    @test "getWeather" in keys(regB)
    @test "getTime" ∉ keys(regB)

    clearTools(storeA)
    @test isempty(getTools(storeA))
    @test !isempty(getTools(storeB))  # storeB unaffected
end
