using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.ObjectModel;
using LeanCloud.Storage;

namespace LeanCloud.Realtime {
    /// <summary>
    /// 普通对话
    /// </summary>
    public class LCIMConversation {
        /// <summary>
        /// 对话 Id
        /// </summary>
        public string Id {
            get; internal set;
        }

        /// <summary>
        /// 是否唯一
        /// </summary>
        public bool Unique {
            get; internal set;
        }

        /// <summary>
        /// 唯一 Id
        /// </summary>
        public string UniqueId {
            get; internal set;
        }

        /// <summary>
        /// 对话名称
        /// </summary>
        public string Name {
            get {
                return this["name"] as string;
            }
            internal set {
                this["name"] = value;
            }
        }

        /// <summary>
        /// 创建者 Id
        /// </summary>
        public string CreatorId {
            get; set;
        }

        /// <summary>
        /// 成员 Id
        /// </summary>
        public ReadOnlyCollection<string> MemberIds {
            get {
                return new ReadOnlyCollection<string>(ids.ToList());
            }
        }

        /// <summary>
        /// 静音成员 Id
        /// </summary>
        public ReadOnlyCollection<string> MutedMemberIds {
            get {
                return new ReadOnlyCollection<string>(mutedIds.ToList());
            }
        }

        /// <summary>
        /// 未读消息数量
        /// </summary>
        public int Unread {
            get; internal set;
        }

        /// <summary>
        /// 最新的一条消息
        /// </summary>
        public LCIMMessage LastMessage {
            get; internal set;
        }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreatedAt {
            get; internal set;
        }

        /// <summary>
        /// 更新时间
        /// </summary>
        public DateTime UpdatedAt {
            get; internal set;
        }

        /// <summary>
        /// 最新送达消息时间戳
        /// </summary>
        public long LastDeliveredTimestamp {
            get; internal set;
        }

        /// <summary>
        /// 最新送达消息时间
        /// </summary>
        public DateTime LastDeliveredAt {
            get {
                DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(LastDeliveredTimestamp);
                return dateTimeOffset.DateTime;
            }
        }

        /// <summary>
        /// 最新已读消息时间戳
        /// </summary>
        public long LastReadTimestamp {
            get; internal set;
        }

        /// <summary>
        /// 最新已读消息时间
        /// </summary>
        public DateTime LastReadAt {
            get {
                DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(LastReadTimestamp);
                return dateTimeOffset.DateTime;
            }
        }

        /// <summary>
        /// 设置/获取对话属性
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public object this[string key] {
            get {
                return customProperties[key];
            }
            set {
                customProperties[key] = value;
            }
        }

        /// <summary>
        /// 是否已静音
        /// </summary>
        public bool IsMute {
            get; private set;
        }

        protected LCIMClient Client {
            get; private set;
        }

        private readonly Dictionary<string, object> customProperties;

        internal HashSet<string> ids;

        internal HashSet<string> mutedIds;

        internal LCIMConversation(LCIMClient client) {
            Client = client;
            customProperties = new Dictionary<string, object>();
            ids = new HashSet<string>();
            mutedIds = new HashSet<string>();
            customProperties = new Dictionary<string, object>();
        }

        /// <summary>
        /// 获取对话人数，或暂态对话的在线人数
        /// </summary>
        /// <returns></returns>
        public async Task<int> GetMembersCount() {
            return await Client.ConversationController.GetMembersCount(Id);
        }

        /// <summary>
        /// 将该会话标记为已读
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task Read() {
            if (LastMessage == null) {
                return;
            }
            await Client.MessageController.Read(Id, LastMessage);
            Unread = 0;
        }

        /// <summary>
        /// 修改对话属性
        /// </summary>
        /// <param name="attributes"></param>
        /// <returns></returns>
        public async Task UpdateInfo(Dictionary<string, object> attributes) {
            if (attributes == null || attributes.Count == 0) {
                throw new ArgumentNullException(nameof(attributes));
            }
            Dictionary<string, object> updatedAttr = await Client.ConversationController.UpdateInfo(Id, attributes);
            if (updatedAttr != null) {
                MergeInfo(updatedAttr);
            }
        }

        /// <summary>
        /// 添加用户到对话
        /// </summary>
        /// <param name="clientIds">用户 Id</param>
        /// <returns></returns>
        public virtual async Task<LCIMPartiallySuccessResult> AddMembers(IEnumerable<string> clientIds) {
            if (clientIds == null || clientIds.Count() == 0) {
                throw new ArgumentNullException(nameof(clientIds));
            }
            LCIMPartiallySuccessResult result = await Client.ConversationController.AddMembers(Id, clientIds);
            ids.UnionWith(result.SuccessfulClientIdList);
            return result;
        }

        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="removeIds">用户 Id</param>
        /// <returns></returns>
        public async Task<LCIMPartiallySuccessResult> RemoveMembers(IEnumerable<string> removeIds) {
            if (removeIds == null || removeIds.Count() == 0) {
                throw new ArgumentNullException(nameof(removeIds));
            }
            LCIMPartiallySuccessResult result = await Client.ConversationController.RemoveMembers(Id, removeIds);
            ids.RemoveWhere(id => result.SuccessfulClientIdList.Contains(id));
            return result;
        }

