const authMiddleware = require('../../../src/api/middlewares/authMiddleware');
const JwtService = require('../../../src/application/services/JwtService');

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

describe('authMiddleware (Cookie-based)', () => {
  let mockReq;
  let mockRes;
  let mockNext;
  let mockJwtService;

  beforeEach(() => {
    // Mock Express req/res/next
    mockReq = {
      cookies: {}
    };
    mockRes = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn()
    };
    mockNext = jest.fn();

    // Mock JwtService instance
    mockJwtService = {
      extractTokenFromCookies: jest.fn(),
      verifyToken: jest.fn()
    };

    JwtService.mockImplementation(() => mockJwtService);

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

  describe('successful authentication', () => {
    it('should authenticate valid JWT token from cookie and call next()', () => {
      // Arrange
      mockReq.cookies = { auth_token: 'valid_token_123' };
      
      const mockDecoded = {
        userId: 1,
        email: 'john@example.com'
      };

      mockJwtService.extractTokenFromCookies.mockReturnValue('valid_token_123');
      mockJwtService.verifyToken.mockReturnValue(mockDecoded);

      // Act
      authMiddleware(mockReq, mockRes, mockNext);

      // Assert
      expect(mockReq.user).toEqual({
        userId: 1,
        email: 'john@example.com'
      });
      expect(mockNext).toHaveBeenCalled();
      expect(mockRes.status).not.toHaveBeenCalled();
    });
  });

  describe('authentication failures', () => {
    it('should return 401 if no cookie is present', () => {
      // Arrange
      mockReq.cookies = {};
      
      mockJwtService.extractTokenFromCookies.mockReturnValue(null);

      // Act
      authMiddleware(mockReq, mockRes, mockNext);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Authentication required',
        message: 'No token provided in cookies'
      });
      expect(mockNext).not.toHaveBeenCalled();
    });

    it('should return 401 if cookie token is invalid', () => {
      // Arrange
      mockReq.cookies = { auth_token: 'invalid_token' };
      
      mockJwtService.extractTokenFromCookies.mockReturnValue('invalid_token');
      mockJwtService.verifyToken.mockImplementation(() => {
        throw new Error('Invalid or expired token');
      });

      // Act
      authMiddleware(mockReq, mockRes, mockNext);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Authentication failed',
        message: 'Invalid or expired token'
      });
      expect(mockNext).not.toHaveBeenCalled();
    });

    it('should return 401 if token is expired', () => {
      // Arrange
      mockReq.cookies = { auth_token: 'expired_token' };
      
      mockJwtService.extractTokenFromCookies.mockReturnValue('expired_token');
      mockJwtService.verifyToken.mockImplementation(() => {
        throw new Error('Token has expired');
      });

      // Act
      authMiddleware(mockReq, mockRes, mockNext);

      // Assert
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({
        error: 'Authentication failed',
        message: 'Token has expired'
      });
      expect(mockNext).not.toHaveBeenCalled();
    });
  });
});
