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"

	"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 TestAuthService(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	authRepo := repository.NewInMemoryAuthRepositoryImpl("secret")
	userDS := mocks.NewMockUserDataSource()
	userRepo := repository.NewUserRepository(userDS)
	authService := services.NewAuthService(authRepo, userRepo)

	router := gin.Default()
	router.RedirectTrailingSlash = false
	api := router.Group("/api/auth")
	authService.RegisterRoutes(api)

	populateUser := func(email, password, name string) entities.User {
		newUser, _ := E.Unwrap(F.Pipe1(
			entities.UserCreate{
				Email:    email,
				Password: password,
				Name:     name,
			},
			userRepo.Create(context.Background()),
		))
		return newUser
	}

	t.Run("Login - Success", func(t *testing.T) {
		user := populateUser("test.user@example.com", "password123", "Test User")

		loginRequest := dto.LoginRequestDTO{
			Email:    user.Email,
			Password: "password123",
		}

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

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code)
		var response dto.TokenResponseDTO
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.NotEmpty(t, response.Token)
	})

	t.Run("Validate - Success", func(t *testing.T) {
		user := populateUser("validate.user@example.com", "password123", "Validate User")
		token, _ := E.Unwrap(F.Pipe1(user.ID, authRepo.GenerateToken(context.Background())))

		req := httptest.NewRequest(http.MethodPost, "/api/auth/validate", nil)
		req.Header.Set("Cookie", "session_token="+token)
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code)
		var response map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Equal(t, "Token validated", response["message"])
		assert.Equal(t, user.ID, response["user_id"])
	})

	t.Run("Logout - Success", func(t *testing.T) {
		user := populateUser("logout.user@example.com", "password123", "Logout User")
		token, _ := E.Unwrap(F.Pipe1(user.ID, authRepo.GenerateToken(context.Background())))

		req := httptest.NewRequest(http.MethodPost, "/api/auth/logout", nil)
		req.Header.Set("Cookie", "session_token="+token)
		rec := httptest.NewRecorder()

		router.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code)
		var response map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		assert.NoError(t, err)
		assert.Equal(t, "Token revoked", response["message"])
		assert.Equal(t, user.ID, response["user_id"])
	})
}
