import { UserModel } from '../UserModel';
import { CreateUserInput } from '../types';

// Mock the database pool
jest.mock('../../config/database', () => ({
  query: jest.fn()
}));

// Mock the logger
jest.mock('../../utils/logger', () => ({
  info: jest.fn(),
  error: jest.fn(),
  warn: jest.fn()
}));

describe('UserModel', () => {
  let mockPool: any;

  beforeEach(() => {
    jest.clearAllMocks();
    mockPool = require('../../config/database');
  });

  describe('create', () => {
    it('should create a new user successfully', async () => {
      const userData: CreateUserInput = {
        email: 'test@example.com',
        name: 'Test User',
        password: 'password123',
        role: 'user'
      };

      const mockUser = {
        id: '123e4567-e89b-12d3-a456-426614174000',
        email: userData.email,
        name: userData.name,
        password_hash: 'hashed_password',
        role: userData.role,
        created_at: new Date(),
        updated_at: new Date(),
        is_active: true
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUser] });

      const result = await UserModel.create(userData);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO users'),
        [userData.email, userData.name, userData.password, userData.role]
      );
      expect(result).toEqual(mockUser);
    });

    it('should handle database errors', async () => {
      const userData: CreateUserInput = {
        email: 'test@example.com',
        name: 'Test User',
        password: 'password123'
      };

      const error = new Error('Database error');
      mockPool.query.mockRejectedValueOnce(error);

      await expect(UserModel.create(userData)).rejects.toThrow('Database error');
    });
  });

  describe('findById', () => {
    it('should find user by ID successfully', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';
      const mockUser = {
        id: userId,
        email: 'test@example.com',
        name: 'Test User',
        password_hash: 'hashed_password',
        role: 'user',
        created_at: new Date(),
        updated_at: new Date(),
        is_active: true
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUser] });

      const result = await UserModel.findById(userId);

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT * FROM users WHERE id = $1 AND is_active = true',
        [userId]
      );
      expect(result).toEqual(mockUser);
    });

    it('should return null when user not found', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';

      mockPool.query.mockResolvedValueOnce({ rows: [] });

      const result = await UserModel.findById(userId);

      expect(result).toBeNull();
    });
  });

  describe('findByEmail', () => {
    it('should find user by email successfully', async () => {
      const email = 'test@example.com';
      const mockUser = {
        id: '123e4567-e89b-12d3-a456-426614174000',
        email,
        name: 'Test User',
        password_hash: 'hashed_password',
        role: 'user',
        created_at: new Date(),
        updated_at: new Date(),
        is_active: true
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUser] });

      const result = await UserModel.findByEmail(email);

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT * FROM users WHERE email = $1 AND is_active = true',
        [email]
      );
      expect(result).toEqual(mockUser);
    });
  });

  describe('update', () => {
    it('should update user successfully', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';
      const updates = {
        name: 'Updated Name',
        email: 'updated@example.com'
      };

      const mockUpdatedUser = {
        id: userId,
        ...updates,
        password_hash: 'hashed_password',
        role: 'user',
        created_at: new Date(),
        updated_at: new Date(),
        is_active: true
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUpdatedUser] });

      const result = await UserModel.update(userId, updates);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('UPDATE users'),
        expect.arrayContaining([updates.name, updates.email, userId])
      );
      expect(result).toEqual(mockUpdatedUser);
    });
  });

  describe('delete', () => {
    it('should soft delete user successfully', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';

      mockPool.query.mockResolvedValueOnce({ rows: [{ id: userId }] });

      const result = await UserModel.delete(userId);

      expect(mockPool.query).toHaveBeenCalledWith(
        'UPDATE users SET is_active = false WHERE id = $1 RETURNING id',
        [userId]
      );
      expect(result).toBe(true);
    });

    it('should return false when user not found', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';

      mockPool.query.mockResolvedValueOnce({ rows: [] });

      const result = await UserModel.delete(userId);

      expect(result).toBe(false);
    });
  });

  describe('emailExists', () => {
    it('should return true when email exists', async () => {
      const email = 'test@example.com';

      mockPool.query.mockResolvedValueOnce({ rows: [{ id: '123' }] });

      const result = await UserModel.emailExists(email);

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT id FROM users WHERE email = $1 AND is_active = true',
        [email]
      );
      expect(result).toBe(true);
    });

    it('should return false when email does not exist', async () => {
      const email = 'test@example.com';

      mockPool.query.mockResolvedValueOnce({ rows: [] });

      const result = await UserModel.emailExists(email);

      expect(result).toBe(false);
    });
  });

  describe('count', () => {
    it('should return correct user count', async () => {
      const expectedCount = 5;

      mockPool.query.mockResolvedValueOnce({ rows: [{ count: expectedCount.toString() }] });

      const result = await UserModel.count();

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT COUNT(*) FROM users WHERE is_active = true'
      );
      expect(result).toBe(expectedCount);
    });
  });
}); 