import assert from "node:assert/strict";
import { test } from "node:test";
import { OpenAICompatibleModelClient, CodingAgent, ToolRegistry, } from "@sean.holung/minicode-sdk";
import { createTestAgentConfig } from "./test-utils.js";
import { loadAgentConfig } from "../src/agent/config.js";
// ---------------------------------------------------------------------------
// Config: REASONING_EFFORT env var
// ---------------------------------------------------------------------------
test("loadAgentConfig parses REASONING_EFFORT env var", async () => {
    const prev = process.env.REASONING_EFFORT;
    try {
        process.env.REASONING_EFFORT = "high";
        const config = await loadAgentConfig("/tmp");
        assert.equal(config.reasoningEffort, "high");
    }
    finally {
        if (prev === undefined) {
            delete process.env.REASONING_EFFORT;
        }
        else {
            process.env.REASONING_EFFORT = prev;
        }
    }
});
test("loadAgentConfig ignores invalid REASONING_EFFORT values", async () => {
    const prev = process.env.REASONING_EFFORT;
    try {
        process.env.REASONING_EFFORT = "ultra";
        const config = await loadAgentConfig("/tmp");
        assert.equal(config.reasoningEffort, undefined);
    }
    finally {
        if (prev === undefined) {
            delete process.env.REASONING_EFFORT;
        }
        else {
            process.env.REASONING_EFFORT = prev;
        }
    }
});
test("loadAgentConfig leaves reasoningEffort undefined when env var is unset", async () => {
    const prev = process.env.REASONING_EFFORT;
    try {
        delete process.env.REASONING_EFFORT;
        const config = await loadAgentConfig("/tmp");
        assert.equal(config.reasoningEffort, undefined);
    }
    finally {
        if (prev === undefined) {
            delete process.env.REASONING_EFFORT;
        }
        else {
            process.env.REASONING_EFFORT = prev;
        }
    }
});
test("loadAgentConfig parses REASONING_MAX_TOKENS env var", async () => {
    const prev = process.env.REASONING_MAX_TOKENS;
    try {
        process.env.REASONING_MAX_TOKENS = "4000";
        const config = await loadAgentConfig("/tmp");
        assert.equal(config.reasoningMaxTokens, 4000);
    }
    finally {
        if (prev === undefined) {
            delete process.env.REASONING_MAX_TOKENS;
        }
        else {
            process.env.REASONING_MAX_TOKENS = prev;
        }
    }
});
test("loadAgentConfig ignores non-positive or invalid REASONING_MAX_TOKENS values", async () => {
    const prev = process.env.REASONING_MAX_TOKENS;
    try {
        for (const bad of ["0", "-100", "abc", ""]) {
            process.env.REASONING_MAX_TOKENS = bad;
            const config = await loadAgentConfig("/tmp");
            assert.equal(config.reasoningMaxTokens, undefined, `expected undefined for input ${JSON.stringify(bad)}`);
        }
    }
    finally {
        if (prev === undefined) {
            delete process.env.REASONING_MAX_TOKENS;
        }
        else {
            process.env.REASONING_MAX_TOKENS = prev;
        }
    }
});
test("loadAgentConfig normalizes REASONING_EFFORT case", async () => {
    const prev = process.env.REASONING_EFFORT;
    try {
        process.env.REASONING_EFFORT = "MEDIUM";
        const config = await loadAgentConfig("/tmp");
        assert.equal(config.reasoningEffort, "medium");
    }
    finally {
        if (prev === undefined) {
            delete process.env.REASONING_EFFORT;
        }
        else {
            process.env.REASONING_EFFORT = prev;
        }
    }
});
// ---------------------------------------------------------------------------
// OpenAI-compatible client: reasoning field in request body
// ---------------------------------------------------------------------------
test("openai-compatible client includes reasoning field when reasoningEffort is set", async () => {
    let capturedBody = "";
    const fetchImpl = async (_input, init) => {
        capturedBody = String(init?.body ?? "");
        return new Response(JSON.stringify({
            choices: [
                {
                    finish_reason: "stop",
                    message: { content: "Hello", tool_calls: [] },
                },
            ],
            usage: { prompt_tokens: 10, completion_tokens: 5 },
        }), { status: 200, headers: { "content-type": "application/json" } });
    };
    const client = new OpenAICompatibleModelClient({
        baseUrl: "http://localhost:1234/v1",
        fetchImpl,
    });
    await client.chat({
        model: "test-model",
        system: "System",
        messages: [{ role: "user", content: "Hi" }],
        tools: [],
        maxTokens: 1024,
        reasoningEffort: "high",
    });
    const parsed = JSON.parse(capturedBody);
    assert.deepEqual(parsed.reasoning, { effort: "high" });
});
test("openai-compatible client omits reasoning field when reasoningEffort is not set", async () => {
    let capturedBody = "";
    const fetchImpl = async (_input, init) => {
        capturedBody = String(init?.body ?? "");
        return new Response(JSON.stringify({
            choices: [
                {
                    finish_reason: "stop",
                    message: { content: "Hello", tool_calls: [] },
                },
            ],
            usage: { prompt_tokens: 10, completion_tokens: 5 },
        }), { status: 200, headers: { "content-type": "application/json" } });
    };
    const client = new OpenAICompatibleModelClient({
        baseUrl: "http://localhost:1234/v1",
        fetchImpl,
    });
    await client.chat({
        model: "test-model",
        system: "System",
        messages: [{ role: "user", content: "Hi" }],
        tools: [],
        maxTokens: 1024,
    });
    const parsed = JSON.parse(capturedBody);
    assert.equal(parsed.reasoning, undefined);
});
test("openai-compatible client sends all valid effort levels", async () => {
    const levels = ["xhigh", "high", "medium", "low", "minimal", "none"];
    for (const level of levels) {
        let capturedBody = "";
        const fetchImpl = async (_input, init) => {
            capturedBody = String(init?.body ?? "");
            return new Response(JSON.stringify({
                choices: [
                    {
                        finish_reason: "stop",
                        message: { content: "Ok", tool_calls: [] },
                    },
                ],
                usage: { prompt_tokens: 5, completion_tokens: 2 },
            }), { status: 200, headers: { "content-type": "application/json" } });
        };
        const client = new OpenAICompatibleModelClient({
            baseUrl: "http://localhost:1234/v1",
            fetchImpl,
        });
        await client.chat({
            model: "test-model",
            system: "System",
            messages: [{ role: "user", content: "Hi" }],
            tools: [],
            maxTokens: 1024,
            reasoningEffort: level,
        });
        const parsed = JSON.parse(capturedBody);
        assert.deepEqual(parsed.reasoning, { effort: level }, `Expected reasoning.effort="${level}"`);
    }
});
// ---------------------------------------------------------------------------
// CodingAgent: get/set reasoning effort
// ---------------------------------------------------------------------------
test("CodingAgent.getReasoningEffort returns config value", () => {
    const config = {
        ...createTestAgentConfig("/tmp"),
        reasoningEffort: "medium",
    };
    const mockClient = {
        async chat() {
            return {
                text: "done",
                toolCalls: [],
                stopReason: "end_turn",
                usage: { inputTokens: 0, outputTokens: 0 },
            };
        },
    };
    const agent = new CodingAgent({
        config,
        modelClient: mockClient,
        toolRegistry: new ToolRegistry([]),
    });
    assert.equal(agent.getReasoningEffort(), "medium");
});
test("CodingAgent.setReasoningEffort updates reasoning effort", () => {
    const config = createTestAgentConfig("/tmp");
    const mockClient = {
        async chat() {
            return {
                text: "done",
                toolCalls: [],
                stopReason: "end_turn",
                usage: { inputTokens: 0, outputTokens: 0 },
            };
        },
    };
    const agent = new CodingAgent({
        config,
        modelClient: mockClient,
        toolRegistry: new ToolRegistry([]),
    });
    assert.equal(agent.getReasoningEffort(), undefined);
    agent.setReasoningEffort("high");
    assert.equal(agent.getReasoningEffort(), "high");
    agent.setReasoningEffort("none");
    assert.equal(agent.getReasoningEffort(), "none");
    agent.setReasoningEffort(undefined);
    assert.equal(agent.getReasoningEffort(), undefined);
});
// ---------------------------------------------------------------------------
// Agent loop passes reasoningEffort to model client
// ---------------------------------------------------------------------------
test("agent loop passes reasoningEffort to model client chat call", async () => {
    let capturedReasoningEffort;
    const config = {
        ...createTestAgentConfig("/tmp"),
        reasoningEffort: "low",
    };
    const mockClient = {
        async chat(params) {
            capturedReasoningEffort = params.reasoningEffort;
            return {
                text: "Response",
                toolCalls: [],
                stopReason: "end_turn",
                usage: { inputTokens: 10, outputTokens: 5 },
            };
        },
    };
    const agent = new CodingAgent({
        config,
        modelClient: mockClient,
        toolRegistry: new ToolRegistry([]),
    });
    await agent.runTurn("Hello");
    assert.equal(capturedReasoningEffort, "low");
});
test("agent loop omits reasoningEffort when not configured", async () => {
    let capturedParams = {};
    const config = createTestAgentConfig("/tmp");
    const mockClient = {
        async chat(params) {
            capturedParams = params;
            return {
                text: "Response",
                toolCalls: [],
                stopReason: "end_turn",
                usage: { inputTokens: 10, outputTokens: 5 },
            };
        },
    };
    const agent = new CodingAgent({
        config,
        modelClient: mockClient,
        toolRegistry: new ToolRegistry([]),
    });
    await agent.runTurn("Hello");
    assert.equal(capturedParams.reasoningEffort, undefined);
});
// ---------------------------------------------------------------------------
// Agent loop passes toolChoice to model client
// ---------------------------------------------------------------------------
test("agent loop passes toolChoice to model client chat call when set", async () => {
    let capturedToolChoice = "<unset>";
    const config = {
        ...createTestAgentConfig("/tmp"),
        toolChoice: "required",
    };
    const mockClient = {
        async chat(params) {
            capturedToolChoice = params.toolChoice;
            return {
                text: "Response",
                toolCalls: [],
                stopReason: "end_turn",
                usage: { inputTokens: 10, outputTokens: 5 },
            };
        },
    };
    const agent = new CodingAgent({
        config,
        modelClient: mockClient,
        toolRegistry: new ToolRegistry([]),
    });
    await agent.runTurn("Hello");
    assert.equal(capturedToolChoice, "required");
});
test("agent loop omits toolChoice when not configured", async () => {
    let capturedParams = {};
    const mockClient = {
        async chat(params) {
            capturedParams = params;
            return {
                text: "Response",
                toolCalls: [],
                stopReason: "end_turn",
                usage: { inputTokens: 10, outputTokens: 5 },
            };
        },
    };
    const agent = new CodingAgent({
        config: createTestAgentConfig("/tmp"),
        modelClient: mockClient,
        toolRegistry: new ToolRegistry([]),
    });
    await agent.runTurn("Hello");
    assert.equal(capturedParams.toolChoice, undefined);
});
