package agent

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/drivercontract"
	"github.com/tidbcloud/AgentTeam/desktop/internal/consts"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

func TestOpenCodePermissionAskedTransport(t *testing.T) {
	var got struct {
		Path      string
		Directory string
		Body      map[string]string
	}
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		got.Path = r.URL.Path
		got.Directory = r.Header.Get("x-opencode-directory")
		require.Equal(t, http.MethodPost, r.Method)
		require.NoError(t, json.NewDecoder(r.Body).Decode(&got.Body))
		_, _ = w.Write([]byte(`true`))
	}))
	defer server.Close()

	workDir := t.TempDir()
	driver := &OpenCodeDriver{
		serverURL:       server.URL,
		password:        "pw",
		httpClient:      server.Client(),
		workDir:         workDir,
		sessionID:       "ses_current",
		permissionReply: openCodePermissionReplyOnce,
	}
	driver.handleSSEEvent(openCodePermissionAskedEvent(t, openCodePermissionRequest{
		ID: "per_1", SessionID: "ses_current", Permission: "doom_agentteam",
	}, nil))

	require.Equal(t, "/permission/per_1/reply", got.Path)
	require.Equal(t, workDir, got.Directory)
	require.Equal(t, map[string]string{"reply": openCodePermissionReplyOnce}, got.Body)
}

func TestOpenCodePermissionAskedDiagnosticsOmitEventPayload(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte(`true`))
	}))
	defer server.Close()

	driver := &OpenCodeDriver{
		serverURL:           server.URL,
		password:            "pw",
		httpClient:          server.Client(),
		workDir:             t.TempDir(),
		sessionID:           "ses_current",
		permissionReply:     openCodePermissionReplyOnce,
		runtimeDebugEnabled: true,
	}
	debugLogPath := attachOpenCodeRuntimeDebugLogForTest(t, driver, driver.workDir)
	driver.handleSSEEvent(openCodePermissionAskedEvent(t, openCodePermissionRequest{
		ID: "per_diag", SessionID: "ses_current", Permission: "doom_agentteam",
	}, map[string]any{
		"patterns": []string{"bash --token raw-pattern-secret"},
		"metadata": map[string]string{"command": "raw-metadata-secret"},
		"tool":     map[string]string{"messageID": "msg_diag", "callID": "call_diag"},
	}))

	data, err := json.Marshal(readOpenCodeRuntimeDebugEntries(t, debugLogPath))
	require.NoError(t, err)
	require.Contains(t, string(data), "per_diag")
	require.Contains(t, string(data), "call_diag")
	require.NotContains(t, string(data), "raw-pattern-secret")
	require.NotContains(t, string(data), "raw-metadata-secret")
}

func TestOpenCodePermissionReplyFailureStopsRuntime(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.NotFound(w, r)
	}))
	defer server.Close()

	cancelled := false
	driver := &OpenCodeDriver{
		serverURL:       server.URL,
		password:        "pw",
		httpClient:      server.Client(),
		workDir:         t.TempDir(),
		sessionID:       "ses_current",
		permissionReply: openCodePermissionReplyOnce,
		translatedLines: make(chan string, 1),
		turnActive:      true,
		sessionStatus:   "busy",
		serverCancel:    func() { cancelled = true },
	}
	driver.handleSSEEvent(openCodePermissionAskedEvent(t, openCodePermissionRequest{
		ID: "per_failure", SessionID: "ses_current", Permission: "doom_agentteam",
	}, nil))

	require.True(t, cancelled)
	require.True(t, driver.turnActive)
	require.Equal(t, "busy", driver.sessionStatus)
	events := driver.ParseLine(<-driver.translatedLines)
	require.Len(t, events, 1)
	require.Equal(t, consts.ParsedEventKindError, events[0].Kind)
	require.Contains(t, events[0].Message, "runtime was stopped")
}

func TestOpenCodePermissionReplyForLaunch(t *testing.T) {
	tests := []struct {
		name  string
		cfg   drivercontract.LaunchConfig
		reply string
	}{
		{name: "full access", cfg: drivercontract.LaunchConfig{PermissionMode: types.AgentPermissionModeFullAccess}, reply: openCodePermissionReplyOnce},
		{name: "native tools disabled", cfg: drivercontract.LaunchConfig{DisableNativeTools: true}, reply: openCodePermissionReplyReject},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			reply, err := openCodePermissionReplyForLaunch(tt.cfg)
			require.NoError(t, err)
			require.Equal(t, tt.reply, reply)
		})
	}
}

func openCodePermissionAskedEvent(
	t *testing.T,
	request openCodePermissionRequest,
	extra map[string]any,
) string {
	t.Helper()
	properties := map[string]any{
		"id": request.ID, "sessionID": request.SessionID, "permission": request.Permission,
	}
	if request.Tool != nil {
		properties["tool"] = request.Tool
	}
	for key, value := range extra {
		properties[key] = value
	}
	data, err := json.Marshal(map[string]any{
		"type": "permission.asked", "properties": properties,
	})
	require.NoError(t, err)
	return strings.TrimSpace(string(data))
}
