package dashboard

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
)

func TestCreateMCPServerHandlerPersists0600(t *testing.T) {
	dir := t.TempDir()
	t.Setenv(profile.ConfigDirEnvKey, dir)
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.POST("/mcp-servers", NewCreateMCPServerHandler())

	body := `{"id":"zap_http","displayName":"Zap HTTP","transport":"http","http":{"url":"https://mcp.example.com","headers":["Authorization: Bearer secret"]}}`
	req := httptest.NewRequest(http.MethodPost, "/mcp-servers", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

	require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
	var resp struct {
		Enabled bool `json:"enabled"`
		HTTP    struct {
			Headers []string `json:"headers"`
		} `json:"http"`
	}
	require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
	require.True(t, resp.Enabled)
	require.Empty(t, resp.HTTP.Headers)

	info, err := os.Stat(filepath.Join(dir, "mcp-servers.yaml"))
	require.NoError(t, err)
	require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
}

func TestUpdateMCPServerHandlerPresistsHeaders(t *testing.T) {
	dir := t.TempDir()
	t.Setenv(profile.ConfigDirEnvKey, dir)
	_, err := profile.Default().MCPServers().Add(profile.MCPServer{
		ID:          "zap_http",
		DisplayName: "Zap HTTP",
		Enabled:     true,
		Transport:   profile.MCPServerTransportHTTP,
		HTTP:        &profile.MCPHTTPConfig{URL: "https://mcp.example.com", Headers: []string{"Authorization: Bearer old"}},
	})
	require.NoError(t, err)

	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.PUT("/mcp-servers/:id", NewUpdateMCPServerHandler())

	body := `{"displayName":"Zap HTTP","transport":"http","http":{"url":"https://mcp.example.com","headers":["Authorization: Bearer new"]}}`
	req := httptest.NewRequest(http.MethodPut, "/mcp-servers/zap_http", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	data, err := os.ReadFile(filepath.Join(dir, "mcp-servers.yaml"))
	require.NoError(t, err)
	require.Contains(t, string(data), "Bearer new")
}

func TestUpdateMCPServerHandlerPreservesOmittedHeaders(t *testing.T) {
	dir := t.TempDir()
	t.Setenv(profile.ConfigDirEnvKey, dir)
	_, err := profile.Default().MCPServers().Add(profile.MCPServer{
		ID:          "zap_http_preserve",
		DisplayName: "Zap HTTP",
		Enabled:     true,
		Transport:   profile.MCPServerTransportHTTP,
		HTTP:        &profile.MCPHTTPConfig{URL: "https://mcp.example.com", Headers: []string{"Authorization: Bearer old"}},
	})
	require.NoError(t, err)

	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.PUT("/mcp-servers/:id", NewUpdateMCPServerHandler())

	body := `{"displayName":"Zap HTTP","enabled":false,"transport":"http","http":{"url":"https://mcp.example.com"}}`
	req := httptest.NewRequest(http.MethodPut, "/mcp-servers/zap_http_preserve", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	data, err := os.ReadFile(filepath.Join(dir, "mcp-servers.yaml"))
	require.NoError(t, err)
	require.Contains(t, string(data), "Bearer old")
}

func TestTestUnsavedMCPServerHandlerResolvesHeaders(t *testing.T) {
	dir := t.TempDir()
	t.Setenv(profile.ConfigDirEnvKey, dir)
	fixture := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, "Bearer resolved", r.Header.Get("Authorization"))
		var req struct {
			Method string `json:"method"`
		}
		require.NoError(t, json.NewDecoder(r.Body).Decode(&req))
		switch req.Method {
		case "initialize":
			_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"fixture","version":"1"}}}`))
		case "notifications/initialized":
			w.WriteHeader(http.StatusAccepted)
		case "tools/list":
			_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":2,"result":{"tools":[]}}`))
		default:
			w.WriteHeader(http.StatusBadRequest)
		}
	}))
	defer fixture.Close()

	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.POST("/mcp-servers/test", NewTestUnsavedMCPServerHandler())

	body := `{"id":"zap_http","displayName":"Zap HTTP","transport":"http","http":{"url":"` + fixture.URL + `","headers":["Authorization: Bearer resolved"]}}`
	req := httptest.NewRequest(http.MethodPost, "/mcp-servers/test", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
}

// TestUpsertMCPServerPreservesEnabledOnUpdate pins the B2 fix for the RPC
// upsert path: when updating an existing MCP server with `enabled` omitted,
// the existing Enabled value MUST be preserved (not silently flipped to
// false by the plain-bool zero value). Also pins the create default (true)
// and an explicit update to false.
func TestUpsertMCPServerPreservesEnabledOnUpdate(t *testing.T) {
	dir := t.TempDir()
	t.Setenv(profile.ConfigDirEnvKey, dir)

	_, err := profile.Default().MCPServers().Add(profile.MCPServer{
		ID:          "srv_keep",
		DisplayName: "Keeper",
		Enabled:     true,
		Transport:   profile.MCPServerTransportHTTP,
		HTTP:        &profile.MCPHTTPConfig{URL: "https://mcp.example.com"},
	})
	require.NoError(t, err)

	// Update with enabled omitted: existing true MUST be preserved.
	resp, err := UpsertMCPServer(dto.MCPServerRequest{
		ID:          "srv_keep",
		DisplayName: "Keeper",
		Transport:   "http",
		HTTP:        &dto.MCPServerHTTPConfig{URL: "https://mcp.example.com"},
	})
	require.NoError(t, err)
	require.True(t, resp.Enabled, "omitted enabled on update MUST preserve the existing true value")

	// Update with enabled explicitly false: MUST flip to false.
	enabledFalse := false
	resp, err = UpsertMCPServer(dto.MCPServerRequest{
		ID:          "srv_keep",
		DisplayName: "Keeper",
		Enabled:     &enabledFalse,
		Transport:   "http",
		HTTP:        &dto.MCPServerHTTPConfig{URL: "https://mcp.example.com"},
	})
	require.NoError(t, err)
	require.False(t, resp.Enabled, "explicit enabled=false on update MUST flip to false")

	// Create with enabled omitted: MUST default to true.
	resp, err = UpsertMCPServer(dto.MCPServerRequest{
		ID:          "srv_new",
		DisplayName: "New",
		Transport:   "http",
		HTTP:        &dto.MCPServerHTTPConfig{URL: "https://mcp.example.com"},
	})
	require.NoError(t, err)
	require.True(t, resp.Enabled, "omitted enabled on create MUST default to true")
}
