package examples

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

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"

	"erp-mvp/core-service/internal/api/handlers"
	"erp-mvp/core-service/internal/api/middleware"
	"erp-mvp/core-service/internal/auth"
	"erp-mvp/core-service/internal/models"
)

// MockAuthService мок для AuthService
type MockAuthService struct {
	mock.Mock
}

func (m *MockAuthService) Register(ctx context.Context, req *models.RegisterRequest) (*models.LoginResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*models.LoginResponse), args.Error(1)
}

func (m *MockAuthService) Login(ctx context.Context, req *models.LoginRequest) (*models.LoginResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*models.LoginResponse), args.Error(1)
}

// TestAuthHandler_Register тестирует endpoint регистрации
func TestAuthHandler_Register(t *testing.T) {
	// Arrange
	gin.SetMode(gin.TestMode)

	mockAuthService := &MockAuthService{}
	handler := handlers.NewAuthHandler(mockAuthService)

	router := gin.New()
	router.POST("/register", handler.Register)

	registerReq := &models.RegisterRequest{
		OrganizationName: "Test Workshop",
		UserEmail:        "admin@test.com",
		UserPassword:     "password123",
		OrganizationType: "workshop",
	}

	expectedResponse := &models.LoginResponse{
		Token: "test_token",
		User: models.UserResponse{
			ID:    uuid.New(),
			Email: "admin@test.com",
			Role:  "admin",
		},
		Organization: models.OrganizationResponse{
			ID:   uuid.New(),
			Name: "Test Workshop",
			Type: "workshop",
		},
	}

	mockAuthService.On("Register", mock.Anything, registerReq).Return(expectedResponse, nil)

	reqBody, _ := json.Marshal(registerReq)

	// Act
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(reqBody))
	req.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusCreated, w.Code)

	var response models.LoginResponse
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, expectedResponse.Token, response.Token)
	assert.Equal(t, expectedResponse.User.Email, response.User.Email)
	assert.Equal(t, expectedResponse.Organization.Name, response.Organization.Name)

	mockAuthService.AssertExpectations(t)
}

// TestAuthHandler_Login тестирует endpoint входа
func TestAuthHandler_Login(t *testing.T) {
	// Arrange
	gin.SetMode(gin.TestMode)

	mockAuthService := &MockAuthService{}
	handler := handlers.NewAuthHandler(mockAuthService)

	router := gin.New()
	router.POST("/login", handler.Login)

	loginReq := &models.LoginRequest{
		Email:    "admin@test.com",
		Password: "password123",
	}

	expectedResponse := &models.LoginResponse{
		Token: "test_token",
		User: models.UserResponse{
			ID:    uuid.New(),
			Email: "admin@test.com",
			Role:  "admin",
		},
		Organization: models.OrganizationResponse{
			ID:   uuid.New(),
			Name: "Test Workshop",
			Type: "workshop",
		},
	}

	mockAuthService.On("Login", mock.Anything, loginReq).Return(expectedResponse, nil)

	reqBody, _ := json.Marshal(loginReq)

	// Act
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "/login", bytes.NewBuffer(reqBody))
	req.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)

	var response models.LoginResponse
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, expectedResponse.Token, response.Token)

	mockAuthService.AssertExpectations(t)
}

// TestAuthHandler_Register_ValidationError тестирует валидацию при регистрации
func TestAuthHandler_Register_ValidationError(t *testing.T) {
	// Arrange
	gin.SetMode(gin.TestMode)

	mockAuthService := &MockAuthService{}
	handler := handlers.NewAuthHandler(mockAuthService)

	router := gin.New()
	router.POST("/register", handler.Register)

	// Неверный запрос - отсутствует email
	registerReq := map[string]interface{}{
		"organization_name": "Test Workshop",
		"user_password":     "password123",
		"organization_type": "workshop",
	}

	reqBody, _ := json.Marshal(registerReq)

	// Act
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(reqBody))
	req.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var errorResponse map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &errorResponse)
	assert.NoError(t, err)
	assert.Contains(t, errorResponse["error"], "Validation failed")
}