        /// <summary>
        /// 加入对话
        /// </summary>
        /// <returns></returns>
        public async Task Join() {
            LCIMPartiallySuccessResult result = await Client.ConversationController.AddMembers(Id,
                new string[] { Client.Id });
            if (result.IsSuccess) {
                ids.UnionWith(result.SuccessfulClientIdList);
            } else {
                LCIMOperationFailure error = result.FailureList[0];
                throw new LCException(error.Code, error.Reason);
            }
        }

        /// <summary>
        /// 离开对话
        /// </summary>
        /// <returns></returns>
        public async Task Quit() {
            LCIMPartiallySuccessResult result = await RemoveMembers(new string[] { Client.Id });
            if (!result.IsSuccess) {
                LCIMOperationFailure error = result.FailureList[0];
                throw new LCException(error.Code, error.Reason);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task<LCIMMessage> Send(LCIMMessage message,
            LCIMMessageSendOptions options = null) {
            if (message == null) {
                throw new ArgumentNullException(nameof(message));
            }
            if (options == null) {
                options = LCIMMessageSendOptions.Default;
            }
            await Client.MessageController.Send(Id, message, options);
            LastMessage = message;
            return message;
        }

        /// <summary>
        /// 静音
        /// </summary>
        /// <returns></returns>
        public async Task Mute() {
            await Client.ConversationController.Mute(Id);
            IsMute = true;
        }

        /// <summary>
        /// 取消静音
        /// </summary>
        /// <returns></returns>
        public async Task Unmute() {
            await Client.ConversationController.Unmute(Id);
            IsMute = false;
        }

        /// <summary>
        /// 禁言
        /// </summary>
        /// <param name="clientIds"></param>
        /// <returns></returns>
        public async Task<LCIMPartiallySuccessResult> MuteMembers(IEnumerable<string> clientIds) {
            if (clientIds == null || clientIds.Count() == 0) {
                throw new ArgumentNullException(nameof(clientIds));
            }
            LCIMPartiallySuccessResult result = await Client.ConversationController.MuteMembers(Id, clientIds);
            if (result.SuccessfulClientIdList != null) {
                mutedIds.UnionWith(result.SuccessfulClientIdList);
            }
            return result;
        }

        /// <summary>
        /// 取消禁言
        /// </summary>
        /// <param name="clientIdList"></param>
        /// <returns></returns>
        public async Task<LCIMPartiallySuccessResult> UnmuteMembers(IEnumerable<string> clientIds) {
            if (clientIds == null || clientIds.Count() == 0) {
                throw new ArgumentNullException(nameof(clientIds));
            }
            LCIMPartiallySuccessResult result = await Client.ConversationController.UnmuteMembers(Id, clientIds);
            if (result.SuccessfulClientIdList != null) {
                mutedIds.RemoveWhere(id => result.SuccessfulClientIdList.Contains(id));
            }
            return result;
        }

        /// <summary>
        /// 将用户加入黑名单
        /// </summary>
        /// <param name="clientIds"></param>
        /// <returns></returns>
        public async Task<LCIMPartiallySuccessResult> BlockMembers(IEnumerable<string> clientIds) {
            if (clientIds == null || clientIds.Count() == 0) {
                throw new ArgumentNullException(nameof(clientIds));
            }
            return await Client.ConversationController.BlockMembers(Id, clientIds);
        }

        /// <summary>
        /// 将用户移除黑名单
        /// </summary>
        /// <param name="clientIds"></param>
        /// <returns></returns>
        public async Task<LCIMPartiallySuccessResult> UnblockMembers(IEnumerable<string> clientIds) {
            if (clientIds == null || clientIds.Count() == 0) {
                throw new ArgumentNullException(nameof(clientIds));
            }
            return await Client.ConversationController.UnblockMembers(Id, clientIds);
        }

        /// <summary>
        /// 撤回消息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task RecallMessage(LCIMMessage message) {
            if (message == null) {
                throw new ArgumentNullException(nameof(message));
            }
            await Client.MessageController.RecallMessage(Id, message);
        }

        /// <summary>
        /// 修改消息
        /// </summary>
        /// <param name="oldMessage"></param>
        /// <param name="newMessage"></param>
        /// <returns></returns>
        public async Task UpdateMessage(LCIMMessage oldMessage, LCIMMessage newMessage) {
            if (oldMessage == null) {
                throw new ArgumentNullException(nameof(oldMessage));
            }
            if (newMessage == null) {
                throw new ArgumentNullException(nameof(newMessage));
            }
            await Client.MessageController.UpdateMessage(Id, oldMessage, newMessage);
        }

        /// <summary>
        /// 更新对话中成员的角色
        /// </summary>
        /// <param name="memberId"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        public async Task UpdateMemberRole(string memberId, string role) {
            if (string.IsNullOrEmpty(memberId)) {
                throw new ArgumentNullException(nameof(memberId));
            }
            if (role != LCIMConversationMemberInfo.Manager && role != LCIMConversationMemberInfo.Member) {
                throw new ArgumentException("role MUST be Manager Or Memebr");
            }
            await Client.ConversationController.UpdateMemberRole(Id, memberId, role);
        }

