package agent

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

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

	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

type fakeAgentAuthenticator struct {
	agent agent.AuthenticatedAgent
}

func (auth fakeAgentAuthenticator) AuthenticateAgent(_ context.Context, agentID string, bearerToken string) (agent.AuthenticatedAgent, error) {
	if agentID != auth.agent.AgentID || bearerToken != "test-token" {
		return agent.AuthenticatedAgent{}, storeerr.ErrUnauthorized
	}
	return auth.agent, nil
}

type fakeReactionStore struct {
	resolvedMessageID string
	addedMessageID    string
	addedEmoji        string
}

func (fake *fakeReactionStore) ResolveReactionMessageIDForAgent(_ context.Context, _ agent.AuthenticatedAgent, messageID string) (string, error) {
	fake.resolvedMessageID = messageID
	return "msg_resolved", nil
}

func (fake *fakeReactionStore) GetMessageReactionsForAgent(_ context.Context, _ agent.AuthenticatedAgent, messageID string) ([]message.MessageReactionAggregate, error) {
	return []message.MessageReactionAggregate{{Emoji: "👍", Count: 1}}, nil
}

func (fake *fakeReactionStore) AddReactionForAgent(_ context.Context, _ agent.AuthenticatedAgent, messageID string, emoji string) (message.AddReactionResult, error) {
	fake.addedMessageID = messageID
	fake.addedEmoji = emoji
	return message.AddReactionResult{
		Changed:   true,
		ChannelID: "chn_1",
		Reaction: message.MessageReaction{
			ID:    "rxn_1",
			Emoji: emoji,
			AuthorProfile: message.AuthorProfile{
				ActorType:   message.AuthorActorTypeAgent,
				MemberID:    "000000000001",
				DisplayName: "Agent Reactor",
			},
		},
		Delivery: &message.ReactionDelivery{
			Recipient: message.AgentRecipient{AgentID: "agt_0000000000wake", MachineID: "mac_1"},
			Seq:       7,
			Message:   message.ReceiveMessage{MessageID: "msg_delivery", Content: "reaction"},
		},
	}, nil
}

func (fake *fakeReactionStore) RemoveReactionForAgent(context.Context, agent.AuthenticatedAgent, string, string) (message.RemoveReactionResult, error) {
	panic("not used")
}

type fakeReactionFanout struct {
	addWorkspaceID string
	addMessageID   string
	addResult      message.AddReactionResult
}

func (fanout *fakeReactionFanout) DeliverReactionAdded(_ context.Context, workspaceID string, messageID string, result message.AddReactionResult, _ []message.MessageReactionAggregate) {
	fanout.addWorkspaceID = workspaceID
	fanout.addMessageID = messageID
	fanout.addResult = result
}

func (fanout *fakeReactionFanout) DeliverReactionRemoved(context.Context, string, string, message.RemoveReactionResult, []message.MessageReactionAggregate) {
	panic("not used")
}

func TestReactionHandlerAddUsesNarrowInterfaces(t *testing.T) {
	gin.SetMode(gin.TestMode)

	agentIdentity := agent.AuthenticatedAgent{AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", MemberID: "0000000agent"}
	reactionStore := &fakeReactionStore{}
	fanout := &fakeReactionFanout{}
	handler := &ReactionHandler{
		auth:   fakeAgentAuthenticator{agent: agentIdentity},
		store:  reactionStore,
		fanout: fanout,
	}

	router := gin.New()
	router.POST("/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/reactions/add", handler.Add)

	body, err := json.Marshal(map[string]string{"message_id": "msg_input", "emoji": "👍"})
	require.NoError(t, err)
	request := httptest.NewRequest(http.MethodPost, "/api/v1/workspaces/wsp_test/agents/agt_0000000000test/runtime/reactions/add", bytes.NewReader(body))
	request.Header.Set("Authorization", "Bearer test-token")
	request.Header.Set("Content-Type", "application/json")
	response := httptest.NewRecorder()

	router.ServeHTTP(response, request)

	require.Equal(t, http.StatusOK, response.Code)
	require.Equal(t, "msg_input", reactionStore.resolvedMessageID)
	require.Equal(t, "msg_resolved", reactionStore.addedMessageID)
	require.Equal(t, "👍", reactionStore.addedEmoji)
	require.Equal(t, "wsp_test", fanout.addWorkspaceID)
	require.Equal(t, "msg_resolved", fanout.addMessageID)
	require.True(t, fanout.addResult.Changed)
	require.Equal(t, "chn_1", fanout.addResult.ChannelID)
	require.Equal(t, "agt_0000000000wake", fanout.addResult.Delivery.Recipient.AgentID)
	require.Equal(t, int64(7), fanout.addResult.Delivery.Seq)
	require.Equal(t, "👍", fanout.addResult.Reaction.Emoji)
	require.JSONEq(t, `{"changed":true,"reactions":[{"emoji":"👍","count":1}]}`, response.Body.String())
}

func TestAgentRouteRejectsRouteWorkspaceMismatch(t *testing.T) {
	gin.SetMode(gin.TestMode)

	agentIdentity := agent.AuthenticatedAgent{AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", MemberID: "0000000agent"}
	handler := &ReactionHandler{
		auth:   fakeAgentAuthenticator{agent: agentIdentity},
		store:  &fakeReactionStore{},
		fanout: &fakeReactionFanout{},
	}

	router := gin.New()
	router.POST("/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/reactions/add", handler.Add)

	body, err := json.Marshal(map[string]string{"message_id": "msg_input", "emoji": "👍"})
	require.NoError(t, err)
	request := httptest.NewRequest(http.MethodPost, "/api/v1/workspaces/wsp_other/agents/agt_0000000000test/runtime/reactions/add", bytes.NewReader(body))
	request.Header.Set("Authorization", "Bearer test-token")
	request.Header.Set("Content-Type", "application/json")
	response := httptest.NewRecorder()

	router.ServeHTTP(response, request)

	require.Equal(t, http.StatusMisdirectedRequest, response.Code)
	require.Contains(t, response.Body.String(), "workspace owner mismatch")
	require.Contains(t, response.Body.String(), "workspace_owner_mismatch")
}
