import '../../core/constants/env_config.dart';
import '../../core/errors/exceptions.dart';
import '../../core/utils/app_logger.dart';
import '../../domain/entities/event.dart';

/// Modèle de données pour les événements (Data Transfer Object).
///
/// Cette classe est responsable de la sérialisation/désérialisation
/// avec l'API backend et convertit vers/depuis l'entité de domaine [Event].
///
/// **Usage:**
/// ```dart
/// // Depuis JSON
/// final event = EventModel.fromJson(jsonData);
///
/// // Vers JSON
/// final json = event.toJson();
///
/// // Vers entité de domaine
/// final entity = event.toEntity();
/// ```
class EventModel {
  /// Crée une nouvelle instance de [EventModel].
  ///
  /// [id] L'identifiant unique de l'événement
  /// [title] Le titre de l'événement
  /// [description] La description de l'événement
  /// [startDate] La date de début (format ISO 8601 string)
  /// [location] Le lieu de l'événement
  /// [category] La catégorie de l'événement
  /// [link] Le lien associé (optionnel)
  /// [imageUrl] L'URL de l'image (optionnel)
  /// [creatorEmail] L'email du créateur
  /// [creatorFirstName] Le prénom du créateur
  /// [creatorLastName] Le nom du créateur
  /// [profileImageUrl] L'URL de l'image de profil du créateur
  /// [participants] La liste des participants (IDs ou objets)
  /// [status] Le statut de l'événement ('ouvert', 'fermé', 'annulé')
  /// [reactionsCount] Le nombre de réactions
  /// [commentsCount] Le nombre de commentaires
  /// [sharesCount] Le nombre de partages
  EventModel({
    required this.id,
    required this.title,
    required this.description,
    required this.startDate,
    required this.location,
    required this.category,
    required this.link,
    required this.creatorEmail,
    required this.creatorFirstName,
    required this.creatorLastName,
    required this.profileImageUrl,
    required this.participants,
    required this.status,
    required this.reactionsCount,
    required this.commentsCount,
    required this.sharesCount,
    this.imageUrl,
  });

  /// L'identifiant unique de l'événement
  final String id;

  /// Le titre de l'événement
  final String title;

  /// La description de l'événement
  final String description;

  /// La date de début (format ISO 8601 string)
  final String startDate;

  /// Le lieu de l'événement
  final String location;

  /// La catégorie de l'événement
  final String category;

  /// Le lien associé à l'événement
  final String link;

  /// L'URL de l'image de l'événement (optionnel)
  final String? imageUrl;

  /// L'email du créateur de l'événement
  final String creatorEmail;

  /// Le prénom du créateur
  final String creatorFirstName;

  /// Le nom du créateur
  final String creatorLastName;

  /// L'URL de l'image de profil du créateur
  final String profileImageUrl;

  /// La liste des participants (peut contenir des IDs ou des objets)
  final List<dynamic> participants;

  /// Le statut de l'événement ('ouvert', 'fermé', 'annulé')
  String status;

  /// Le nombre de réactions
  final int reactionsCount;

  /// Le nombre de commentaires
  final int commentsCount;

  /// Le nombre de partages
  final int sharesCount;

  // ============================================================================
  // FACTORY METHODS
  // ============================================================================

