import 'package:afterwork/domain/entities/friend.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  group('Friend Entity', () {
    const tFriendId = 'friend123';
    const tFirstName = 'Jane';
    const tLastName = 'Smith';
    const tEmail = 'jane.smith@example.com';
    const tImageUrl = 'https://example.com/jane.jpg';

    test('should create a Friend with required fields', () {
      // Act
      final friend = Friend(
        friendId: tFriendId,
        friendFirstName: tFirstName,
        friendLastName: tLastName,
      );

      // Assert
      expect(friend.friendId, tFriendId);
      expect(friend.friendFirstName, tFirstName);
      expect(friend.friendLastName, tLastName);
      expect(friend.status, FriendStatus.unknown); // Default
      expect(friend.isOnline, isFalse); // Default
      expect(friend.isBestFriend, isFalse); // Default
      expect(friend.hasKnownSinceChildhood, isFalse); // Default
    });

    test('should create a Friend with optional fields', () {
      // Act
      final friend = Friend(
        friendId: tFriendId,
        friendFirstName: tFirstName,
        friendLastName: tLastName,
        email: tEmail,
        imageUrl: tImageUrl,
        status: FriendStatus.accepted,
        isOnline: true,
        isBestFriend: true,
        hasKnownSinceChildhood: true,
      );

      // Assert
      expect(friend.email, tEmail);
      expect(friend.imageUrl, tImageUrl);
      expect(friend.status, FriendStatus.accepted);
      expect(friend.isOnline, isTrue);
      expect(friend.isBestFriend, isTrue);
      expect(friend.hasKnownSinceChildhood, isTrue);
    });

    test('should use default name when firstName is not provided', () {
      // Act
      final friend = Friend(
        friendId: tFriendId,
      );

      // Assert
      expect(friend.friendFirstName, 'Ami inconnu');
      expect(friend.friendLastName, '');
    });

    test('should support value equality using Equatable', () {
      // Arrange
      final friend1 = Friend(
        friendId: tFriendId,
        friendFirstName: tFirstName,
        friendLastName: tLastName,
      );

      final friend2 = Friend(
        friendId: tFriendId,
        friendFirstName: tFirstName,
        friendLastName: tLastName,
      );

      // Assert
      expect(friend1, equals(friend2));
      expect(friend1.hashCode, equals(friend2.hashCode));
    });

    test('fromJson should create Friend from valid JSON', () {
      // Arrange
      final json = {
        'friendId': tFriendId,
        'friendFirstName': tFirstName,
        'friendLastName': tLastName,
        'email': tEmail,
        'friendProfileImageUrl': tImageUrl,
        'status': 'accepted',
        'isOnline': true,
        'isBestFriend': false,
        'hasKnownSinceChildhood': false,
      };

      // Act
      final friend = Friend.fromJson(json);

      // Assert
      expect(friend.friendId, tFriendId);
      expect(friend.friendFirstName, tFirstName);
      expect(friend.friendLastName, tLastName);
      expect(friend.email, tEmail);
      expect(friend.imageUrl, tImageUrl);
      expect(friend.status, FriendStatus.accepted);
      expect(friend.isOnline, isTrue);
    });

    test('fromJson should use default values for missing fields', () {
      // Arrange
      final json = {
        'friendId': tFriendId,
      };

      // Act
      final friend = Friend.fromJson(json);

      // Assert
      expect(friend.friendId, tFriendId);
      expect(friend.friendFirstName, 'Ami inconnu');
      expect(friend.friendLastName, '');
      expect(friend.status, FriendStatus.unknown);
    });

    test('fromJson should throw when friendId is missing', () {
      // Arrange
      final json = <String, dynamic>{};

      // Act & Assert
      expect(
        () => Friend.fromJson(json),
        throwsA(isA<ArgumentError>()),
      );
    });

    test('toJson should serialize Friend correctly', () {
      // Arrange
      final friend = Friend(
        friendId: tFriendId,
        friendFirstName: tFirstName,
        friendLastName: tLastName,
        email: tEmail,
        imageUrl: tImageUrl,
        status: FriendStatus.accepted,
        isOnline: true,
        isBestFriend: false,
        hasKnownSinceChildhood: false,
      );

      // Act
      final json = friend.toJson();

      // Assert
      expect(json['friendId'], tFriendId);
      expect(json['friendFirstName'], tFirstName);
      expect(json['friendLastName'], tLastName);
      expect(json['email'], tEmail);
      expect(json['friendProfileImageUrl'], tImageUrl);
      expect(json['status'], 'accepted');
      expect(json['isOnline'], isTrue);
    });

    test('copyWith should create new instance with updated fields', () {
      // Arrange
      final original = Friend(
        friendId: tFriendId,
        friendFirstName: tFirstName,
        friendLastName: tLastName,
        status: FriendStatus.pending,
      );

      // Act
      final updated = original.copyWith(
        status: FriendStatus.accepted,
        isOnline: true,
      );

      // Assert
      expect(updated.friendId, original.friendId); // Unchanged
      expect(updated.friendFirstName, original.friendFirstName); // Unchanged
      expect(updated.status, FriendStatus.accepted); // Changed
      expect(updated.isOnline, isTrue); // Changed
    });
  });

  group('FriendStatus', () {
    test('should have all required status values', () {
      expect(FriendStatus.values.length, 4);
      expect(FriendStatus.values, contains(FriendStatus.pending));
      expect(FriendStatus.values, contains(FriendStatus.accepted));
      expect(FriendStatus.values, contains(FriendStatus.blocked));
      expect(FriendStatus.values, contains(FriendStatus.unknown));
    });

    test('parseStatus should handle different string formats', () {
      // This tests the private method indirectly through fromJson
      final jsonPending = {'friendId': '1', 'status': 'pending'};
      final jsonAccepted = {'friendId': '1', 'status': 'accepted'};
      final jsonBlocked = {'friendId': '1', 'status': 'blocked'};
      final jsonUnknown = {'friendId': '1', 'status': 'invalid'};

      expect(Friend.fromJson(jsonPending).status, FriendStatus.pending);
      expect(Friend.fromJson(jsonAccepted).status, FriendStatus.accepted);
      expect(Friend.fromJson(jsonBlocked).status, FriendStatus.blocked);
      expect(Friend.fromJson(jsonUnknown).status, FriendStatus.unknown);
    });
  });
}

