//go:build integration

package auth

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/joho/godotenv"
	"github.com/nexuscrm/backend/internal/infrastructure/database"
	"github.com/nexuscrm/backend/internal/infrastructure/persistence"
	"github.com/nexuscrm/backend/pkg/auth"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestAuthService_Integration_LoginFlow(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// 1. Setup DB
	// Load .env from project root relative to this test file
	// We are in backend/internal/features/auth, so root is ../../../../
	if err := godotenv.Load("../../../../.env"); err != nil {
		t.Logf("Warning: .env file not found at project root: %v", err)
	}

	conn, err := database.GetInstance()
	if err != nil {
		t.Skip("Skipping integration test: failed to connect to DB: " + err.Error())
	}
	db := conn.DB()

	// 2. Setup Repos & Service
	userRepo := persistence.NewUserRepository(db)
	sessionRepo := persistence.NewSessionRepository(db)
	permRepo := persistence.NewPermissionRepository(db)

	svc := NewAuthService(userRepo, sessionRepo, permRepo)

	// 3. Create Test User
	email := fmt.Sprintf("test_auth_%d@example.com", time.Now().UnixNano())
	password := "TestPassword123!"

	// Ensure the referenced Profile row exists (FK constraint: user.ProfileId -> profile.Id)
	_, _ = db.Exec("INSERT IGNORE INTO Profile (Id, Name, Description) VALUES (?, ?, ?)",
		constants.ProfileStandardUser, "Standard User", "Default profile for integration tests")

	// Create user explicitly via Repo to ensure we have a valid user to log in with
	// We need to hash password manually first as AuthService.Login expects it hashed in DB
	hashedPassword, err := auth.HashPassword(password)
	require.NoError(t, err)

	userID := fmt.Sprintf("u_%d", time.Now().UnixNano())
	user := &models.User{
		ID:          userID,
		Username:    email,
		Email:       email,
		FirstName:   "Test",
		LastName:    "User",
		Password:    hashedPassword,
		IsActive:    true,
		CreatedDate: time.Now().UTC(),
		ProfileID:   constants.ProfileStandardUser,
	}

	err = userRepo.CreateUser(context.Background(), user)
	require.NoError(t, err, "Failed to create test user")

	// Cleanup
	defer func() {
		_ = userRepo.DeleteUser(context.Background(), userID)
	}()

	// 4. Test Login
	ctx := context.Background()
	loginResult, err := svc.Login(ctx, email, password, "127.0.0.1", "Test-Agent", nil)
	require.NoError(t, err, "Login should succeed")
	assert.NotEmpty(t, loginResult.Token)
	assert.Equal(t, email, loginResult.User.Email)

	// 5. Test Validate Session
	claims, err := svc.ValidateSession(ctx, loginResult.Token)
	assert.NoError(t, err, "Session validation should succeed")
	assert.Equal(t, userID, claims.Subject)

	// 6. Test Logout
	err = svc.Logout(ctx, loginResult.Token)
	assert.NoError(t, err, "Logout should succeed")

	// 7. Test Session Invalid after Logout
	_, err = svc.ValidateSession(ctx, loginResult.Token)
	assert.Error(t, err, "Session should be invalid after logout")
	assert.Contains(t, err.Error(), "revoked")
}

func TestAuthService_Integration_LoginRejectsInactiveUser(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	if err := godotenv.Load("../../../../.env"); err != nil {
		t.Logf("Warning: .env file not found at project root: %v", err)
	}

	conn, err := database.GetInstance()
	if err != nil {
		t.Skip("Skipping integration test: failed to connect to DB: " + err.Error())
	}
	db := conn.DB()

	userRepo := persistence.NewUserRepository(db)
	sessionRepo := persistence.NewSessionRepository(db)
	permRepo := persistence.NewPermissionRepository(db)

	svc := NewAuthService(userRepo, sessionRepo, permRepo)

	_, _ = db.Exec("INSERT IGNORE INTO Profile (Id, Name, Description) VALUES (?, ?, ?)",
		constants.ProfileStandardUser, "Standard User", "Default profile for integration tests")

	email := fmt.Sprintf("test_auth_inactive_%d@example.com", time.Now().UnixNano())
	password := "TestPassword123!"
	hashedPassword, err := auth.HashPassword(password)
	require.NoError(t, err)

	userID := fmt.Sprintf("u_inactive_%d", time.Now().UnixNano())
	user := &models.User{
		ID:          userID,
		Username:    email,
		Email:       email,
		FirstName:   "Inactive",
		LastName:    "User",
		Password:    hashedPassword,
		IsActive:    false,
		CreatedDate: time.Now().UTC(),
		ProfileID:   constants.ProfileStandardUser,
	}

	err = userRepo.CreateUser(context.Background(), user)
	require.NoError(t, err, "Failed to create inactive test user")

	defer func() {
		_ = userRepo.DeleteUser(context.Background(), userID)
	}()

	loginResult, err := svc.Login(context.Background(), email, password, "127.0.0.1", "Test-Agent", nil)
	require.Error(t, err, "Login should fail for inactive user")
	assert.Contains(t, err.Error(), "inactive")
	assert.Nil(t, loginResult)

	var sessionCount int
	err = db.QueryRow(
		fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s = ?", constants.TableSession, constants.FieldSysSession_UserID),
		userID,
	).Scan(&sessionCount)
	require.NoError(t, err)
	assert.Equal(t, 0, sessionCount, "inactive users must not receive persisted sessions")
}
