package auth

import (
	"context"
	"errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"tercul/internal/domain"
	"tercul/internal/platform/auth"
	"testing"
)

type AuthQueriesSuite struct {
	suite.Suite
	userRepo   *mockUserRepository
	jwtManager *mockJWTManager
	queries    *AuthQueries
}

func (s *AuthQueriesSuite) SetupTest() {
	s.userRepo = newMockUserRepository()
	s.jwtManager = &mockJWTManager{}
	s.queries = NewAuthQueries(s.userRepo, s.jwtManager)
}

func TestAuthQueriesSuite(t *testing.T) {
	suite.Run(t, new(AuthQueriesSuite))
}

func (s *AuthQueriesSuite) TestGetUserFromContext_Success() {
	user := domain.User{Active: true}
	user.ID = 1
	s.userRepo.users[1] = user

	ctx := context.WithValue(context.Background(), auth.ClaimsContextKey, &auth.Claims{UserID: 1})

	retrievedUser, err := s.queries.GetUserFromContext(ctx)
	assert.NoError(s.T(), err)
	assert.NotNil(s.T(), retrievedUser)
	assert.Equal(s.T(), user.ID, retrievedUser.ID)
}

func (s *AuthQueriesSuite) TestGetUserFromContext_NoClaims() {
	retrievedUser, err := s.queries.GetUserFromContext(context.Background())
	assert.Error(s.T(), err)
	assert.Nil(s.T(), retrievedUser)
}

func (s *AuthQueriesSuite) TestGetUserFromContext_UserNotFound() {
	ctx := context.WithValue(context.Background(), auth.ClaimsContextKey, &auth.Claims{UserID: 1})

	retrievedUser, err := s.queries.GetUserFromContext(ctx)
	assert.ErrorIs(s.T(), err, ErrUserNotFound)
	assert.Nil(s.T(), retrievedUser)
}

func (s *AuthQueriesSuite) TestGetUserFromContext_InactiveUser() {
	user := domain.User{Active: false}
	user.ID = 1
	s.userRepo.users[1] = user

	ctx := context.WithValue(context.Background(), auth.ClaimsContextKey, &auth.Claims{UserID: 1})

	retrievedUser, err := s.queries.GetUserFromContext(ctx)
	assert.ErrorIs(s.T(), err, ErrInvalidCredentials)
	assert.Nil(s.T(), retrievedUser)
}

func (s *AuthQueriesSuite) TestGetUserFromContext_NilContext() {
	//nolint:staticcheck // This test intentionally passes a nil context to verify error handling.
	user, err := s.queries.GetUserFromContext(nil)
	assert.ErrorIs(s.T(), err, ErrContextRequired)
	assert.Nil(s.T(), user)
}

func (s *AuthQueriesSuite) TestValidateToken_NilContext() {
	//nolint:staticcheck // This test intentionally passes a nil context to verify error handling.
	user, err := s.queries.ValidateToken(nil, "token")
	assert.ErrorIs(s.T(), err, ErrContextRequired)
	assert.Nil(s.T(), user)
}

func (s *AuthQueriesSuite) TestValidateToken_Success() {
	user := domain.User{Active: true}
	user.ID = 1
	s.userRepo.users[1] = user

	s.jwtManager.validateTokenFunc = func(tokenString string) (*auth.Claims, error) {
		return &auth.Claims{UserID: 1}, nil
	}

	retrievedUser, err := s.queries.ValidateToken(context.Background(), "valid-token")
	assert.NoError(s.T(), err)
	assert.NotNil(s.T(), retrievedUser)
	assert.Equal(s.T(), user.ID, retrievedUser.ID)
}

func (s *AuthQueriesSuite) TestValidateToken_EmptyToken() {
	retrievedUser, err := s.queries.ValidateToken(context.Background(), "")
	assert.ErrorIs(s.T(), err, auth.ErrMissingToken)
	assert.Nil(s.T(), retrievedUser)
}

func (s *AuthQueriesSuite) TestValidateToken_InvalidToken() {
	s.jwtManager.validateTokenFunc = func(tokenString string) (*auth.Claims, error) {
		return nil, errors.New("invalid token")
	}

	retrievedUser, err := s.queries.ValidateToken(context.Background(), "invalid-token")
	assert.Error(s.T(), err)
	assert.Nil(s.T(), retrievedUser)
}

func (s *AuthQueriesSuite) TestValidateToken_UserNotFound() {
	s.jwtManager.validateTokenFunc = func(tokenString string) (*auth.Claims, error) {
		return &auth.Claims{UserID: 1}, nil
	}

	retrievedUser, err := s.queries.ValidateToken(context.Background(), "valid-token")
	assert.ErrorIs(s.T(), err, ErrUserNotFound)
	assert.Nil(s.T(), retrievedUser)
}

func (s *AuthQueriesSuite) TestValidateToken_InactiveUser() {
	user := domain.User{Active: false}
	user.ID = 1
	s.userRepo.users[1] = user

	s.jwtManager.validateTokenFunc = func(tokenString string) (*auth.Claims, error) {
		return &auth.Claims{UserID: 1}, nil
	}

	retrievedUser, err := s.queries.ValidateToken(context.Background(), "valid-token")
	assert.ErrorIs(s.T(), err, ErrInvalidCredentials)
	assert.Nil(s.T(), retrievedUser)
}
