package services_test

import (
	"actatempus_backend/internal/application/repository"
	"actatempus_backend/internal/application/services"
	"actatempus_backend/internal/application/services/dto"
	"actatempus_backend/internal/domain/entities"
	"fmt"

	"github.com/gin-gonic/gin"

	"actatempus_backend/tests/mocks"
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	E "github.com/IBM/fp-go/either"
	F "github.com/IBM/fp-go/function"
	"github.com/stretchr/testify/assert"
)

func TestUserService(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	//mockAuthRepo := repository.NewInMemoryAuthRepositoryImpl("secret")
	mockAuthRepo := mocks.NewMockAuthRepository()
	userDS := mocks.NewMockUserDataSource()
	userRepo := repository.NewUserRepository(userDS)
	userService := services.NewUserService(mockAuthRepo, userRepo)

	router := gin.Default()
	router.RedirectTrailingSlash = false
	api := router.Group("/api/users")
	userService.RegisterRoutes(api)

	t.Run("CreateUser - Success", func(t *testing.T) {
		userCreateDTO := dto.UserCreateDTO{
			Name:     "John Doe",
			Email:    "john.doe@example.com",
			Password: "password123",
		}

		body, _ := json.Marshal(userCreateDTO)
		req := httptest.NewRequest(http.MethodPost, "/api/users/", bytes.NewBuffer(body))
		req.Header.Set("Content-Type", "application/json")
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusCreated, rec.Code)
		var response dto.UserDTO
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Equal(t, userCreateDTO.Name, response.Name)
		assert.Equal(t, userCreateDTO.Email, response.Email)
	})

	t.Run("GetUserByID - Success", func(t *testing.T) {
		// Prepopulate mock data
		user := entities.UserCreate{
			Name:     "John Doe",
			Email:    "john.doe@example.com",
			Password: "hashed_password",
		}

		newUser, _ := E.Unwrap(F.Pipe1(
			user,
			userRepo.Create(context.Background()),
		))

		req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/users/%s", newUser.ID), nil)
		req.Header.Set("Cookie", "session_token=fake_test_token")
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)
		print(rec.Body.String())
		assert.Equal(t, http.StatusOK, rec.Code)
		var response dto.UserDTO
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Equal(t, user.Name, response.Name)
		assert.Equal(t, user.Email, response.Email)
	})

	t.Run("GetAllUsers - Success", func(t *testing.T) {
		userDS.Clear()
		// Prepopulate mock data
		userRepo.Create(context.Background())(entities.UserCreate{
			Name:     "John Doe",
			Email:    "john.doe@example.com",
			Password: "hashed_password",
		})
		userRepo.Create(context.Background())(entities.UserCreate{
			Name:     "Jane Smith",
			Email:    "jane.smith@example.com",
			Password: "hashed_password",
		})

		req := httptest.NewRequest(http.MethodGet, "/api/users/", nil)
		req.Header.Set("Cookie", "session_token=fake_test_token")
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code)
		var response []dto.UserDTO
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Len(t, response, 2)
	})

	t.Run("UpdateUser - Success", func(t *testing.T) {
		// Prepopulate mock data

		newUser, _ := E.Unwrap(F.Pipe1(
			entities.UserCreate{
				Name:     "John Doe",
				Email:    "john.doe@example.com",
				Password: "hashed_password",
			},
			userRepo.Create(context.Background()),
		))

		userUpdateDTO := dto.UserUpdateDTO{
			Name:  StringPtr("John Updated"),
			Email: StringPtr("john.updated@example.com"),
		}

		body, _ := json.Marshal(userUpdateDTO)
		req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/users/%s", newUser.ID), bytes.NewBuffer(body))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Cookie", "session_token=fake_test_token")
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code)
		var response dto.UserDTO
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Equal(t, *userUpdateDTO.Name, response.Name)
		assert.Equal(t, *userUpdateDTO.Email, response.Email)
	})

	t.Run("DeleteUser - Success", func(t *testing.T) {
		// Prepopulate mock data
		newUser, _ := E.Unwrap(F.Pipe1(
			entities.UserCreate{
				Name:     "John Doe",
				Email:    "john.doe@example.com",
				Password: "hashed_password",
			},
			userRepo.Create(context.Background()),
		))

		req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/users/%s", newUser.ID), nil)
		req.Header.Set("Cookie", "session_token=fake_test_token")
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code)
		var response dto.UserDTO
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Equal(t, "John Doe", response.Name)
	})
}