        /// <summary>
        /// 获取对话中成员的角色（只返回管理员）
        /// </summary>
        /// <returns></returns>
        public async Task<ReadOnlyCollection<LCIMConversationMemberInfo>> GetAllMemberInfo() {
            return await Client.ConversationController.GetAllMemberInfo(Id);
        }

        /// <summary>
        /// 获取对话中指定成员的角色
        /// </summary>
        /// <param name="memberId"></param>
        /// <returns></returns>
        public async Task<LCIMConversationMemberInfo> GetMemberInfo(string memberId) {
            if (string.IsNullOrEmpty(memberId)) {
                throw new ArgumentNullException(nameof(memberId));
            }
            ReadOnlyCollection<LCIMConversationMemberInfo> members = await GetAllMemberInfo();
            foreach (LCIMConversationMemberInfo member in members) {
                if (member.MemberId == memberId) {
                    return member;
                }
            }
            return null;
        }

        /// <summary>
        /// 查询禁言用户
        /// </summary>
        /// <param name="limit"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task<LCIMPageResult> QueryMutedMembers(int limit = 10,
            string next = null) {
            return await Client.ConversationController.QueryMutedMembers(Id, limit, next);
        }

        /// <summary>
        /// 查询黑名单用户
        /// </summary>
        /// <param name="limit">限制</param>
        /// <param name="next">其实用户 Id</param>
        /// <returns></returns>
        public async Task<LCIMPageResult> QueryBlockedMembers(int limit = 10,
            string next = null) {
            return await Client.ConversationController.QueryBlockedMembers(Id, limit, next);
        }

        /// <summary>
        /// 查询聊天记录
        /// </summary>
        /// <param name="start">起点</param>
        /// <param name="end">终点</param>
        /// <param name="direction">查找方向</param>
        /// <param name="limit">限制</param>
        /// <param name="messageType">消息类型</param>
        /// <returns></returns>
        public async Task<ReadOnlyCollection<LCIMMessage>> QueryMessages(LCIMMessageQueryEndpoint start = null,
            LCIMMessageQueryEndpoint end = null,
            LCIMMessageQueryDirection direction = LCIMMessageQueryDirection.NewToOld,
            int limit = 20,
            int messageType = 0) {
            return await Client.MessageController.QueryMessages(Id, start, end, direction, limit, messageType);
        }

        /// <summary>
        /// 获取会话已收/已读时间戳
        /// </summary>
        /// <returns></returns>
        public async Task FetchReciptTimestamps() {
            await Client.ConversationController.FetchReciptTimestamp(Id);
        }

        internal static bool IsTemporayConversation(string convId) {
            return convId.StartsWith("_tmp:");
        }

        internal void MergeFrom(Dictionary<string, object> conv) {
            if (conv.TryGetValue("objectId", out object idObj)) {
                Id = idObj as string;
            }
            if (conv.TryGetValue("unique", out object uniqueObj)) {
                Unique = (bool)uniqueObj;
            }
            if (conv.TryGetValue("uniqueId", out object uniqueIdObj)) {
                UniqueId = uniqueIdObj as string;
            }
            if (conv.TryGetValue("createdAt", out object createdAtObj)) {
                CreatedAt = DateTime.Parse(createdAtObj.ToString());
            }
            if (conv.TryGetValue("updatedAt", out object updatedAtObj)) {
                UpdatedAt = DateTime.Parse(updatedAtObj.ToString());
            }
            if (conv.TryGetValue("c", out object co)) {
                CreatorId = co as string;
            }
            if (conv.TryGetValue("m", out object mo)) {
                IEnumerable<string> ids = (mo as IList<object>).Cast<string>();
                this.ids = new HashSet<string>(ids);
            }
            if (conv.TryGetValue("mu", out object muo)) {
                IEnumerable<string> ids = (muo as IList<object>).Cast<string>();
                mutedIds = new HashSet<string>(ids);
            }
            //if (conv.TryGetValue("lm", out object lmo)) {
            //    LastMessageAt = (DateTime)LCDecoder.Decode(lmo);
            //}
        }

        internal void MergeInfo(Dictionary<string, object> attr) {
            if (attr == null || attr.Count == 0) {
                return;
            }
            foreach (KeyValuePair<string, object> kv in attr) {
                customProperties[kv.Key] = kv.Value;
            }
        }
    }
}