  /// Crée un [EventModel] à partir d'un JSON reçu depuis l'API.
  ///
  /// [json] Les données JSON à parser
  ///
  /// Returns un [EventModel] avec les données parsées
  ///
  /// Throws [ValidationException] si les données essentielles sont manquantes
  ///
  /// **Exemple:**
  /// ```dart
  /// final json = {
  ///   'id': '123',
  ///   'title': 'Concert',
  ///   'startDate': '2026-01-10T20:00:00Z',
  ///   ...
  /// };
  /// final event = EventModel.fromJson(json);
  /// ```
  factory EventModel.fromJson(Map<String, dynamic> json) {
    try {
      // Validation des champs essentiels
      if (json['id'] == null || json['id'].toString().isEmpty) {
        throw ValidationException(
          'L\'ID de l\'événement est requis',
          field: 'id',
        );
      }

      if (json['title'] == null || json['title'].toString().isEmpty) {
        throw ValidationException(
          'Le titre de l\'événement est requis',
          field: 'title',
        );
      }

      if (json['startDate'] == null || json['startDate'].toString().isEmpty) {
        throw ValidationException(
          'La date de début est requise',
          field: 'startDate',
        );
      }

      // Parsing avec valeurs par défaut pour les champs optionnels
      final model = EventModel(
        id: json['id'].toString(),
        title: json['title'].toString(),
        description: json['description']?.toString() ?? '',
        startDate: json['startDate'].toString(),
        location: json['location']?.toString() ?? '',
        category: json['category']?.toString() ?? 'Autre',
        link: json['link']?.toString() ?? '',
        imageUrl: json['imageUrl']?.toString(),
        creatorEmail: json['creatorEmail']?.toString() ?? '',
        creatorFirstName: json['creatorFirstName']?.toString() ?? '',
        creatorLastName: json['creatorLastName']?.toString() ?? '',
        profileImageUrl: json['profileImageUrl']?.toString() ?? '',
        participants: json['participants'] is List
            ? json['participants'] as List<dynamic>
            : [],
        status: json['status']?.toString() ?? 'ouvert',
        reactionsCount: _parseInt(json, 'reactionsCount') ?? 0,
        commentsCount: _parseInt(json, 'commentsCount') ?? 0,
        sharesCount: _parseInt(json, 'sharesCount') ?? 0,
      );

      if (EnvConfig.enableDetailedLogs) {
        _logEventParsed(model);
      }

      return model;
    } catch (e, stackTrace) {
      if (e is ValidationException) rethrow;
      AppLogger.e('Erreur lors du parsing JSON', error: e, stackTrace: stackTrace, tag: 'EventModel');
      throw ValidationException(
        'Erreur lors du parsing de l\'événement: $e',
      );
    }
  }

  /// Parse une valeur int depuis le JSON.
  static int? _parseInt(Map<String, dynamic> json, String key) {
    final value = json[key];
    if (value == null) return null;
    if (value is int) return value;
    if (value is String) {
      return int.tryParse(value);
    }
    if (value is double) {
      return value.toInt();
    }
    return null;
  }

  /// Log les détails d'un événement parsé (uniquement en mode debug).
  static void _logEventParsed(EventModel event) {
    AppLogger.d('Événement parsé: ID=${event.id}, Titre=${event.title}, Date=${event.startDate}, Localisation=${event.location}, Statut=${event.status}, Participants=${event.participants.length}', tag: 'EventModel');
  }

  /// Crée un [EventModel] depuis une entité de domaine [Event].
  ///
  /// [event] L'entité de domaine à convertir
  ///
  /// Returns un [EventModel] avec les données de l'entité
  ///
  /// **Exemple:**
  /// ```dart
  /// final entity = Event(...);
  /// final model = EventModel.fromEntity(entity);
  /// ```
  factory EventModel.fromEntity(Event event) {
    return EventModel(
      id: event.id,
      title: event.title,
      description: event.description,
      startDate: event.startDate.toIso8601String(),
      location: event.location,
      category: event.category,
      link: event.link ?? '',
      imageUrl: event.imageUrl,
      creatorEmail: event.creatorEmail,
      creatorFirstName: event.creatorFirstName,
      creatorLastName: event.creatorLastName,
      profileImageUrl: event.creatorProfileImageUrl,
      participants: event.participantIds,
      status: event.status.toApiString(),
      reactionsCount: event.reactionsCount,
      commentsCount: event.commentsCount,
      sharesCount: event.sharesCount,
    );
  }

  // ============================================================================
  // CONVERSION METHODS
  // ============================================================================

  /// Convertit ce [EventModel] en JSON pour l'envoi vers l'API.
  ///
  /// Returns une [Map] contenant les données de l'événement
  ///
  /// **Exemple:**
  /// ```dart
  /// final event = EventModel(...);
  /// final json = event.toJson();
  /// // Envoyer json à l'API
  /// ```
  Map<String, dynamic> toJson() {
    final json = <String, dynamic>{
      'id': id,
      'title': title,
      'description': description,
      'startDate': startDate,
      'location': location,
      'category': category,
      'link': link,
      if (imageUrl != null && imageUrl!.isNotEmpty) 'imageUrl': imageUrl,
      'creatorEmail': creatorEmail,
      'creatorFirstName': creatorFirstName,
      'creatorLastName': creatorLastName,
      if (profileImageUrl.isNotEmpty) 'profileImageUrl': profileImageUrl,
      'participants': participants,
      'status': status,
      'reactionsCount': reactionsCount,
      'commentsCount': commentsCount,
      'sharesCount': sharesCount,
    };

    AppLogger.d('Conversion en JSON pour l\'événement: $id', tag: 'EventModel');

    return json;
  }

