package handler

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"mcp-bridge/src/logger"

	"github.com/creachadair/jrpc2"
)

// MockRPCClient is a mock implementation of the RPCClient interface for testing
type MockRPCClient struct {
	CallResultFunc func(ctx context.Context, method string, params interface{}, result interface{}) error
	CloseFunc      func() error
}

func (m *MockRPCClient) CallResult(ctx context.Context, method string, params interface{}, result interface{}) error {
	if m.CallResultFunc != nil {
		return m.CallResultFunc(ctx, method, params, result)
	}
	return nil
}

func (m *MockRPCClient) Close() error {
	if m.CloseFunc != nil {
		return m.CloseFunc()
	}
	return nil
}

func init() {
	// Initialize logger with a test level
	logger.Initialize("error") // Use error level to minimize test output
}

func TestServiceHandler_ServeHTTP_Success(t *testing.T) {
	// Create a mock client that returns a successful response
	mockClient := &MockRPCClient{
		CallResultFunc: func(ctx context.Context, method string, params interface{}, result interface{}) error {
			// Cast result to json.RawMessage and set a test response
			if r, ok := result.(*json.RawMessage); ok {
				*r = json.RawMessage(`{"result":"success"}`)
			}
			return nil
		},
	}

	// Create a service handler
	handler := NewServiceHandler(mockClient, "test-service", "/test", "test.method", "test-tool")

	// Create a test request
	reqBody := bytes.NewBufferString(`{"param":"value"}`)
	req := httptest.NewRequest(http.MethodPost, "/test", reqBody)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Request-ID", "test-request-id")

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
	}

	// Check the content type
	contentType := rr.Header().Get("Content-Type")
	if contentType != "application/json" {
		t.Errorf("handler returned wrong content type: got %v want %v", contentType, "application/json")
	}

	// Check the response body
	expected := `{"result":"success"}`
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
	}
}

func TestServiceHandler_ServeHTTP_InvalidJSON(t *testing.T) {
	// Create a mock client
	mockClient := &MockRPCClient{}

	// Create a service handler
	handler := NewServiceHandler(mockClient, "test-service", "/test", "test.method", "test-tool")

	// Create a test request with invalid JSON
	reqBody := bytes.NewBufferString(`{"param":invalid}`)
	req := httptest.NewRequest(http.MethodPost, "/test", reqBody)
	req.Header.Set("Content-Type", "application/json")

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusBadRequest {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest)
	}
}

func TestServiceHandler_ServeHTTP_RPCError(t *testing.T) {
	// Create a mock client that returns an RPC error
	mockClient := &MockRPCClient{
		CallResultFunc: func(ctx context.Context, method string, params interface{}, result interface{}) error {
			return &jrpc2.Error{
				Code:    -32602,
				Message: "Invalid params",
			}
		},
	}

	// Create a service handler
	handler := NewServiceHandler(mockClient, "test-service", "/test", "test.method", "test-tool")

	// Create a test request
	reqBody := bytes.NewBufferString(`{"param":"value"}`)
	req := httptest.NewRequest(http.MethodPost, "/test", reqBody)
	req.Header.Set("Content-Type", "application/json")

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusBadRequest {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest)
	}

	// Check that the response contains the error
	var respError jrpc2.Error
	if err := json.Unmarshal(rr.Body.Bytes(), &respError); err != nil {
		t.Errorf("failed to unmarshal response: %v", err)
	}

	if respError.Code != -32602 || respError.Message != "Invalid params" {
		t.Errorf("handler returned unexpected error: got %v want code=%v message=%v",
			respError, -32602, "Invalid params")
	}
}

func TestServiceHandler_ServeHTTP_GenericError(t *testing.T) {
	// Create a mock client that returns a generic error
	mockClient := &MockRPCClient{
		CallResultFunc: func(ctx context.Context, method string, params interface{}, result interface{}) error {
			return errors.New("generic error")
		},
	}

	// Create a service handler
	handler := NewServiceHandler(mockClient, "test-service", "/test", "test.method", "test-tool")

	// Create a test request
	reqBody := bytes.NewBufferString(`{"param":"value"}`)
	req := httptest.NewRequest(http.MethodPost, "/test", reqBody)
	req.Header.Set("Content-Type", "application/json")

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusInternalServerError {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
	}

	// Check that the response contains the error message
	expected := "RPC call failed: generic error\n"
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
	}
}

func TestServiceHandler_ServeHTTP_NilClient(t *testing.T) {
	// Create a service handler with a nil client
	handler := NewServiceHandler(nil, "test-service", "/test", "test.method", "test-tool")

	// Create a test request
	reqBody := bytes.NewBufferString(`{"param":"value"}`)
	req := httptest.NewRequest(http.MethodPost, "/test", reqBody)
	req.Header.Set("Content-Type", "application/json")

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusInternalServerError {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
	}

	// Check that the response contains the error message
	expected := "Service test-service not found\n"
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
	}
}

func TestReloadHandler_ServeHTTP_Success(t *testing.T) {
	// Create a reload handler with a successful reload function
	reloadCalled := false
	handler := NewReloadHandler(func() error {
		reloadCalled = true
		return nil
	})

	// Create a test request
	req := httptest.NewRequest(http.MethodPost, "/reload", nil)

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check that the reload function was called
	if !reloadCalled {
		t.Error("reload function was not called")
	}

	// Check the status code
	if status := rr.Code; status != http.StatusAccepted {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusAccepted)
	}

	// Check the response body
	expected := `{"status":"reload initiated"}`
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
	}
}

func TestReloadHandler_ServeHTTP_Error(t *testing.T) {
	// Create a reload handler with an error-returning reload function
	handler := NewReloadHandler(func() error {
		return errors.New("reload error")
	})

	// Create a test request
	req := httptest.NewRequest(http.MethodPost, "/reload", nil)

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusInternalServerError {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
	}

	// Check that the response contains the error message
	expected := "Failed to reload: reload error\n"
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
	}
}

func TestReloadHandler_ServeHTTP_MethodNotAllowed(t *testing.T) {
	// Create a reload handler
	handler := NewReloadHandler(func() error {
		return nil
	})

	// Create a test request with an unsupported method
	req := httptest.NewRequest(http.MethodGet, "/reload", nil)

	// Create a response recorder
	rr := httptest.NewRecorder()

	// Call the handler
	handler.ServeHTTP(rr, req)

	// Check the status code
	if status := rr.Code; status != http.StatusMethodNotAllowed {
		t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusMethodNotAllowed)
	}

	// Check the response body
	expected := "Method not allowed\n"
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
	}
}
