/**
 * Test for request compression.
 *
 * Tests that:
 * 1. Large requests are automatically compressed
 * 2. Tool calls are preserved during compression
 * 3. Compression reduces request size effectively
 *
 * Usage:
 *   npx tsx test/compression.ts
 */

import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import type { AddressInfo } from "node:net";
import { generatePrivateKey } from "viem/accounts";

// Track which models were called and payment attempts
const modelCalls: string[] = [];
const paymentAttempts: number[] = [];
let requestBodies: string[] = [];

// Mock BlockRun API server
async function startMockServer(): Promise<{ port: number; close: () => Promise<void> }> {
  const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
    const chunks: Buffer[] = [];
    for await (const chunk of req) {
      chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
    }
    const body = Buffer.concat(chunks).toString();
    requestBodies.push(body);

    try {
      const parsed = JSON.parse(body) as { model?: string; messages?: Array<{ content: string }> };
      const model = parsed.model || "unknown";
      modelCalls.push(model);

      // Track payment attempt (x-payment header means payment was attempted)
      if (req.headers["x-payment"]) {
        paymentAttempts.push(Date.now());
      }

      // Success response
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(
        JSON.stringify({
          id: "chatcmpl-test",
          object: "chat.completion",
          created: Date.now(),
          model,
          choices: [
            {
              index: 0,
              message: { role: "assistant", content: `Response from ${model}` },
              finish_reason: "stop",
            },
          ],
          usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
        }),
      );
    } catch {
      res.writeHead(400, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ error: "Invalid request" }));
    }
  });

  return new Promise((resolve) => {
    server.listen(0, "127.0.0.1", () => {
      const addr = server.address() as AddressInfo;
      resolve({
        port: addr.port,
        close: () => new Promise<void>((res) => server.close(() => res())),
      });
    });
  });
}

// Import after mock server is ready
async function runTests() {
  const { startProxy } = await import("../src/proxy.js");

  console.log("\n═══ Compression & Size Validation Tests ═══\n");

  let passed = 0;
  let failed = 0;

  function assert(condition: boolean, msg: string) {
    if (condition) {
      console.log(`  ✓ ${msg}`);
      passed++;
    } else {
      console.error(`  ✗ FAIL: ${msg}`);
      failed++;
    }
  }

  // Start mock BlockRun API
  const mockApi = await startMockServer();
  console.log(`Mock API started on port ${mockApi.port}`);

  // Generate an ephemeral test wallet key
  const testWalletKey = generatePrivateKey();

  // Start ClawRouter proxy pointing to mock API
  const proxy = await startProxy({
    wallet: testWalletKey,
    apiBase: `http://127.0.0.1:${mockApi.port}`,
    port: 0,
    skipBalanceCheck: true,
    autoCompressRequests: true, // Enable compression
    compressionThresholdKB: 50, // Lower threshold for testing
    onReady: (port) => console.log(`ClawRouter proxy started on port ${port}`),
  });

  // Test 1: Small request - no compression needed
  {
    console.log("\n--- Test 1: Small request (no compression) ---");
    modelCalls.length = 0;
    paymentAttempts.length = 0;
    requestBodies.length = 0;

    const res = await fetch(`${proxy.baseUrl}/v1/chat/completions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "auto",
        messages: [{ role: "user", content: "Hello" }],
        max_tokens: 50,
      }),
    });

    assert(res.ok, `Small request succeeds: ${res.status}`);
    assert(modelCalls.length === 1, `One model called: ${modelCalls.join(", ")}`);
  }

  // Test 2: Large request - compression attempted
  {
    console.log("\n--- Test 2: Large request (compression attempted) ---");
    modelCalls.length = 0;
    requestBodies.length = 0;

    // Create a large message with whitespace that can be compressed
    const largeContent = JSON.stringify(
      {
        key1: "value".repeat(200),
        key2: "value".repeat(200),
        key3: "value".repeat(200),
      },
      null,
      2,
    ).repeat(100);

    const originalBody = JSON.stringify({
      model: "auto",
      messages: [{ role: "user", content: largeContent }],
      max_tokens: 50,
    });
    const originalSize = Buffer.byteLength(originalBody);

    const res = await fetch(`${proxy.baseUrl}/v1/chat/completions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: originalBody,
    });

    // With conservative compression (whitespace + deduplication + jsonCompact),
    // all requests should pass regardless of size
    assert(res.ok, `Request passes with compression: ${res.status}`);
  }

  // Test 3: Tool call preservation
  {
    console.log("\n--- Test 3: Tool call preservation ---");
    modelCalls.length = 0;
    requestBodies.length = 0;

    const toolCallMessage = {
      role: "assistant" as const,
      content: null,
      tool_calls: [
        {
          id: "call_123",
          type: "function" as const,
          function: {
            name: "get_weather",
            arguments: JSON.stringify({ location: "San Francisco" }),
          },
        },
      ],
    };

    const toolResultMessage = {
      role: "tool" as const,
      tool_call_id: "call_123",
      content: "The weather is sunny, 72°F",
    };

    // Large content to trigger compression
    const largeContent = "x".repeat(60 * 1024);

    const res = await fetch(`${proxy.baseUrl}/v1/chat/completions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "auto",
        messages: [{ role: "user", content: largeContent }, toolCallMessage, toolResultMessage],
        max_tokens: 50,
      }),
    });

    assert(res.ok, `Request with tool calls succeeds: ${res.status}`);

    // Parse what server received and verify tool structures
    const serverReceived = JSON.parse(requestBodies[0]) as {
      messages?: Array<{
        role: string;
        tool_calls?: unknown[];
        tool_call_id?: string;
        content?: string | null;
      }>;
    };

    assert(
      serverReceived.messages?.[1]?.tool_calls?.[0] !== undefined,
      "Tool call structure preserved",
    );
    assert(serverReceived.messages?.[2]?.tool_call_id === "call_123", "Tool call ID preserved");

    // Verify tool_calls function name and arguments are intact
    const receivedToolCall = serverReceived.messages?.[1]?.tool_calls?.[0] as {
      id: string;
      type: string;
      function: { name: string; arguments: string };
    };
    assert(receivedToolCall?.function?.name === "get_weather", "Tool function name preserved");
    assert(
      receivedToolCall?.function?.arguments.includes("San Francisco"),
      "Tool function arguments preserved",
    );
  }

  // Test 4: Very large request still succeeds
  {
    console.log("\n--- Test 4: Very large request succeeds ---");
    modelCalls.length = 0;
    paymentAttempts.length = 0;

    // Create a large request (300KB)
    const hugeContent = "x".repeat(300 * 1024);

    const res = await fetch(`${proxy.baseUrl}/v1/chat/completions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "auto",
        messages: [{ role: "user", content: hugeContent }],
        max_tokens: 50,
      }),
    });

    assert(res.ok, `Large request succeeds: ${res.status}`);
    assert(modelCalls.length > 0, "At least one model called");
  }

  // Cleanup
  await proxy.close();
  await mockApi.close();
  console.log("\nServers closed.");

  // Summary
  console.log("\n═══════════════════════════════════");
  console.log(`  ${passed} passed, ${failed} failed`);
  console.log("═══════════════════════════════════\n");

  process.exit(failed > 0 ? 1 : 0);
}

runTests().catch((err) => {
  console.error("Test failed:", err);
  process.exit(1);
});
