const AuthController = require('../../../src/api/controllers/AuthController');
const RegisterUserCommand = require('../../../src/application/auth/commands/RegisterUserCommand');
const LoginUserCommand = require('../../../src/application/auth/commands/LoginUserCommand');

describe('AuthController', () => {
  let controller;
  let mockRegisterHandler;
  let mockLoginHandler;
  let mockReq;
  let mockRes;

  beforeEach(() => {
    // Mock handlers
    mockRegisterHandler = {
      handle: jest.fn()
    };
    mockLoginHandler = {
      handle: jest.fn()
    };

    controller = new AuthController(mockRegisterHandler, mockLoginHandler);

    // Mock Express req/res
    mockReq = {
      body: {}
    };
    mockRes = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn()
    };

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

  describe('register', () => {
    it('should register user successfully and return 201', async () => {
      // Arrange
      mockReq.body = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123'
      };

      const mockResult = {
        user: { id: 1, name: 'John Doe', email: 'john@example.com' },
        token: 'mock_jwt_token'
      };

      mockRegisterHandler.handle.mockResolvedValue(mockResult);

      // Act
      await controller.register(mockReq, mockRes);

      // Assert
      expect(mockRegisterHandler.handle).toHaveBeenCalledWith(
        expect.any(RegisterUserCommand)
      );
      expect(mockRes.status).toHaveBeenCalledWith(201);
      expect(mockRes.json).toHaveBeenCalledWith({
        message: 'User registered successfully',
        data: mockResult
      });
    });

    it('should return 400 for validation errors', async () => {
      // Arrange
      mockReq.body = {
        name: '',
        email: 'john@example.com',
        password: 'password123'
      };

      mockRegisterHandler.handle.mockRejectedValue(new Error('Name, email and password are required'));

      // Act
      await controller.register(mockReq, mockRes);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(400);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Name, email and password are required'
      });
    });

    it('should return 400 if user already exists', async () => {
      // Arrange
      mockReq.body = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123'
      };

      mockRegisterHandler.handle.mockRejectedValue(new Error('User with this email already exists'));

      // Act
      await controller.register(mockReq, mockRes);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(400);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'User with this email already exists'
      });
    });

    it('should return 500 for unexpected errors', async () => {
      // Arrange
      mockReq.body = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123'
      };

      mockRegisterHandler.handle.mockRejectedValue(new Error('Database connection failed'));

      // Act
      await controller.register(mockReq, mockRes);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(500);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Database connection failed'
      });
    });
  });

  describe('login', () => {
    it('should login user successfully and return 200', async () => {
      // Arrange
      mockReq.body = {
        email: 'john@example.com',
        password: 'password123'
      };

      const mockResult = {
        user: { id: 1, name: 'John Doe', email: 'john@example.com' },
        token: 'mock_jwt_token'
      };

      mockLoginHandler.handle.mockResolvedValue(mockResult);

      // Act
      await controller.login(mockReq, mockRes);

      // Assert
      expect(mockLoginHandler.handle).toHaveBeenCalledWith(
        expect.any(LoginUserCommand)
      );
      expect(mockRes.status).toHaveBeenCalledWith(200);
      expect(mockRes.json).toHaveBeenCalledWith({
        message: 'Login successful',
        data: mockResult
      });
    });

    it('should return 401 for invalid credentials', async () => {
      // Arrange
      mockReq.body = {
        email: 'john@example.com',
        password: 'wrongpassword'
      };

      mockLoginHandler.handle.mockRejectedValue(new Error('Invalid email or password'));

      // Act
      await controller.login(mockReq, mockRes);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Invalid email or password'
      });
    });

    it('should return 401 for missing credentials', async () => {
      // Arrange
      mockReq.body = {
        email: '',
        password: ''
      };

      mockLoginHandler.handle.mockRejectedValue(new Error('Email and password are required'));

      // Act
      await controller.login(mockReq, mockRes);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Email and password are required'
      });
    });
  });
});
