package ucxi

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

	"chorus.services/bzzz/pkg/ucxl"
)

// Mock implementations for testing

type MockCollaborativeResolver struct {
	resolveResults map[string]*ResolvedContent
	announcements  []string
	discoveries    map[string][]*ResolvedContent
}

func NewMockCollaborativeResolver() *MockCollaborativeResolver {
	return &MockCollaborativeResolver{
		resolveResults: make(map[string]*ResolvedContent),
		announcements:  make([]string, 0),
		discoveries:    make(map[string][]*ResolvedContent),
	}
}

func (m *MockCollaborativeResolver) Resolve(ctx context.Context, addr *ucxl.Address) (*ResolvedContent, error) {
	key := addr.String()
	if result, exists := m.resolveResults[key]; exists {
		return result, nil
	}
	return nil, fmt.Errorf("not found: %s", key)
}

func (m *MockCollaborativeResolver) Announce(ctx context.Context, addr *ucxl.Address, content *Content) error {
	m.announcements = append(m.announcements, addr.String())
	return nil
}

func (m *MockCollaborativeResolver) Discover(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error) {
	key := pattern.String()
	if results, exists := m.discoveries[key]; exists {
		return results, nil
	}
	return []*ResolvedContent{}, nil
}

type MockCollaborativeStorage struct {
	contents map[string]*Content
}

func NewMockCollaborativeStorage() *MockCollaborativeStorage {
	return &MockCollaborativeStorage{
		contents: make(map[string]*Content),
	}
}

func (m *MockCollaborativeStorage) Store(ctx context.Context, key string, content *Content) error {
	m.contents[key] = content
	return nil
}

func (m *MockCollaborativeStorage) Retrieve(ctx context.Context, key string) (*Content, error) {
	if content, exists := m.contents[key]; exists {
		return content, nil
	}
	return nil, fmt.Errorf("not found: %s", key)
}

func (m *MockCollaborativeStorage) Delete(ctx context.Context, key string) error {
	delete(m.contents, key)
	return nil
}

func (m *MockCollaborativeStorage) List(ctx context.Context, prefix string) ([]string, error) {
	keys := make([]string, 0)
	for key := range m.contents {
		if strings.HasPrefix(key, prefix) {
			keys = append(keys, key)
		}
	}
	return keys, nil
}

type MockCollaborativeLogger struct{}

func (l MockCollaborativeLogger) Info(msg string, fields ...interface{})  {}
func (l MockCollaborativeLogger) Warn(msg string, fields ...interface{})  {}
func (l MockCollaborativeLogger) Error(msg string, fields ...interface{}) {}
func (l MockCollaborativeLogger) Debug(msg string, fields ...interface{}) {}

// Integration tests for role-based collaboration features

func TestCollaborationStatusEndpoint(t *testing.T) {
	// Setup server with mock dependencies
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	// Test GET /collaboration endpoint
	req := httptest.NewRequest(http.MethodGet, "/api/ucxi/v1/collaboration", nil)
	w := httptest.NewRecorder()

	server.handleCollaboration(w, req)

	// Verify response
	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response struct {
		Response struct {
			Code string `json:"code"`
			Data struct {
				System struct {
					Enabled bool `json:"enabled"`
				} `json:"system"`
				ActiveSessions []map[string]interface{} `json:"active_sessions"`
			} `json:"data"`
		} `json:"response"`
	}

	if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if response.Response.Code != "UCXL-200-SUCCESS" {
		t.Errorf("Expected code UCXL-200-SUCCESS, got %s", response.Response.Code)
	}

	if !response.Response.Data.System.Enabled {
		t.Error("Expected collaboration system to be enabled")
	}

	if len(response.Response.Data.ActiveSessions) == 0 {
		t.Error("Expected at least one active collaboration session")
	}
}

