import 'package:comunic/helpers/serialization/base_serialization_helper.dart';
import 'package:comunic/models/conversation_member.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

import 'group.dart';

/// Conversation model
///
/// @author Pierre HUBERT

enum CallCapabilities { NONE, AUDIO, VIDEO }

class Conversation extends SerializableElement<Conversation> {
  final int id;
  final int lastActivity;
  final String name;
  final Color color;
  final String logoURL;
  final int groupID;
  final GroupMembershipLevel groupMinMembershipLevel;
  final List<ConversationMember> members;
  final bool canEveryoneAddMembers;
  final CallCapabilities callCapabilities;
  final bool isHavingCall;

  Conversation({
    @required this.id,
    @required this.lastActivity,
    @required this.name,
    @required this.color,
    @required this.logoURL,
    @required this.groupID,
    @required this.groupMinMembershipLevel,
    @required this.members,
    @required this.canEveryoneAddMembers,
    this.callCapabilities = CallCapabilities.NONE,
    this.isHavingCall = false,
  })  : assert(id != null),
        assert(lastActivity != null),
        assert(members != null),
        assert(canEveryoneAddMembers != null),
        assert((groupID == null) == (groupMinMembershipLevel == null)),
        assert(callCapabilities != null),
        assert(isHavingCall != null);

  /// Check out whether a conversation has a fixed name or not
  bool get hasName => this.name != null;

  /// Get current user membership
  ConversationMember get membership =>
      members.firstWhere((m) => m.userID == userID());

  /// Check out whether current user of the application is an admin
  bool get isAdmin => membership.isAdmin;

  /// Check if current user is the last admin of the conversation
  bool get isLastAdmin => isAdmin && adminsID.length == 1;

  /// Check it current user is following the conversation or not
  bool get following => membership.following;

  /// Get the list of members in the conversation
  Set<int> get membersID => members.map((e) => e.userID).toSet();

  /// Get the list of admins in the conversation
  Set<int> get adminsID =>
      members.where((e) => e.isAdmin).map((e) => e.userID).toSet();

  /// Get the list of the OTHER members of the conversation (all except current user)
  Set<int> get otherMembersID => membersID..remove(userID());

  /// Check if the last message has been seen or not
  bool get sawLastMessage => lastActivity <= membership.lastAccessTime;

  /// Check out whether a conversation is managed or not
  bool get isManaged => isGroupConversation;

  bool get isGroupConversation => groupID != null;

  bool get hasLogo => logoURL != null;

  Conversation.fromJson(Map<String, dynamic> map)
      : id = map["id"],
        name = map["name"],
        color = map["color"] == null ? null : Color(map["color"]),
        logoURL = map["logoURL"],
        groupID = map["groupID"],
        groupMinMembershipLevel = GroupMembershipLevel.values.firstWhere(
            (element) => element.toString() == map["groupMinMembershipLevel"],
            orElse: () => null),
        lastActivity = map["lastActivity"],
        members = map["members"]
            .map((el) => ConversationMember.fromJSON(el))
            .toList()
            .cast<ConversationMember>(),
        canEveryoneAddMembers = map["canEveryoneAddMembers"],

        // By default, we can not do any call
        callCapabilities = CallCapabilities.NONE,
        isHavingCall = false;

  Map<String, dynamic> toJson() {
    return {
      "id": id,
      "name": name,
      "color": color?.value,
      "logoURL": logoURL,
      "groupID": groupID,
      "groupMinMembershipLevel": groupMinMembershipLevel?.toString(),
      "lastActivity": lastActivity,
      "members": members.map((e) => e.toJson()).toList(),
      "canEveryoneAddMembers": canEveryoneAddMembers,
    };
  }

  @override
  int compareTo(Conversation other) {
    return other.lastActivity.compareTo(this.lastActivity);
  }
}