// TestAuthMiddleware тестирует middleware аутентификации
func TestAuthMiddleware(t *testing.T) {
	// Arrange
	gin.SetMode(gin.TestMode)

	secret := "test_secret_key"
	ttl := 24 * time.Hour
	jwtService := auth.NewJWTService(secret, ttl)
	authMiddleware := middleware.NewAuthMiddleware(jwtService)

	router := gin.New()
	router.Use(authMiddleware.AuthRequired())

	router.GET("/protected", func(c *gin.Context) {
		claims := middleware.GetClaims(c)
		c.JSON(http.StatusOK, gin.H{
			"user_id":         claims.UserID.String(),
			"organization_id": claims.OrganizationID.String(),
			"email":           claims.Email,
			"role":            claims.Role,
		})
	})

	// Создаем валидный JWT токен
	userID := uuid.New()
	orgID := uuid.New()
	validToken, err := jwtService.GenerateToken(userID, orgID, "test@example.com", "admin")
	assert.NoError(t, err)

	// Act - тест с валидным токеном
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/protected", nil)
	req.Header.Set("Authorization", "Bearer "+validToken)
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, userID.String(), response["user_id"])
	assert.Equal(t, "test@example.com", response["email"])

	// Act - тест без токена
	w = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "/protected", nil)
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusUnauthorized, w.Code)

	// Act - тест с неверным токеном
	w = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "/protected", nil)
	req.Header.Set("Authorization", "Bearer invalid_token")
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

// TestLocationHandler_CreateLocation тестирует создание места хранения
func TestLocationHandler_CreateLocation(t *testing.T) {
	// Arrange
	gin.SetMode(gin.TestMode)

	mockLocationService := &MockLocationService{}
	handler := handlers.NewLocationHandler(mockLocationService)

	router := gin.New()
	router.Use(mockAuthMiddleware())
	router.POST("/locations", handler.CreateLocation)

	locationReq := &models.CreateLocationRequest{
		Name:    "Test Location",
		Address: "Test Address",
		Type:    "warehouse",
		Coordinates: models.JSON{
			"lat": 55.7558,
			"lng": 37.6176,
		},
	}

	expectedLocation := &models.StorageLocation{
		ID:      uuid.New(),
		Name:    "Test Location",
		Address: "Test Address",
		Type:    "warehouse",
		Coordinates: models.JSON{
			"lat": 55.7558,
			"lng": 37.6176,
		},
	}

	mockLocationService.On("CreateLocation", mock.Anything, mock.Anything, locationReq).Return(expectedLocation, nil)

	reqBody, _ := json.Marshal(locationReq)

	// Act
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "/locations", bytes.NewBuffer(reqBody))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer test_token")
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusCreated, w.Code)

	var response models.StorageLocation
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, expectedLocation.Name, response.Name)
	assert.Equal(t, expectedLocation.Type, response.Type)

	mockLocationService.AssertExpectations(t)
}

// MockLocationService мок для LocationService
type MockLocationService struct {
	mock.Mock
}

func (m *MockLocationService) CreateLocation(ctx context.Context, orgID uuid.UUID, req *models.CreateLocationRequest) (*models.StorageLocation, error) {
	args := m.Called(ctx, orgID, req)
	return args.Get(0).(*models.StorageLocation), args.Error(1)
}

func (m *MockLocationService) GetLocations(ctx context.Context, orgID uuid.UUID) ([]*models.StorageLocation, error) {
	args := m.Called(ctx, orgID)
	return args.Get(0).([]*models.StorageLocation), args.Error(1)
}

func (m *MockLocationService) GetLocation(ctx context.Context, id, orgID uuid.UUID) (*models.StorageLocation, error) {
	args := m.Called(ctx, id, orgID)
	return args.Get(0).(*models.StorageLocation), args.Error(1)
}

func (m *MockLocationService) UpdateLocation(ctx context.Context, id, orgID uuid.UUID, req *models.CreateLocationRequest) (*models.StorageLocation, error) {
	args := m.Called(ctx, id, orgID, req)
	return args.Get(0).(*models.StorageLocation), args.Error(1)
}

func (m *MockLocationService) DeleteLocation(ctx context.Context, id, orgID uuid.UUID) error {
	args := m.Called(ctx, id, orgID)
	return args.Error(0)
}

func (m *MockLocationService) GetChildren(ctx context.Context, parentID, orgID uuid.UUID) ([]*models.StorageLocation, error) {
	args := m.Called(ctx, parentID, orgID)
	return args.Get(0).([]*models.StorageLocation), args.Error(1)
}

// mockAuthMiddleware создает middleware для тестов
func mockAuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Устанавливаем тестовые claims
		claims := &auth.Claims{
			UserID:         uuid.New(),
			OrganizationID: uuid.New(),
			Email:          "test@example.com",
			Role:           "admin",
		}
		c.Set("user_id", claims.UserID)
		c.Set("organization_id", claims.OrganizationID)
		c.Set("email", claims.Email)
		c.Set("role", claims.Role)
		c.Next()
	}
}
