package test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"
)

const (
	testPort     = 8099
	testEndpoint = "/test/echo"
)

// setupTestEnvironment creates test configuration files and starts the bridge
func setupTestEnvironment(t *testing.T) (*exec.Cmd, func()) {
	// Create a temporary directory for test configurations
	tmpDir, err := ioutil.TempDir("", "mcp-bridge-test")
	if err != nil {
		t.Fatalf("Failed to create temp dir: %v", err)
	}

	// Create the main config file
	mainConfig := fmt.Sprintf(`
port: %d
services:
  test-adapter:
    config: %s/service.yaml
    command:
      - %s
`, testPort, tmpDir, "../bin/adapter")

	if err := ioutil.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainConfig), 0644); err != nil {
		os.RemoveAll(tmpDir)
		t.Fatalf("Failed to write main config: %v", err)
	}

	// Create the service config file
	serviceConfig := `
serviceName: test-adapter
endpoints:
  - path: /test/echo
    mcp_method: echo
    tool_name: echo
  - path: /test/error
    mcp_method: error
    tool_name: error
  - path: /test/sum
    mcp_method: sum
    tool_name: sum
  - path: /test/tools/call
    mcp_method: tools/call
    tool_name: tools/call
`

	if err := ioutil.WriteFile(filepath.Join(tmpDir, "service.yaml"), []byte(serviceConfig), 0644); err != nil {
		os.RemoveAll(tmpDir)
		t.Fatalf("Failed to write service config: %v", err)
	}

	// Start the bridge
	bridgePath := "../mcp-bridge" // Use the binary in the project root
	cmd := exec.Command(bridgePath, "-port", fmt.Sprintf("%d", testPort), "-config", filepath.Join(tmpDir, "config.yaml"))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Start(); err != nil {
		os.RemoveAll(tmpDir)
		t.Fatalf("Failed to start bridge: %v", err)
	}

	// Wait for the server to start
	time.Sleep(2 * time.Second)

	// Return a cleanup function
	cleanup := func() {
		cmd.Process.Signal(os.Interrupt)
		cmd.Wait()
		os.RemoveAll(tmpDir)
	}

	return cmd, cleanup
}

func TestEchoEndpoint(t *testing.T) {
	_, cleanup := setupTestEnvironment(t)
	defer cleanup()

	// Send a request to the echo endpoint
	payload := map[string]string{"message": "hello world"}
	jsonPayload, _ := json.Marshal(payload)

	resp, err := http.Post(fmt.Sprintf("http://localhost:%d%s", testPort, testEndpoint),
		"application/json", bytes.NewBuffer(jsonPayload))
	if err != nil {
		t.Fatalf("Failed to send request: %v", err)
	}
	defer resp.Body.Close()

	// Check the status code
	if resp.StatusCode != http.StatusOK {
		t.Errorf("Expected status OK, got %v", resp.Status)
	}

	// Read and parse the response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Failed to read response body: %v", err)
	}

	var response struct {
		Message string `json:"message"`
		Status  string `json:"status"`
	}

	if err := json.Unmarshal(body, &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	// Verify the response
	if response.Message != "hello world" {
		t.Errorf("Expected message 'hello world', got '%s'", response.Message)
	}
	if response.Status != "success" {
		t.Errorf("Expected status 'success', got '%s'", response.Status)
	}
}

func TestErrorEndpoint(t *testing.T) {
	_, cleanup := setupTestEnvironment(t)
	defer cleanup()

	// Send a request to the error endpoint
	payload := map[string]interface{}{
		"code":    -32000,
		"message": "test error",
	}
	jsonPayload, _ := json.Marshal(payload)

	resp, err := http.Post(fmt.Sprintf("http://localhost:%d/test/error", testPort),
		"application/json", bytes.NewBuffer(jsonPayload))
	if err != nil {
		t.Fatalf("Failed to send request: %v", err)
	}
	defer resp.Body.Close()

	// Check the status code - should be BadRequest for JSON-RPC errors
	if resp.StatusCode != http.StatusBadRequest {
		t.Errorf("Expected status BadRequest, got %v", resp.Status)
	}

	// Read and parse the response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Failed to read response body: %v", err)
	}

	var response struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
	}

	if err := json.Unmarshal(body, &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	// Verify the response
	if response.Code != -32000 {
		t.Errorf("Expected code -32000, got %d", response.Code)
	}
	if response.Message != "test error" {
		t.Errorf("Expected message 'test error', got '%s'", response.Message)
	}
}

func TestSumEndpoint(t *testing.T) {
	_, cleanup := setupTestEnvironment(t)
	defer cleanup()

	// Send a request to the sum endpoint
	numbers := []float64{1.5, 2.5, 3.5, 4.5, 5.0}
	jsonPayload, _ := json.Marshal(numbers)

	resp, err := http.Post(fmt.Sprintf("http://localhost:%d/test/sum", testPort),
		"application/json", bytes.NewBuffer(jsonPayload))
	if err != nil {
		t.Fatalf("Failed to send request: %v", err)
	}
	defer resp.Body.Close()

	// Check the status code
	if resp.StatusCode != http.StatusOK {
		t.Errorf("Expected status OK, got %v", resp.Status)
	}

	// Read and parse the response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Failed to read response body: %v", err)
	}

	var result float64
	if err := json.Unmarshal(body, &result); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	// Verify the response
	expected := 17.0 // 1.5 + 2.5 + 3.5 + 4.5 + 5.0
	if result != expected {
		t.Errorf("Expected sum %f, got %f", expected, result)
	}
}

func TestToolsCallEndpoint(t *testing.T) {
	_, cleanup := setupTestEnvironment(t)
	defer cleanup()

	// Send a request to the tools/call endpoint
	payload := map[string]interface{}{
		"name": "test-tool",
		"parameters": map[string]string{
			"param1": "value1",
			"param2": "value2",
		},
	}
	jsonPayload, _ := json.Marshal(payload)

	resp, err := http.Post(fmt.Sprintf("http://localhost:%d/test/tools/call", testPort),
		"application/json", bytes.NewBuffer(jsonPayload))
	if err != nil {
		t.Fatalf("Failed to send request: %v", err)
	}
	defer resp.Body.Close()

	// Check the status code
	if resp.StatusCode != http.StatusOK {
		t.Errorf("Expected status OK, got %v", resp.Status)
	}

	// Read and parse the response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Failed to read response body: %v", err)
	}

	var response struct {
		Result map[string]string `json:"result"`
	}

	if err := json.Unmarshal(body, &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	// Verify the response
	if response.Result["param1"] != "value1" || response.Result["param2"] != "value2" {
		t.Errorf("Response does not match expected parameters: %v", response.Result)
	}
}
