const LoginUserCommandHandler = require('../../../src/application/auth/commands/LoginUserCommandHandler');
const LoginUserCommand = require('../../../src/application/auth/commands/LoginUserCommand');
const bcrypt = require('bcryptjs');
const JwtService = require('../../../src/application/services/JwtService');

// Mock dependencies
jest.mock('bcryptjs');
jest.mock('../../../src/application/services/JwtService');

describe('LoginUserCommandHandler', () => {
  let handler;
  let mockPrisma;
  let mockJwtService;

  beforeEach(() => {
    // Mock JwtService instance
    mockJwtService = {
      generateToken: jest.fn()
    };
    JwtService.mockImplementation(() => mockJwtService);

    // Mock Prisma
    mockPrisma = {
      user: {
        findUnique: jest.fn()
      }
    };

    handler = new LoginUserCommandHandler(mockPrisma);

    // Reset all mocks
    jest.clearAllMocks();
  });

  describe('handle - success cases', () => {
    it('should login user successfully with valid credentials', async () => {
      // Arrange
      const command = new LoginUserCommand('john@example.com', 'password123');
      
      mockPrisma.user.findUnique.mockResolvedValue({
        id: 1,
        name: 'John Doe',
        email: 'john@example.com',
        password: 'hashed_password',
        createdAt: new Date(),
        updatedAt: new Date()
      });
      bcrypt.compare.mockResolvedValue(true); // Password is valid
      mockJwtService.generateToken.mockReturnValue('mock_jwt_token');

      // Act
      const result = await handler.handle(command);

      // Assert
      expect(mockPrisma.user.findUnique).toHaveBeenCalledWith({
        where: { email: 'john@example.com' }
      });
      expect(bcrypt.compare).toHaveBeenCalledWith('password123', 'hashed_password');
      expect(mockJwtService.generateToken).toHaveBeenCalledWith({
        userId: 1,
        email: 'john@example.com'
      });
      expect(result.user).toEqual({
        id: 1,
        name: 'John Doe',
        email: 'john@example.com',
        createdAt: expect.any(Date),
        updatedAt: expect.any(Date)
      });
      expect(result.token).toBe('mock_jwt_token');
      expect(result.user.password).toBeUndefined(); // Password should not be returned
    });
  });

  describe('handle - validation errors', () => {
    it('should throw error if email is missing', async () => {
      // Arrange
      const command = new LoginUserCommand('', 'password123');

      // Act & Assert
      await expect(handler.handle(command)).rejects.toThrow('Email and password are required');
    });

    it('should throw error if password is missing', async () => {
      // Arrange
      const command = new LoginUserCommand('john@example.com', '');

      // Act & Assert
      await expect(handler.handle(command)).rejects.toThrow('Email and password are required');
    });

    it('should throw error if user does not exist', async () => {
      // Arrange
      const command = new LoginUserCommand('nonexistent@example.com', 'password123');
      
      mockPrisma.user.findUnique.mockResolvedValue(null);

      // Act & Assert
      await expect(handler.handle(command)).rejects.toThrow('Invalid email or password');
    });

    it('should throw error if password is incorrect', async () => {
      // Arrange
      const command = new LoginUserCommand('john@example.com', 'wrongpassword');
      
      mockPrisma.user.findUnique.mockResolvedValue({
        id: 1,
        email: 'john@example.com',
        password: 'hashed_password'
      });
      bcrypt.compare.mockResolvedValue(false); // Password is invalid

      // Act & Assert
      await expect(handler.handle(command)).rejects.toThrow('Invalid email or password');
    });
  });
});
