// test/friendService.test.js

const sequelize = require('../config/sequelize'); // Sequelize 인스턴스 임포트
const User = require('../models/User');
const Friend = require('../models/Friend');
const friendService = require('../services/friendService'); // FriendService 임포트

// Sequelize의 Op를 가져오기 위해 추가
const { Op } = require('sequelize');

beforeAll(async () => {
  // 테스트 전에 데이터베이스를 동기화하고 테이블을 생성합니다.
  await sequelize.sync({ force: true });
});

beforeEach(async () => {
  // 각 테스트 전에 데이터베이스를 초기화하여 독립성을 보장합니다.
  await sequelize.sync({ force: true });

  // 더미 사용자 생성
  await User.bulkCreate([
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob', email: 'bob@example.com' },
    { id: 3, name: 'Charlie', email: 'charlie@example.com' },
  ]);
});

afterAll(async () => {
  // 모든 테스트가 끝난 후 데이터베이스 연결을 종료합니다.
  await sequelize.close();
});

describe('Friend Service', () => {
  describe('validUser', () => {
    test('should return user when user exists', async () => {
      const user = await friendService.validUser(1);
      expect(user).toBeDefined();
      expect(user.name).toBe('Alice');
    });

    test('should throw error when user does not exist', async () => {
      await expect(friendService.validUser(999)).rejects.toThrow('User not found');
    });
  });

  describe('sendFriendRequest', () => {
    test('should send a friend request successfully', async () => {
      const friendRequest = await friendService.sendFriendRequest(1, 3); // Alice sends request to Charlie
      expect(friendRequest).toBeDefined();
      expect(friendRequest.requester_id).toBe(1);
      expect(friendRequest.receiver_id).toBe(3);
      expect(friendRequest.status).toBe('PENDING');
    });

    test('should throw error when sending friend request to self', async () => {
      await expect(friendService.sendFriendRequest(1, 1)).rejects.toThrow('Cannot send friend request to yourself');
    });

    test('should throw error when sending duplicate friend request', async () => {
      
      await friendService.sendFriendRequest(1, 2);

    
      await friendService.acceptFriendRequest(2, 1);

      await expect(friendService.sendFriendRequest(1, 2)).rejects.toThrow('Friend request already exists');
    });

    test('should throw error when user does not exist', async () => {
      await expect(friendService.sendFriendRequest(1, 999)).rejects.toThrow('User not found');
      await expect(friendService.sendFriendRequest(999, 1)).rejects.toThrow('User not found');
    });
  });

  describe('getReceivedRequests', () => {
    test('friend requests', async () => {
      await friendService.sendFriendRequest(3, 1);

      const receivedRequests = await friendService.getReceivedRequests(1);
      expect(receivedRequests.length).toBe(1);
      expect(receivedRequests[0].requester.name).toBe('Charlie');
    });

    test('not send request', async () => {
      const receivedRequests = await friendService.getReceivedRequests(2); 
      expect(receivedRequests.length).toBe(0);
    });
  });

  describe('getSentRequests', () => {
    test('should retrieve sent friend requests', async () => {

      await friendService.sendFriendRequest(1, 3);

      const sentRequests = await friendService.getSentRequests(1);
      expect(sentRequests.length).toBe(1);
      expect(sentRequests[0].receiver.name).toBe('Charlie');
    });

    test('should return empty array when no sent requests', async () => {
      const sentRequests = await friendService.getSentRequests(3); // Charlie has not sent any PENDING requests
      expect(sentRequests.length).toBe(0);
    });
  });

  describe('acceptFriendRequest', () => {
    test('should accept a pending friend request successfully', async () => {
      
      await friendService.sendFriendRequest(3, 1);

      const updatedRequest = await friendService.acceptFriendRequest(1, 3);
      expect(updatedRequest).toBeDefined();
      expect(updatedRequest.status).toBe('ACCEPTED');

      //Db상태 확인 
      const request = await Friend.findOne({
        where: {
          requester_id: 3,
          receiver_id: 1,
        },
      });
      expect(request.status).toBe('ACCEPTED');
    });

    test('없는 요청수락', async () => {
      await expect(friendService.acceptFriendRequest(1, 999)).rejects.toThrow('Friend request not found');
    });
  });

  describe('rejectFriendRequest', () => {
    test('should reject a pending friend request successfully', async () => {
      
      await friendService.sendFriendRequest(2, 3);

    
      const result = await friendService.rejectFriendRequest(3, 2);
      expect(result).toBe(1);

      const request = await Friend.findOne({
        where: {
          requester_id: 2,
          receiver_id: 3,
        },
      });
      expect(request).toBeNull();
    });

    test('should throw error when rejecting non-existing friend request', async () => {
      await expect(friendService.rejectFriendRequest(1, 999)).rejects.toThrow('Friend request not found');
    });
  });

  describe('getFriendList', () => {
    test('should retrieve friend list with correct pagination', async () => {
      await friendService.sendFriendRequest(1, 2);
      await friendService.acceptFriendRequest(2, 1);
  
      await friendService.sendFriendRequest(1, 3);
      await friendService.acceptFriendRequest(3, 1); 
  
      // 추가 더미데이터 생성 
      for (let i = 4; i <= 23; i++) {
        // Create dummy users
        await User.create({
          id: i,
          name: `User${i}`,
          email: `user${i}@example.com`,
        });
  
        //Alice랑 친구맺기 
        await friendService.sendFriendRequest(1, i);
        await friendService.acceptFriendRequest(i, 1);
      }
  
      // Alice 친구: Bob (2), Charlie (3),User4 to User23 (20 friends)
      const limit = 5;
      const offset = 0;
      const friendsPage1 = await friendService.getFriendList(1, limit, offset);
      expect(friendsPage1.length).toBe(limit);
      expect(['Bob', 'Charlie', 'User4', 'User5', 'User6']).toContain(friendsPage1[0].friendInfo.name);
  
      const friendsPage2 = await friendService.getFriendList(1, limit, limit);
      expect(friendsPage2.length).toBe(limit);
      expect(['User7', 'User8', 'User9', 'User10', 'User11']).toContain(friendsPage2[0].friendInfo.name);
    });
  
    test('should return empty array when user has no friends', async () => {
      const friends = await friendService.getFriendList(999); // Non-existing user
      expect(friends.length).toBe(0);
    });
  });

  describe('deleteFriend', () => {
    test('should delete an existing friend relationship successfully', async () => {
    
      await friendService.sendFriendRequest(1, 2);
      await friendService.acceptFriendRequest(2, 1); 

    
      const result = await friendService.deleteFriend(1, 2);
      expect(result).toBe(1);

    
      const relationship = await Friend.findOne({
        where: {
          [Op.or]: [
            { requester_id: 1, receiver_id: 2 },
            { requester_id: 2, receiver_id: 1 },
          ],
          status: 'ACCEPTED',
        },
      });
      expect(relationship).toBeNull();
    });

    test('should throw error when deleting a non-existing friend relationship', async () => {
      await expect(friendService.deleteFriend(1, 999)).rejects.toThrow('Friend relationship not found');
    });
  });
});