func TestCollaborationInitiation(t *testing.T) {
	// Setup server
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	// Test POST /collaboration endpoint
	requestBody := map[string]interface{}{
		"type":               "expertise_request",
		"from_role":          "junior_developer",
		"to_roles":           []string{"senior_developer", "tech_lead"},
		"required_expertise": []string{"api_design", "error_handling"},
		"project_id":         "bzzz",
		"priority":           "medium",
		"data": map[string]interface{}{
			"context":           "Working on UCXI API standardization",
			"specific_question": "How to handle nested error chains in UCXL responses?",
		},
	}

	reqBody, _ := json.Marshal(requestBody)
	req := httptest.NewRequest(http.MethodPost, "/api/ucxi/v1/collaboration", bytes.NewReader(reqBody))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()

	server.handleCollaboration(w, req)

	// Verify response
	if w.Code != http.StatusCreated {
		t.Errorf("Expected status 201, got %d", w.Code)
	}

	var response struct {
		Response struct {
			Code string `json:"code"`
			Data struct {
				CollaborationInitiated bool   `json:"collaboration_initiated"`
				ThreadID              string `json:"thread_id"`
				Type                  string `json:"type"`
				FromRole              string `json:"from_role"`
				Status                string `json:"status"`
				ExpectedResponseTime  string `json:"expected_response_time"`
				Routing               string `json:"routing"`
			} `json:"data"`
		} `json:"response"`
	}

	if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if response.Response.Code != "UCXL-201-CREATED" {
		t.Errorf("Expected code UCXL-201-CREATED, got %s", response.Response.Code)
	}

	if !response.Response.Data.CollaborationInitiated {
		t.Error("Expected collaboration to be initiated")
	}

	if response.Response.Data.Type != "expertise_request" {
		t.Errorf("Expected type expertise_request, got %s", response.Response.Data.Type)
	}

	if response.Response.Data.FromRole != "junior_developer" {
		t.Errorf("Expected from_role junior_developer, got %s", response.Response.Data.FromRole)
	}

	if response.Response.Data.Status != "initiated" {
		t.Errorf("Expected status initiated, got %s", response.Response.Data.Status)
	}

	if !strings.HasPrefix(response.Response.Data.ThreadID, "thread-expertise_request-") {
		t.Errorf("Expected thread ID to start with 'thread-expertise_request-', got %s", response.Response.Data.ThreadID)
	}

	if response.Response.Data.ExpectedResponseTime != "15m" {
		t.Errorf("Expected expected_response_time 15m, got %s", response.Response.Data.ExpectedResponseTime)
	}

	if response.Response.Data.Routing != "expertise_based" {
		t.Errorf("Expected routing expertise_based, got %s", response.Response.Data.Routing)
	}
}

func TestCollaborationValidationErrors(t *testing.T) {
	// Setup server
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	tests := []struct {
		name           string
		requestBody    map[string]interface{}
		expectedStatus int
		expectedCode   string
	}{
		{
			name:           "Missing type",
			requestBody:    map[string]interface{}{"from_role": "junior_developer"},
			expectedStatus: http.StatusBadRequest,
			expectedCode:   "UCXL-400-INVALID_PAYLOAD",
		},
		{
			name:           "Missing from_role",
			requestBody:    map[string]interface{}{"type": "expertise_request"},
			expectedStatus: http.StatusBadRequest,
			expectedCode:   "UCXL-400-INVALID_PAYLOAD",
		},
		{
			name:           "Invalid JSON",
			requestBody:    nil, // Will send invalid JSON
			expectedStatus: http.StatusBadRequest,
			expectedCode:   "UCXL-400-BAD_REQUEST",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var reqBody []byte
			var err error

			if tt.requestBody != nil {
				reqBody, err = json.Marshal(tt.requestBody)
				if err != nil {
					t.Fatalf("Failed to marshal request body: %v", err)
				}
			} else {
				reqBody = []byte("invalid json")
			}

			req := httptest.NewRequest(http.MethodPost, "/api/ucxi/v1/collaboration", bytes.NewReader(reqBody))
			req.Header.Set("Content-Type", "application/json")
			w := httptest.NewRecorder()

			server.handleCollaboration(w, req)

			if w.Code != tt.expectedStatus {
				t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code)
			}

			var response struct {
				Error struct {
					Code string `json:"code"`
				} `json:"error"`
			}

			if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
				t.Fatalf("Failed to decode error response: %v", err)
			}

			if response.Error.Code != tt.expectedCode {
				t.Errorf("Expected code %s, got %s", tt.expectedCode, response.Error.Code)
			}
		})
	}
}