  /// Convertit ce modèle vers une entité de domaine [Event].
  ///
  /// Returns une instance de [Event] avec les mêmes données
  ///
  /// Throws [ValidationException] si la date ne peut pas être parsée
  ///
  /// **Exemple:**
  /// ```dart
  /// final model = EventModel.fromJson(json);
  /// final entity = model.toEntity();
  /// ```
  Event toEntity() {
    DateTime parsedDate;
    try {
      parsedDate = DateTime.parse(startDate);
    } catch (e) {
      throw ValidationException(
        'Format de date invalide: $startDate',
        field: 'startDate',
      );
    }

    // Convertir les participants en liste de strings
    final participantIds = participants.map((p) {
      if (p is Map) {
        return p['id']?.toString() ?? p['userId']?.toString() ?? '';
      }
      return p.toString();
    }).where((id) => id.isNotEmpty).toList();

    return Event(
      id: id,
      title: title,
      description: description,
      startDate: parsedDate,
      location: location,
      category: category,
      link: link.isEmpty ? null : link,
      imageUrl: imageUrl,
      creatorEmail: creatorEmail,
      creatorFirstName: creatorFirstName,
      creatorLastName: creatorLastName,
      creatorProfileImageUrl: profileImageUrl,
      participantIds: participantIds,
      status: EventStatus.fromString(status),
      reactionsCount: reactionsCount,
      commentsCount: commentsCount,
      sharesCount: sharesCount,
    );
  }

  // ============================================================================
  // UTILITY METHODS
  // ============================================================================

  /// Crée une copie de ce [EventModel] avec des valeurs modifiées.
  ///
  /// Tous les paramètres sont optionnels. Seuls les paramètres fournis
  /// seront modifiés dans la nouvelle instance.
  ///
  /// **Exemple:**
  /// ```dart
  /// final updated = event.copyWith(
  ///   title: 'Nouveau titre',
  ///   status: 'fermé',
  /// );
  /// ```
  EventModel copyWith({
    String? id,
    String? title,
    String? description,
    String? startDate,
    String? location,
    String? category,
    String? link,
    String? imageUrl,
    String? creatorEmail,
    String? creatorFirstName,
    String? creatorLastName,
    String? profileImageUrl,
    List<dynamic>? participants,
    String? status,
    int? reactionsCount,
    int? commentsCount,
    int? sharesCount,
  }) {
    return EventModel(
      id: id ?? this.id,
      title: title ?? this.title,
      description: description ?? this.description,
      startDate: startDate ?? this.startDate,
      location: location ?? this.location,
      category: category ?? this.category,
      link: link ?? this.link,
      imageUrl: imageUrl ?? this.imageUrl,
      creatorEmail: creatorEmail ?? this.creatorEmail,
      creatorFirstName: creatorFirstName ?? this.creatorFirstName,
      creatorLastName: creatorLastName ?? this.creatorLastName,
      profileImageUrl: profileImageUrl ?? this.profileImageUrl,
      participants: participants ?? this.participants,
      status: status ?? this.status,
      reactionsCount: reactionsCount ?? this.reactionsCount,
      commentsCount: commentsCount ?? this.commentsCount,
      sharesCount: sharesCount ?? this.sharesCount,
    );
  }

  /// Retourne le nombre de participants.
  ///
  /// Returns le nombre de participants dans la liste
  int get participantsCount => participants.length;

  /// Vérifie si l'événement est ouvert.
  ///
  /// Returns `true` si le statut est 'ouvert', `false` sinon
  bool get isOpen => status.toLowerCase() == 'ouvert';

  /// Vérifie si l'événement est fermé.
  ///
  /// Returns `true` si le statut est 'fermé', `false` sinon
  bool get isClosed => status.toLowerCase() == 'fermé';

  /// Vérifie si l'événement est annulé.
  ///
  /// Returns `true` si le statut est 'annulé', `false` sinon
  bool get isCancelled => status.toLowerCase() == 'annulé';

  @override
  String toString() {
    return 'EventModel('
        'id: $id, '
        'title: $title, '
        'startDate: $startDate, '
        'status: $status'
        ')';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is EventModel && other.id == id;
  }

  @override
  int get hashCode => id.hashCode;
}
