package task_test

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

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	. "github.com/tidbcloud/AgentTeam/internal/api/testkit"
)

type postedTaskScopeDiscussionMessage struct {
	Task    dto.Task
	Entry   dto.TaskDiscussionEntry
	Message dto.AppMessage
}

func postTaskScopeDiscussionMessage(t *testing.T, client *http.Client, baseURL string, accessToken string, workspaceID string, taskID string, body string, attachmentIDs []string) postedTaskScopeDiscussionMessage {
	t.Helper()

	var before dto.TaskDetailResponse
	response := DoJSON(t, client, http.MethodGet, baseURL+"/api/v1/workspaces/"+workspaceID+"/tasks/"+taskID, nil, WorkspaceHeaders(accessToken, workspaceID), &before)
	require.Equal(t, http.StatusOK, response.StatusCode)
	taskScopeID := before.Detail.Task.TaskScopeID
	require.NotEmpty(t, taskScopeID)

	var message dto.AppMessage
	payload := map[string]any{
		"scopeId": taskScopeID,
		"content": body,
	}
	if len(attachmentIDs) > 0 {
		payload["attachmentIds"] = attachmentIDs
	}
	response, responseBody := doTaskTestJSONRaw(t, client, http.MethodPost, baseURL+WorkspaceScopeMessagesPath(workspaceID, taskScopeID), payload, WorkspaceHeaders(accessToken, workspaceID))
	require.Equal(t, http.StatusOK, response.StatusCode, "response body: %s", string(responseBody))
	require.NoError(t, json.Unmarshal(responseBody, &message), "response body: %s", string(responseBody))
	require.Equal(t, taskScopeID, message.ChannelID)

	var detail dto.TaskDetailResponse
	response = DoJSON(t, client, http.MethodGet, baseURL+"/api/v1/workspaces/"+workspaceID+"/tasks/"+taskID, nil, WorkspaceHeaders(accessToken, workspaceID), &detail)
	require.Equal(t, http.StatusOK, response.StatusCode)
	for _, entry := range taskDetailDiscussionEntries(detail.Detail) {
		if entry.ID == message.ID {
			return postedTaskScopeDiscussionMessage{
				Task:    detail.Detail.Task,
				Entry:   entry,
				Message: message,
			}
		}
	}
	require.Failf(t, "task-scope discussion message not found in detail timeline", "messageID=%s taskID=%s", message.ID, taskID)
	return postedTaskScopeDiscussionMessage{}
}

func doTaskTestJSONRaw(t *testing.T, client *http.Client, method string, url string, payload any, headers map[string]string) (*http.Response, []byte) {
	t.Helper()
	encoded, err := json.Marshal(payload)
	require.NoError(t, err)
	request, err := http.NewRequestWithContext(context.Background(), method, url, bytes.NewReader(encoded))
	require.NoError(t, err)
	request.Header.Set("Content-Type", "application/json")
	for key, value := range headers {
		request.Header.Set(key, value)
	}
	response, err := client.Do(request)
	require.NoError(t, err)
	t.Cleanup(func() {
		_ = response.Body.Close()
	})
	body, err := io.ReadAll(response.Body)
	require.NoError(t, err)
	return response, body
}

func taskDetailDiscussionEntries(detail dto.TaskDetail) []dto.TaskDiscussionEntry {
	entries := make([]dto.TaskDiscussionEntry, 0, len(detail.Timeline))
	for _, entry := range detail.Timeline {
		if entry.Kind == "discussion" && entry.Discussion != nil {
			entries = append(entries, *entry.Discussion)
		}
	}
	return entries
}

func taskDetailActivityEvents(detail dto.TaskDetail) []dto.TaskActivityEvent {
	entries := make([]dto.TaskActivityEvent, 0, len(detail.Timeline))
	for _, entry := range detail.Timeline {
		if entry.Kind == "activity" && entry.Activity != nil {
			entries = append(entries, *entry.Activity)
		}
	}
	return entries
}

func taskTimelineDiscussionEntries(page dto.TaskTimelineListResponse) []dto.TaskDiscussionEntry {
	entries := make([]dto.TaskDiscussionEntry, 0, len(page.Entries))
	for _, entry := range page.Entries {
		if entry.Kind == "discussion" && entry.Discussion != nil {
			entries = append(entries, *entry.Discussion)
		}
	}
	return entries
}

func taskTimelineActivityEvents(page dto.TaskTimelineListResponse) []dto.TaskActivityEvent {
	entries := make([]dto.TaskActivityEvent, 0, len(page.Entries))
	for _, entry := range page.Entries {
		if entry.Kind == "activity" && entry.Activity != nil {
			entries = append(entries, *entry.Activity)
		}
	}
	return entries
}

func taskActivityTypes(events []dto.TaskActivityEvent) []string {
	types := make([]string, 0, len(events))
	for _, event := range events {
		types = append(types, event.Type)
	}
	return types
}

func taskActivityByType(t require.TestingT, events []dto.TaskActivityEvent, eventType string) dto.TaskActivityEvent {
	for _, event := range events {
		if event.Type == eventType {
			return event
		}
	}
	require.Failf(t, "task activity not found", "activity type %q not found in %#v", eventType, taskActivityTypes(events))
	return dto.TaskActivityEvent{}
}
