package handlers

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

	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// MockRoomService implements RoomServiceInterface for testing
type MockRoomService struct {
	CreateRoomFunc     func(ctx context.Context, userID uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error)
	GetUserRoomsFunc   func(ctx context.Context, userID uuid.UUID) ([]*services.RoomResponse, error)
	GetRoomFunc        func(ctx context.Context, roomID uuid.UUID) (*services.RoomResponse, error)
	UpdateRoomFunc     func(ctx context.Context, roomID uuid.UUID, userID uuid.UUID, req services.UpdateRoomRequest) (*services.RoomResponse, error)
	AddMemberFunc      func(ctx context.Context, roomID, userID uuid.UUID) error
	RemoveMemberFunc   func(ctx context.Context, roomID, userID uuid.UUID) error
	GetRoomHistoryFunc func(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]services.ChatMessageResponse, error)
	DeleteRoomFunc     func(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error
}

func (m *MockRoomService) CreateRoom(ctx context.Context, userID uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error) {
	if m.CreateRoomFunc != nil {
		return m.CreateRoomFunc(ctx, userID, req)
	}
	return nil, nil
}

func (m *MockRoomService) GetUserRooms(ctx context.Context, userID uuid.UUID) ([]*services.RoomResponse, error) {
	if m.GetUserRoomsFunc != nil {
		return m.GetUserRoomsFunc(ctx, userID)
	}
	return nil, nil
}

func (m *MockRoomService) GetRoom(ctx context.Context, roomID uuid.UUID) (*services.RoomResponse, error) {
	if m.GetRoomFunc != nil {
		return m.GetRoomFunc(ctx, roomID)
	}
	return nil, nil
}

func (m *MockRoomService) AddMember(ctx context.Context, roomID, userID uuid.UUID) error {
	if m.AddMemberFunc != nil {
		return m.AddMemberFunc(ctx, roomID, userID)
	}
	return nil
}

func (m *MockRoomService) GetRoomHistory(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]services.ChatMessageResponse, error) {
	if m.GetRoomHistoryFunc != nil {
		return m.GetRoomHistoryFunc(ctx, roomID, limit, offset)
	}
	return nil, nil
}

func (m *MockRoomService) UpdateRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID, req services.UpdateRoomRequest) (*services.RoomResponse, error) {
	if m.UpdateRoomFunc != nil {
		return m.UpdateRoomFunc(ctx, roomID, userID, req)
	}
	return nil, nil
}

func (m *MockRoomService) RemoveMember(ctx context.Context, roomID, userID uuid.UUID) error {
	if m.RemoveMemberFunc != nil {
		return m.RemoveMemberFunc(ctx, roomID, userID)
	}
	return nil
}

func (m *MockRoomService) DeleteRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
	if m.DeleteRoomFunc != nil {
		return m.DeleteRoomFunc(ctx, roomID, userID)
	}
	return nil
}

func TestRoomHandler_CreateRoom(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	logger := zap.NewNop()

	userID := uuid.New()

	tests := []struct {
		name           string
		setupMock      func() *MockRoomService
		requestBody    interface{}
		setupContext   func(*gin.Context)
		expectedStatus int
	}{
		{
			name: "Success",
			setupMock: func() *MockRoomService {
				return &MockRoomService{
					CreateRoomFunc: func(ctx context.Context, uid uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error) {
						return &services.RoomResponse{
							ID:   uuid.New(),
							Name: req.Name,
							Type: req.Type,
						}, nil
					},
				}
			},
			requestBody: services.CreateRoomRequest{
				Name: "General",
				Type: "public",
			},
			setupContext: func(c *gin.Context) {
				c.Set("user_id", userID)
			},
			expectedStatus: http.StatusCreated,
		},
		{
			name: "Unauthorized",
			setupMock: func() *MockRoomService {
				return &MockRoomService{}
			},
			requestBody: services.CreateRoomRequest{Name: "Test"},
			setupContext: func(c *gin.Context) {
				// No user_id set
			},
			expectedStatus: http.StatusUnauthorized,
		},
		{
			name: "Invalid Payload",
			setupMock: func() *MockRoomService {
				return &MockRoomService{}
			},
			requestBody: "invalid-json", // String instead of struct
			setupContext: func(c *gin.Context) {
				c.Set("user_id", userID)
			},
			expectedStatus: http.StatusBadRequest,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mockService := tt.setupMock()
			handler := NewRoomHandler(mockService, logger)

			w := httptest.NewRecorder()
			c, _ := gin.CreateTestContext(w)

			// Setup request
			c.Request, _ = http.NewRequest(http.MethodPost, "/conversations", nil)
			if body, ok := tt.requestBody.(string); ok && body == "invalid-json" {
				c.Request.Body = &closingBuffer{bytes.NewBufferString("invalid-json")}
			} else {
				jsonBytes, _ := json.Marshal(tt.requestBody)
				c.Request.Body = &closingBuffer{bytes.NewBuffer(jsonBytes)}
			}
			c.Request.Header.Set("Content-Type", "application/json")

			// Setup context (auth)
			tt.setupContext(c)

			// Execute
			handler.CreateRoom(c)

			// Assert
			if w.Code != tt.expectedStatus {
				t.Errorf("Expected status %d, got %d. Body: %s", tt.expectedStatus, w.Code, w.Body.String())
			}
		})
	}
}

// closingBuffer helps to mock ReadCloser
type closingBuffer struct {
	*bytes.Buffer
}

func (cb *closingBuffer) Close() error {
	return nil
}
