import 'dart:convert';

import 'package:afterwork/data/models/event_model.dart';
import 'package:afterwork/domain/entities/event.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  group('EventModel', () {
    final tEventModel = EventModel(
      id: '1',
      title: 'After-work Tech',
      description: 'Soirée networking',
      startDate: '2026-01-15T19:00:00Z',
      location: 'Paris, France',
      category: 'Networking',
      link: 'https://event.com',
      imageUrl: 'https://event.com/image.jpg',
      creatorEmail: 'john@example.com',
      creatorFirstName: 'John',
      creatorLastName: 'Doe',
      profileImageUrl: 'https://example.com/profile.jpg',
      participants: ['user1', 'user2'],
      status: 'ouvert',
      reactionsCount: 10,
      commentsCount: 5,
      sharesCount: 3,
    );

    final tEventModelJson = {
      'id': '1',
      'title': 'After-work Tech',
      'description': 'Soirée networking',
      'startDate': '2026-01-15T19:00:00Z',
      'location': 'Paris, France',
      'category': 'Networking',
      'link': 'https://event.com',
      'imageUrl': 'https://event.com/image.jpg',
      'creatorEmail': 'john@example.com',
      'creatorFirstName': 'John',
      'creatorLastName': 'Doe',
      'profileImageUrl': 'https://example.com/profile.jpg',
      'participants': ['user1', 'user2'],
      'status': 'ouvert',
      'reactionsCount': 10,
      'commentsCount': 5,
      'sharesCount': 3,
    };

    test('should be a subclass of EventModel', () {
      // Assert
      expect(tEventModel, isA<EventModel>());
    });

    group('fromJson', () {
      test('should return a valid EventModel from JSON', () {
        // Act
        final result = EventModel.fromJson(tEventModelJson);

        // Assert
        expect(result.id, tEventModel.id);
        expect(result.title, tEventModel.title);
        expect(result.description, tEventModel.description);
        expect(result.startDate, tEventModel.startDate);
        expect(result.location, tEventModel.location);
        expect(result.category, tEventModel.category);
        expect(result.link, tEventModel.link);
        expect(result.imageUrl, tEventModel.imageUrl);
        expect(result.creatorEmail, tEventModel.creatorEmail);
        expect(result.creatorFirstName, tEventModel.creatorFirstName);
        expect(result.creatorLastName, tEventModel.creatorLastName);
        expect(result.status, tEventModel.status);
      });

      test('should use default values for missing fields', () {
        // Arrange
        final minimalJson = {
          'id': '1',
          'title': 'Event',
          'description': 'Description',
          'startDate': '2026-01-15T19:00:00Z',
          'location': 'Location',
          'category': 'Category',
          'link': 'Link',
          'creatorEmail': 'email@example.com',
          'creatorFirstName': 'John',
          'creatorLastName': 'Doe',
          'profileImageUrl': 'profile.jpg',
          'participants': [],
        };

        // Act
        final result = EventModel.fromJson(minimalJson);

        // Assert
        expect(result.imageUrl, isNull);
        expect(result.status, 'ouvert'); // Default
        expect(result.reactionsCount, 0); // Default
        expect(result.commentsCount, 0); // Default
        expect(result.sharesCount, 0); // Default
      });

      test('should handle null imageUrl', () {
        // Arrange
        final jsonWithNullImage = Map<String, dynamic>.from(tEventModelJson);
        jsonWithNullImage['imageUrl'] = null;

        // Act
        final result = EventModel.fromJson(jsonWithNullImage);

        // Assert
        expect(result.imageUrl, isNull);
      });
    });

    group('toJson', () {
      test('should return a JSON map containing proper data', () {
        // Act
        final result = tEventModel.toJson();

        // Assert
        expect(result, isA<Map<String, dynamic>>());
        expect(result['id'], '1');
        expect(result['title'], 'After-work Tech');
        expect(result['description'], 'Soirée networking');
        expect(result['startDate'], '2026-01-15T19:00:00Z');
        expect(result['location'], 'Paris, France');
        expect(result['category'], 'Networking');
        expect(result['link'], 'https://event.com');
        expect(result['imageUrl'], 'https://event.com/image.jpg');
        expect(result['creatorEmail'], 'john@example.com');
        expect(result['creatorFirstName'], 'John');
        expect(result['creatorLastName'], 'Doe');
        expect(result['status'], 'ouvert');
        expect(result['reactionsCount'], 10);
        expect(result['commentsCount'], 5);
        expect(result['sharesCount'], 3);
      });

      test('should be reversible (fromJson -> toJson)', () {
        // Act
        final json = tEventModel.toJson();
        final model = EventModel.fromJson(json);

        // Assert
        expect(model.id, tEventModel.id);
        expect(model.title, tEventModel.title);
        expect(model.status, tEventModel.status);
      });
    });

    group('toEntity', () {
      test('should convert EventModel to Event entity', () {
        // Act
        final entity = tEventModel.toEntity();

        // Assert
        expect(entity, isA<Event>());
        expect(entity.id, tEventModel.id);
        expect(entity.title, tEventModel.title);
        expect(entity.description, tEventModel.description);
        expect(entity.location, tEventModel.location);
        expect(entity.category, tEventModel.category);
        expect(entity.link, tEventModel.link);
        expect(entity.imageUrl, tEventModel.imageUrl);
        expect(entity.creatorEmail, tEventModel.creatorEmail);
        expect(entity.creatorFirstName, tEventModel.creatorFirstName);
        expect(entity.creatorLastName, tEventModel.creatorLastName);
        expect(entity.reactionsCount, tEventModel.reactionsCount);
        expect(entity.commentsCount, tEventModel.commentsCount);
        expect(entity.sharesCount, tEventModel.sharesCount);
      });

      test('should convert startDate string to DateTime', () {
        // Act
        final entity = tEventModel.toEntity();

        // Assert
        expect(entity.startDate, isA<DateTime>());
        expect(entity.startDate.year, 2026);
        expect(entity.startDate.month, 1);
        expect(entity.startDate.day, 15);
      });

      test('should convert status string to EventStatus enum', () {
        // Arrange
        final openModel = EventModel(
          id: '1',
          title: 'Event',
          description: 'Desc',
          startDate: '2026-01-15T19:00:00Z',
          location: 'Location',
          category: 'Category',
          link: 'Link',
          creatorEmail: 'email@example.com',
          creatorFirstName: 'John',
          creatorLastName: 'Doe',
          profileImageUrl: 'profile.jpg',
          participants: [],
          status: 'ouvert',
          reactionsCount: 0,
          commentsCount: 0,
          sharesCount: 0,
        );

        final closedModel = EventModel(
          id: '1',
          title: 'Event',
          description: 'Desc',
          startDate: '2026-01-15T19:00:00Z',
          location: 'Location',
          category: 'Category',
          link: 'Link',
          creatorEmail: 'email@example.com',
          creatorFirstName: 'John',
          creatorLastName: 'Doe',
          profileImageUrl: 'profile.jpg',
          participants: [],
          status: 'fermé',
          reactionsCount: 0,
          commentsCount: 0,
          sharesCount: 0,
        );

        // Act
        final openEntity = openModel.toEntity();
        final closedEntity = closedModel.toEntity();

        // Assert
        expect(openEntity.status, EventStatus.open);
        expect(closedEntity.status, EventStatus.closed);
      });

      test('should convert participants list to participantIds', () {
        // Act
        final entity = tEventModel.toEntity();

        // Assert
        expect(entity.participantIds, ['user1', 'user2']);
        expect(entity.participantsCount, 2);
      });
    });

    group('fromEntity', () {
      test('should convert Event entity to EventModel', () {
        // Arrange
        final entity = Event(
          id: '1',
          title: 'Event Title',
          description: 'Event Description',
          startDate: DateTime(2026, 1, 15, 19, 0),
          location: 'Paris',
          category: 'Tech',
          link: 'https://link.com',
          imageUrl: 'https://image.com',
          creatorEmail: 'creator@example.com',
          creatorFirstName: 'Jane',
          creatorLastName: 'Smith',
          creatorProfileImageUrl: 'https://profile.jpg',
          participantIds: const ['p1', 'p2', 'p3'],
          status: EventStatus.open,
          reactionsCount: 15,
          commentsCount: 8,
          sharesCount: 4,
        );

        // Act
        final model = EventModel.fromEntity(entity);

        // Assert
        expect(model.id, entity.id);
        expect(model.title, entity.title);
        expect(model.description, entity.description);
        expect(model.location, entity.location);
        expect(model.category, entity.category);
        expect(model.link, entity.link);
        expect(model.imageUrl, entity.imageUrl);
        expect(model.creatorEmail, entity.creatorEmail);
        expect(model.creatorFirstName, entity.creatorFirstName);
        expect(model.creatorLastName, entity.creatorLastName);
        expect(model.reactionsCount, entity.reactionsCount);
        expect(model.commentsCount, entity.commentsCount);
        expect(model.sharesCount, entity.sharesCount);
      });

      test('should convert DateTime to ISO8601 string', () {
        // Arrange
        final entity = Event(
          id: '1',
          title: 'Event',
          description: 'Desc',
          startDate: DateTime(2026, 1, 15, 19, 0),
          location: 'Location',
          category: 'Category',
          creatorEmail: 'email@example.com',
          creatorFirstName: 'John',
          creatorLastName: 'Doe',
          creatorProfileImageUrl: 'profile.jpg',
        );

        // Act
        final model = EventModel.fromEntity(entity);

        // Assert
        expect(model.startDate, contains('2026-01-15'));
        expect(model.startDate, contains('T'));
      });

      test('should convert EventStatus to API string', () {
        // Arrange
        final openEntity = Event(
          id: '1',
          title: 'Event',
          description: 'Desc',
          startDate: DateTime(2026, 1, 15),
          location: 'Location',
          category: 'Category',
          creatorEmail: 'email@example.com',
          creatorFirstName: 'John',
          creatorLastName: 'Doe',
          creatorProfileImageUrl: 'profile.jpg',
          status: EventStatus.open,
        );

        final closedEntity = openEntity.copyWith(status: EventStatus.closed);

        // Act
        final openModel = EventModel.fromEntity(openEntity);
        final closedModel = EventModel.fromEntity(closedEntity);

        // Assert
        expect(openModel.status, 'ouvert');
        expect(closedModel.status, 'fermé');
      });

      test('should handle null link', () {
        // Arrange
        final entity = Event(
          id: '1',
          title: 'Event',
          description: 'Desc',
          startDate: DateTime(2026, 1, 15),
          location: 'Location',
          category: 'Category',
          creatorEmail: 'email@example.com',
          creatorFirstName: 'John',
          creatorLastName: 'Doe',
          creatorProfileImageUrl: 'profile.jpg',
          link: null,
        );

        // Act
        final model = EventModel.fromEntity(entity);

        // Assert
        expect(model.link, ''); // Converted to empty string
      });
    });

    group('Entity-Model round trip', () {
      test('should maintain data integrity through conversions', () {
        // Arrange
        final originalEntity = Event(
          id: '123',
          title: 'Original Event',
          description: 'Original Description',
          startDate: DateTime(2026, 6, 15, 18, 30),
          location: 'Original Location',
          category: 'Original Category',
          link: 'https://original.com',
          imageUrl: 'https://original.com/image.jpg',
          creatorEmail: 'original@example.com',
          creatorFirstName: 'Original',
          creatorLastName: 'Creator',
          creatorProfileImageUrl: 'https://profile.jpg',
          participantIds: const ['p1', 'p2'],
          status: EventStatus.open,
          reactionsCount: 20,
          commentsCount: 10,
          sharesCount: 5,
        );

        // Act: Entity -> Model -> Entity
        final model = EventModel.fromEntity(originalEntity);
        final finalEntity = model.toEntity();

        // Assert
        expect(finalEntity.id, originalEntity.id);
        expect(finalEntity.title, originalEntity.title);
        expect(finalEntity.description, originalEntity.description);
        expect(finalEntity.location, originalEntity.location);
        expect(finalEntity.category, originalEntity.category);
        expect(finalEntity.status, originalEntity.status);
        expect(finalEntity.participantsCount, originalEntity.participantsCount);
      });
    });
  });
}

