package aifin

import (
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"
)

type windRPCRequest struct {
	ID     json.RawMessage `json:"id"`
	Method string          `json:"method"`
	Params json.RawMessage `json:"params"`
}

type windMCPWireDouble struct {
	t            *testing.T
	credential   string
	path         string
	sseOperation bool
	result       func(windRPCRequest) any

	mu      sync.Mutex
	methods []string
}

func (server *windMCPWireDouble) RoundTrip(
	request *http.Request,
) (*http.Response, error) {
	server.t.Helper()
	require.Equal(server.t, "https", request.URL.Scheme)
	require.Equal(server.t, "mcp.wind.com.cn", request.URL.Host)
	require.Equal(server.t, server.path, request.URL.Path)
	require.Empty(server.t, request.URL.RawQuery)
	require.Equal(
		server.t,
		"Bearer "+server.credential,
		request.Header.Get("Authorization"),
	)

	recorder := httptest.NewRecorder()
	if request.Method == http.MethodDelete {
		require.NotEmpty(server.t, request.Header.Get("Mcp-Session-Id"))
		server.record("DELETE")
		recorder.WriteHeader(http.StatusNoContent)
		return recorder.Result(), nil
	}
	require.Equal(server.t, http.MethodPost, request.Method)
	body, err := io.ReadAll(request.Body)
	require.NoError(server.t, err)
	var rpc windRPCRequest
	require.NoError(server.t, json.Unmarshal(body, &rpc))
	server.record(rpc.Method)

	switch rpc.Method {
	case "server/discover":
		writeWindRPCError(
			server.t,
			recorder,
			rpc.ID,
			-32601,
			"legacy only",
		)
	case "initialize":
		recorder.Header().Set(
			"Mcp-Session-Id",
			"wind-aifinmarket-test-session",
		)
		writeWindRPCResult(server.t, recorder, rpc.ID, map[string]any{
			"protocolVersion": "2025-06-18",
			"capabilities":    map[string]any{},
			"serverInfo": map[string]any{
				"name":    "wind-aifinmarket-double",
				"version": "1.0.0",
			},
		}, false)
	case "notifications/initialized":
		require.Equal(
			server.t,
			"wind-aifinmarket-test-session",
			request.Header.Get("Mcp-Session-Id"),
		)
		recorder.WriteHeader(http.StatusAccepted)
	case "tools/call":
		require.Equal(
			server.t,
			"wind-aifinmarket-test-session",
			request.Header.Get("Mcp-Session-Id"),
		)
		require.NotNil(server.t, server.result)
		writeWindRPCResult(
			server.t,
			recorder,
			rpc.ID,
			server.result(rpc),
			server.sseOperation,
		)
	default:
		require.Failf(
			server.t,
			"unexpected MCP method",
			"method=%q",
			rpc.Method,
		)
		recorder.WriteHeader(http.StatusBadRequest)
	}
	return recorder.Result(), nil
}

func (server *windMCPWireDouble) record(method string) {
	server.mu.Lock()
	defer server.mu.Unlock()
	server.methods = append(server.methods, method)
}

func (server *windMCPWireDouble) recordedMethods() []string {
	server.mu.Lock()
	defer server.mu.Unlock()
	return append([]string(nil), server.methods...)
}

func writeWindRPCResult(
	t *testing.T,
	recorder *httptest.ResponseRecorder,
	id json.RawMessage,
	result any,
	sse bool,
) {
	t.Helper()
	payload, err := json.Marshal(map[string]any{
		"jsonrpc": "2.0",
		"id":      json.RawMessage(id),
		"result":  result,
	})
	require.NoError(t, err)
	if sse {
		recorder.Header().Set("Content-Type", "text/event-stream")
		_, err = recorder.Write([]byte("event: message\ndata: "))
		require.NoError(t, err)
		_, err = recorder.Write(payload)
		require.NoError(t, err)
		_, err = recorder.Write([]byte("\n\n"))
		require.NoError(t, err)
		return
	}
	recorder.Header().Set("Content-Type", "application/json")
	_, err = recorder.Write(append(payload, '\n'))
	require.NoError(t, err)
}

func writeWindRPCError(
	t *testing.T,
	recorder *httptest.ResponseRecorder,
	id json.RawMessage,
	code int64,
	message string,
) {
	t.Helper()
	recorder.Header().Set("Content-Type", "application/json")
	require.NoError(t, json.NewEncoder(recorder).Encode(map[string]any{
		"jsonrpc": "2.0",
		"id":      json.RawMessage(id),
		"error": map[string]any{
			"code":    code,
			"message": message,
		},
	}))
}
