using Common.Dtos.Conversation;
using Common.Enums;
using Hushian.Application.Contracts.Persistence;
using Hushian.Application.Models;
using Hushian.Domain.Entites;
using Hushian.WebApi;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hushian.Application.Services
{
    public class ChatService
    {
        private readonly IGenericRepository<Conversation> _ConversationRepository;
        private readonly IGenericRepository<ConversationResponse> _ConversationResponseRepository;
        private readonly CompanyService _companyService;
        private readonly UserService _userService;
        private readonly GroupService _groupService;
        private readonly ExperService _experService;
        private readonly IHubContext<ChatNotificationHub> _hubContext;

        public ChatService(IGenericRepository<Conversation> conversationRepository, IGenericRepository<ConversationResponse> conversationResponseRepository, CompanyService companyService, UserService userService, GroupService groupService, ExperService experService, IHubContext<ChatNotificationHub> hubContext)
        {
            _ConversationRepository = conversationRepository;
            _ConversationResponseRepository = conversationResponseRepository;
            _companyService = companyService;
            _userService = userService;
            _groupService = groupService;
            _experService = experService;
            _hubContext = hubContext;
        }

        public async Task<List<ChatItemDto>> GeChatsByExperID(int ExperID, ConversationStatus status)
     => await _ConversationRepository.Get()
                .Include(inc => inc.Group)
                .Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
            .Where(w => w.ConversationResponses.Any(a => a.ExperID == ExperID) && w.Status == status)
                .Select(s => new ChatItemDto()
                {
                    ID = s.ID,
                    ExperID = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).ExperID,
                    ExperFullName = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).Exper.FullName,
                    GroupID = s.GroupID,
                    GroupName = s.Group.Name,
                    LastText = s.ConversationResponses.OrderBy(o => o.ID).Last().Text,
                    LastMsgdate = s.Cdatetime.GetDatePersian(),
                    LastMsgtime = s.Cdatetime.GetTime(),
                    LastMsgType = s.ConversationResponses.OrderBy(o => o.ID).Last().Type,
                    status = s.Status,
                    UserID = s.UserID,
                    UserFullName = string.IsNullOrEmpty(s.User.FullName) ? s.User.Mobile : s.User.FullName,
                    Responses = s.ConversationResponses.Select(ss => new ChatItemResponseDto()
                    {
                        ChatItemID = ss.ConversationID,
                        ExperID = ss.ExperID,
                        ExperName = ss.Exper.FullName,
                        FileContent = ss.FileContent,
                        FileName = ss.FileName,
                        FileType = ss.FileType,
                        ID = ss.ID,
                        IsRead = ss.IsRead,
                        text = ss.Text,
                        Type = ss.Type
                    }).ToList()

                }).ToListAsync();
        public async Task<List<ChatItemDto>> GetChatsByCompanyID(int CompanyID, ConversationStatus status)
      => await _ConversationRepository.Get()
                .Include(inc => inc.Group)
                .Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
            .Where(w => w.CompanyID == CompanyID && w.Status == status)
                .Select(s => new ChatItemDto()
                {
                    ID = s.ID,
                    ExperID = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).ExperID,
                    ExperFullName = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).Exper.FullName,
                    GroupID = s.GroupID,
                    GroupName = s.Group.Name,
                    LastText = s.ConversationResponses.OrderBy(o => o.ID).Last().Text,
                    LastMsgdate = s.Cdatetime.GetDatePersian(),
                    LastMsgtime = s.Cdatetime.GetTime(),
                    LastMsgType = s.ConversationResponses.OrderBy(o => o.ID).Last().Type,
                    status = s.Status,
                    UserID = s.UserID,
                    UserFullName = string.IsNullOrEmpty(s.User.FullName) ? s.User.Mobile : s.User.FullName,
                    Responses = s.ConversationResponses.Select(ss => new ChatItemResponseDto()
                    {
                        ChatItemID = ss.ConversationID,
                        ExperID = ss.ExperID,
                        ExperName = ss.Exper.FullName,
                        FileContent = ss.FileContent,
                        FileName = ss.FileName,
                        FileType = ss.FileType,
                        ID = ss.ID,
                        IsRead = ss.IsRead,
                        text = ss.Text,
                        Type = ss.Type
                    }).ToList()

                }).ToListAsync();
        public async Task<List<ChatItemDto>> ChatsAwaitingOurResponse(int CompanyID, int? ExperID)
        {
            int[]? groupallow = null;
            if (ExperID.HasValue)
            {
                var groups = await _groupService.GetGroupsExper(ExperID.Value);
                if (groups.Count > 0) groupallow = groups.Select(s => s.ID).ToArray();
            }

            var request = _ConversationRepository.Get()
       .Include(inc => inc.Group)
       .Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
   .Where(w => w.Status == ConversationStatus.Recorded && w.CompanyID == CompanyID);

            if (groupallow != null) request = request.Where(w => !w.GroupID.HasValue || groupallow.Contains(w.GroupID.Value));
            return await request.Select(s => new ChatItemDto()
            {
                ID = s.ID,
                ExperID = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).ExperID,
                ExperFullName = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).Exper.FullName,
                GroupID = s.GroupID,
                GroupName = s.Group.Name,
                LastText = s.ConversationResponses.OrderBy(o => o.ID).Last().Text,
                LastMsgdate = s.Cdatetime.GetDatePersian(),
                LastMsgtime = s.Cdatetime.GetTime(),
                LastMsgType = s.ConversationResponses.OrderBy(o => o.ID).Last().Type,
                status = s.Status,
                UserID = s.UserID,
                UserFullName = string.IsNullOrEmpty(s.User.FullName) ? s.User.Mobile : s.User.FullName,
                Responses = s.ConversationResponses.Select(ss => new ChatItemResponseDto()
                {
                    ChatItemID = ss.ConversationID,
                    ExperID = ss.ExperID,
                    ExperName = ss.Exper.FullName,
                    FileContent = ss.FileContent,
                    FileName = ss.FileName,
                    FileType = ss.FileType,
                    ID = ss.ID,
                    IsRead = ss.IsRead,
                    text = ss.Text,
                    Type = ss.Type
                }).ToList()

            }).ToListAsync();
        }
        //------------------------
        public async Task<ResponseBase<ChatItemDto>> NewChat(ADD_ConversationDto dto, ConversationType type = ConversationType.UE)
        {
            var Response = new ResponseBase<ChatItemDto>();
            if (await _companyService.AnyCompany(dto.CompanyID))
            {
                if (await _userService.AnyUser(dto.UserID))
                {
                    if (type == ConversationType.Bot)
                    {
                        if (!await _companyService.AllowBot(dto.CompanyID))
                        {
                            Response.Errors.Add("دستیار گفتگو هوشمند برای این شرکت در دسترس نمی باشد");
                            return Response;
                        }
                    }

                    if (!dto.GroupID.HasValue || (dto.GroupID.HasValue
                   && await _groupService.AnyGroup(dto.GroupID.Value)
                    && await _groupService.CHeckGroupMemberCompany(dto.GroupID.Value, dto.CompanyID)))
                    {
                        Conversation conversation = new Conversation()
                        {
                            CompanyID = dto.CompanyID,
                            UserID = dto.UserID,
                            GroupID = dto.GroupID,
                            ConversationResponses = new List<ConversationResponse>() { new() { Text = dto.Question, Type = type } }
                        };
                        var mi = await _ConversationRepository.ADD(conversation);
                        if (mi.ID > 0)
                            Response.Value = await _ConversationRepository.Get()
                                    .Include(inc => inc.Group).Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
                                    .Where(w => w.ID == mi.ID)
                                    .Select(s => new ChatItemDto()
                                    {
                                        ID = s.ID,
                                        ExperID = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).ExperID,
                                        ExperFullName = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).Exper.FullName,
                                        GroupID = s.GroupID,
                                        GroupName = s.Group.Name,
                                        LastText = s.ConversationResponses.OrderBy(o => o.ID).Last().Text,
                                        LastMsgdate = s.Cdatetime.GetDatePersian(),
                                        LastMsgtime = s.Cdatetime.GetTime(),
                                        LastMsgType = s.ConversationResponses.OrderBy(o => o.ID).Last().Type,
                                        status = s.Status,
                                        UserID = s.UserID,
                                        UserFullName = string.IsNullOrEmpty(s.User.FullName) ? s.User.Mobile : s.User.FullName,
                                        Responses = s.ConversationResponses.Select(ss => new ChatItemResponseDto()
                                        {
                                            ChatItemID = ss.ConversationID,
                                            ExperID = ss.ExperID,
                                            ExperName = ss.Exper.FullName,
                                            FileContent = ss.FileContent,
                                            FileName = ss.FileName,
                                            FileType = ss.FileType,
                                            ID = ss.ID,
                                            IsRead = ss.IsRead,
                                            text = ss.Text,
                                            Type = ss.Type
                                        }).ToList()

                                    }).FirstOrDefaultAsync();
                        Response.Success = mi.ID > 0;
                        if (Response.Success)
                       await NewChatInHub(dto.CompanyID);
                    }
                    else Response.Errors.Add("شناسه گروه صحیح نمی باشد");



                }
                else Response.Errors.Add("شناسه کاربر یافت نشد");

            }
            else Response.Errors.Add("شناسه شرکت یافت نشد");


            return Response;
        }
        public async Task<ResponseBase<ChatItemResponseDto>> ADDChatResponse(ADD_ConversationResponseDto dto, int? ExperID)
        {
            var Response = new ResponseBase<ChatItemResponseDto>();
            if (dto.Type == ConversationType.EU && !ExperID.HasValue)
            {
                Response.Errors.Add("کارشناس گفتگو را مشخص گنید");
            }
            else
            {
                var convModel = await _ConversationRepository.Get()
                              .Include(inc => inc.ConversationResponses)
                              .FirstOrDefaultAsync(w => w.ID == dto.ConversationID);

                if (convModel != null)
                {
                    if (ExperID.HasValue && !await _experService.CheckExperInCompany(convModel.CompanyID, ExperID.Value))
                    {
                        Response.Errors.Add("کارشناس گفتگو صحیح نمی باشد");
                        return Response;
                    }
                    ConversationResponse response = new ConversationResponse()
                    {
                        ConversationID = convModel.ID,
                        Text = dto.Text,
                        Type = dto.Type,
                        ExperID = ExperID,
                        FileContent = dto.FileContent,
                        FileType = dto.FileType,
                        FileName = dto.FileName
                    };
                    var statuschangedb = await _ConversationResponseRepository.ADD(response);
                    Response.Value = new ChatItemResponseDto()
                    {
                        ChatItemID = statuschangedb.ConversationID,
                        ExperID = statuschangedb.ExperID,
                        ExperName = /*statuschangedb.Exper.FullName*/ "",
                        FileContent = statuschangedb.FileContent,
                        FileName = statuschangedb.FileName,
                        FileType = statuschangedb.FileType,
                        ID = statuschangedb.ID,
                        IsRead = statuschangedb.IsRead,
                        text = statuschangedb.Text,
                        Type = statuschangedb.Type
                    };
                    Response.Success = statuschangedb.ID > 0;

                    if (dto.Type != ConversationType.UE && convModel.Status == ConversationStatus.Recorded && Response.Success)
                    {
                        convModel.Status = ConversationStatus.InProgress;
                        await _ConversationRepository.UPDATE(convModel);
                    }
                    if (dto.Type != ConversationType.UE)
                        await WriteInHubFromCompany(Response.Value, convModel.UserID.ToString());
                    else
                    {
                        List<string> users = new();
                        users.Add("C" + convModel.CompanyID.ToString());

                        var modelA = convModel.ConversationResponses.OrderBy(o => o.ID)
                            .LastOrDefault(l =>/* l.Type == ConversationType.EU || l.Type == ConversationType.CU*/ l.ExperID.HasValue);
                        if (modelA != null /*&& (modelA.Type == ConversationType.EU || modelA.Type == ConversationType.UE)*/)
                       // {
                          

                            //if () 
                                users.Add(modelA.ExperID.Value.ToString());

                           

                       

                       // }
                        //else await NewChatInHub(convModel.CompanyID);
                        await WriteInHubFromUser(Response.Value, users);
                    }

                }
                else Response.Errors.Add("گفتگویی یافت نشد");
            }

            return Response;
        }
        public async Task<bool> FinishChat(int ChatID, int CompanyID, int? ExperID)
        {
            var convModel = await _ConversationRepository.Get()
                             .Include(inc => inc.ConversationResponses)
                             .FirstOrDefaultAsync(w => w.ID == ChatID && w.CompanyID == CompanyID);
            if (ExperID.HasValue && !convModel.ConversationResponses.Any(a => a.ExperID == ExperID.Value)) return false;

            if (convModel != null && convModel.Status != ConversationStatus.Finished)
            {
                convModel.Status = ConversationStatus.Finished;
                convModel.FinishedDateTime = DateTime.Now;
                return await _ConversationRepository.UPDATEBool(convModel);
            }

            return true;
        }
        public async Task<bool> OpenChat(int ChatID, int CompanyID, int? ExperID)
        {
            var convModel = await _ConversationRepository.Get()
                             .Include(inc => inc.ConversationResponses)
                             .FirstOrDefaultAsync(w =>
                             w.ID == ChatID
                             && w.CompanyID == CompanyID);

            if (ExperID.HasValue && !convModel.ConversationResponses.Any(a => a.ExperID == ExperID.Value)) return false;

            if (convModel != null && convModel.Status == ConversationStatus.Finished)
            {
                convModel.Status = ConversationStatus.InProgress;
                convModel.FinishedDateTime = null;
                return await _ConversationRepository.UPDATEBool(convModel);
            }

            return true;
        }
        public async Task<bool> FinishChatFromUser(int ChatID, int userID)
        {
            var convModel = await _ConversationRepository.Get()
                             .Include(inc => inc.ConversationResponses)
                             .FirstOrDefaultAsync(w => w.ID == ChatID && w.UserID == userID);

            if (convModel != null && convModel.Status != ConversationStatus.Finished)
            {
                convModel.Status = ConversationStatus.Finished;
                convModel.FinishedDateTime = DateTime.Now;
                return await _ConversationRepository.UPDATEBool(convModel);
            }

            return true;
        }
        public async Task<bool> Attachedto(int ChatID, int? FromExperID, int toExperID, int CompanyID)
        {

            return false;
        }
        public async Task<bool> MarkAsReadChatItem(int ID, ConversationType Type, int? ExperID)
        {
            var item = await _ConversationResponseRepository.Get()
                .Include(inc => inc.conversation).FirstOrDefaultAsync(w => w.ID == ID /*&& !w.ExperID.HasValue*/
                && w.conversation.Status != ConversationStatus.Finished);

            if (item != null)
            {
                if (Type != item.Type)
                {
                    if (Type != ConversationType.UE && item.conversation.Status == ConversationStatus.Recorded)
                    {
                        item.conversation.Status = ConversationStatus.InProgress;
                        await _ConversationRepository.UPDATE(item.conversation);
                    }

                    if (!item.IsRead)
                    {
                        item.IsRead = true;
                        item.ReadDateTime = DateTime.Now;
                        if (!item.ExperID.HasValue)
                        item.ExperID = ExperID;

                
                        if( (await _ConversationResponseRepository.UPDATE(item)) != null)
                        {
                            string user = "";
                            if (item.Type == ConversationType.EU) user = item.ExperID.Value.ToString();

                            else if (item.Type == ConversationType.CU) user ="C"+ item.conversation.CompanyID.ToString();

                            else if (item.Type == ConversationType.UE) user =  item.conversation.UserID.ToString();

                            await CheckMarkAsReadInHub(item.ID, user);

                            return true;
                        }
                    }
                }


            }
            return false;
        }
        public async Task<ChatItemDto?> GetLastOpenChatInCompany(int CompanyID, int UserID)
        {
           return await _ConversationRepository.Get()
                .Include(inc => inc.Group)
                .Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
            .Where(w => w.UserID == UserID && w.CompanyID == CompanyID && w.Status != ConversationStatus.Finished)
            .OrderByDescending(o => o.ID)
                .Select(s => new ChatItemDto()
                {
                    ID = s.ID,
                    ExperID = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).ExperID,
                    ExperFullName = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).Exper.FullName,
                    GroupID = s.GroupID,
                    GroupName = s.Group.Name,
                    LastText = s.ConversationResponses.OrderBy(o => o.ID).Last().Text,
                    LastMsgdate = s.Cdatetime.GetDatePersian(),
                    LastMsgtime = s.Cdatetime.GetTime(),
                    LastMsgType = s.ConversationResponses.OrderBy(o => o.ID).Last().Type,
                    status = s.Status,
                    UserID = s.UserID,
                    UserFullName = string.IsNullOrEmpty(s.User.FullName) ? s.User.Mobile : s.User.FullName,
                    Responses = s.ConversationResponses.Select(ss => new ChatItemResponseDto()
                    {
                        ChatItemID = ss.ConversationID,
                        ExperID = ss.ExperID,
                        ExperName = ss.Exper.FullName,
                        FileContent = ss.FileContent,
                        FileName = ss.FileName,
                        FileType = ss.FileType,
                        ID = ss.ID,
                        IsRead = ss.IsRead,
                        text = ss.Text,
                        Type = ss.Type
                    }).ToList()

                }).FirstOrDefaultAsync();

        
        }
        public async Task<ChatItemDto?> GetChat(int ChatID, int UserID)
        {
           return await _ConversationRepository.Get()
                .Include(inc => inc.Group)
                .Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
                     .Where(w => w.UserID == UserID && w.ID == ChatID)
            .OrderByDescending(o => o.ID)
                .Select(s => new ChatItemDto()
                {
                    ID = s.ID,
                    ExperID = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).ExperID,
                    ExperFullName = s.ConversationResponses.OrderBy(o => o.ID).Last(l => l.ExperID.HasValue).Exper.FullName,
                    GroupID = s.GroupID,
                    GroupName = s.Group.Name,
                    LastText = s.ConversationResponses.OrderBy(o => o.ID).Last().Text,
                    LastMsgdate = s.Cdatetime.GetDatePersian(),
                    LastMsgtime = s.Cdatetime.GetTime(),
                    LastMsgType = s.ConversationResponses.OrderBy(o => o.ID).Last().Type,
                    status = s.Status,
                    UserID = s.UserID,
                    UserFullName = string.IsNullOrEmpty(s.User.FullName) ? s.User.Mobile : s.User.FullName,
                    Responses = s.ConversationResponses.Select(ss => new ChatItemResponseDto()
                    {
                        ChatItemID = ss.ConversationID,
                        ExperID = ss.ExperID,
                        ExperName = ss.Exper.FullName,
                        FileContent = ss.FileContent,
                        FileName = ss.FileName,
                        FileType = ss.FileType,
                        ID = ss.ID,
                        IsRead = ss.IsRead,
                        text = ss.Text,
                        Type = ss.Type
                    }).ToList()

                }).FirstOrDefaultAsync();

  
        }
        public async Task WriteInHubFromCompany(ChatItemResponseDto item, string ToUser)
        {
            await _hubContext.Clients.User(ToUser)
                .SendAsync("ReceiveNewChatItemFromCompany", item);

            //// فرض: لیستی از کاربرانی که به گفتگو دسترسی دارند
            //var usernames = new List<string>();

            //foreach (var usn in usernames)
            //{
            //    //await _hubContext.Clients.User(usn)
            //    //    .SendAsync("ReceiveNewConversation", conv.Id, conv.Title);
            //}
        }
        public async Task WriteInHubFromUser(ChatItemResponseDto item,List< string> ToUserS)
        {
            foreach (var ToUser in ToUserS)
            await _hubContext.Clients.User(ToUser)
                .SendAsync("ReceiveNewChatItemFromUser", item);
        }
        public async Task CheckMarkAsReadInHub(int item, string ToUser)
        {
            await _hubContext.Clients.User(ToUser)
                .SendAsync("CheckMarkAsRead", item);
        }
        public async Task NewChatInHub(int CompanyID)
        {
            await _hubContext.Clients.All
                .SendAsync("NewChat", CompanyID);
        }

        public async Task<(int, int)?> GetLastChat(int UserID)
        {
            int companyid = 0;
            var request = _ConversationRepository.Get()
                .Include(inc => inc.Group)
                .Include(inc => inc.ConversationResponses).ThenInclude(tinc => tinc.Exper)
                     .Where(w => w.UserID == UserID)
            .OrderByDescending(o => o.ID);
               var item1=await request.Select(s => s.ID).FirstOrDefaultAsync();
            if (item1 == null) return null;
            else
            {
                companyid = await request.Select(s => s.CompanyID).FirstOrDefaultAsync();
            }

            return (item1,companyid);
        }
    }
}