func TestEnhancedStatusEndpoint(t *testing.T) {
	// Setup server
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	req := httptest.NewRequest(http.MethodGet, "/api/ucxi/v1/status", nil)
	w := httptest.NewRecorder()

	server.handleStatus(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response struct {
		Response struct {
			Code string `json:"code"`
			Data struct {
				Server       map[string]interface{} `json:"server"`
				Collaboration map[string]interface{} `json:"collaboration"`
				HmmmIntegration map[string]interface{} `json:"hmmm_integration"`
			} `json:"data"`
		} `json:"response"`
	}

	if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if response.Response.Code != "UCXL-200-SUCCESS" {
		t.Errorf("Expected code UCXL-200-SUCCESS, got %s", response.Response.Code)
	}

	// Verify server version is updated
	if version, ok := response.Response.Data.Server["version"].(string); ok {
		if version != "2.1.0" {
			t.Errorf("Expected server version 2.1.0, got %s", version)
		}
	} else {
		t.Error("Expected server version to be present")
	}

	// Verify collaboration status
	if enabled, ok := response.Response.Data.Collaboration["enabled"].(bool); ok {
		if !enabled {
			t.Error("Expected collaboration to be enabled")
		}
	} else {
		t.Error("Expected collaboration enabled status to be present")
	}

	// Verify HMMM integration status
	if enabled, ok := response.Response.Data.HmmmIntegration["enabled"].(bool); ok {
		if !enabled {
			t.Error("Expected HMMM integration to be enabled")
		}
	} else {
		t.Error("Expected HMMM integration enabled status to be present")
	}
}

func TestCollaborationFiltering(t *testing.T) {
	// Setup server
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	// Test with role filter
	req := httptest.NewRequest(http.MethodGet, "/api/ucxi/v1/collaboration?role=senior_developer", nil)
	w := httptest.NewRecorder()

	server.handleCollaboration(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response struct {
		Response struct {
			Code string `json:"code"`
			Data struct {
				FiltersApplied struct {
					Role string `json:"role"`
				} `json:"filters_applied"`
				FilteredResults map[string]interface{} `json:"filtered_results"`
			} `json:"data"`
		} `json:"response"`
	}

	if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if response.Response.Data.FiltersApplied.Role != "senior_developer" {
		t.Errorf("Expected role filter senior_developer, got %s", response.Response.Data.FiltersApplied.Role)
	}

	if response.Response.Data.FilteredResults == nil {
		t.Error("Expected filtered results to be present when filters are applied")
	}
}

func TestMethodNotAllowedHandling(t *testing.T) {
	// Setup server
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	// Test unsupported method
	req := httptest.NewRequest(http.MethodPut, "/api/ucxi/v1/collaboration", nil)
	w := httptest.NewRecorder()

	server.handleCollaboration(w, req)

	if w.Code != http.StatusMethodNotAllowed {
		t.Errorf("Expected status 405, got %d", w.Code)
	}

	var response struct {
		Error struct {
			Code    string `json:"code"`
			Details struct {
				AllowedMethods []string `json:"allowed_methods"`
			} `json:"details"`
		} `json:"error"`
	}

	if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if response.Error.Code != "UCXL-405-METHOD_NOT_ALLOWED" {
		t.Errorf("Expected code UCXL-405-METHOD_NOT_ALLOWED, got %s", response.Error.Code)
	}

	expectedMethods := []string{"GET", "POST"}
	if len(response.Error.Details.AllowedMethods) != len(expectedMethods) {
		t.Errorf("Expected %d allowed methods, got %d", len(expectedMethods), len(response.Error.Details.AllowedMethods))
	}
}

func TestRequestIDHandling(t *testing.T) {
	// Setup server
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	// Test with custom request ID
	customRequestID := "test-request-123"
	req := httptest.NewRequest(http.MethodGet, "/api/ucxi/v1/collaboration", nil)
	req.Header.Set("X-Request-ID", customRequestID)
	w := httptest.NewRecorder()

	server.handleCollaboration(w, req)

	var response struct {
		Response struct {
			RequestID string `json:"request_id"`
		} `json:"response"`
	}

	if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if response.Response.RequestID != customRequestID {
		t.Errorf("Expected request ID %s, got %s", customRequestID, response.Response.RequestID)
	}
}

// Benchmark tests

func BenchmarkCollaborationStatusEndpoint(b *testing.B) {
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest(http.MethodGet, "/api/ucxi/v1/collaboration", nil)
		w := httptest.NewRecorder()
		server.handleCollaboration(w, req)
	}
}

func BenchmarkCollaborationInitiation(b *testing.B) {
	resolver := NewMockCollaborativeResolver()
	storage := NewMockCollaborativeStorage()
	logger := MockCollaborativeLogger{}

	config := ServerConfig{
		Port:     8080,
		BasePath: "/api",
		Resolver: resolver,
		Storage:  storage,
		Logger:   logger,
	}

	server := NewServer(config)

	requestBody := map[string]interface{}{
		"type":      "expertise_request",
		"from_role": "junior_developer",
		"to_roles":  []string{"senior_developer"},
		"data":      map[string]interface{}{"context": "test"},
	}

	reqBodyBytes, _ := json.Marshal(requestBody)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest(http.MethodPost, "/api/ucxi/v1/collaboration", bytes.NewReader(reqBodyBytes))
		req.Header.Set("Content-Type", "application/json")
		w := httptest.NewRecorder()
		server.handleCollaboration(w, req)
	}
}