using Azure;
using Azure.Communication;
using Azure.Communication.Identity;
using Azure.Communication.Rooms;
using EnotaryoPH.Data;
using EnotaryoPH.Data.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace EnotaryoPH.Web.Pages.Participant.VideoCall
{
    public class RoomModel : PageModel
    {
        private const string CONNECTION_STRING = "endpoint=https://comm-enotario.asiapacific.communication.azure.com/;accesskey=yqxq5mRByG8aPwZBiT/KFfx6Rr16RR280dC3GVkGaFVNXGk91sIDy04j5jrZfjDZAvdn0fjtyF0kGJPXMGgfKg==";
        private readonly ICurrentUserService _currentUserService;
        private readonly NotaryoDBContext _dbContext;
        private readonly ISession _session;
        private readonly CommunicationUserIdentifier? _user1;
        private readonly CommunicationUserIdentifier? _user2;
        private CommunicationIdentityClient? _communicationIdentityClient;
        private LawyerVideoConferenceSchedule _LawyerVideoConferenceSchedule;
        private Transaction _Transaction;

        public RoomModel(IHttpContextAccessor httpContextAccessor, ICurrentUserService currentUserService, NotaryoDBContext dbContext)
        {
            _session = httpContextAccessor.HttpContext.Session;
            _currentUserService = currentUserService;
            _dbContext = dbContext;
        }

        public async Task<IActionResult> OnGetAsync()
        {
            _Transaction = _dbContext.Transactions
                .Include(t => t.TransactionSignatories)
                .FirstOrDefault(t => t.Transaction_UID == Transaction_UID);
            if (_Transaction == null)
            {
                return NotFound();
            }

            _LawyerVideoConferenceSchedule = _dbContext.LawyerVideoConferenceSchedules
                .Include(meeting => meeting.LawyerVideoConferenceParticipants)
                .ThenInclude(p => p.Participant)
                .Include(meeting => meeting.Lawyer)
                .ThenInclude(l => l.User)
                .FirstOrDefault(meeting => meeting.TransactionID == _Transaction.TransactionID);

            if (ShouldCreateRoom())
            {
                _LawyerVideoConferenceSchedule ??= new LawyerVideoConferenceSchedule
                {
                    LawyerVideoConferenceSchedule_UID = Guid.CreateVersion7(),
                    CreatedOn = DateTime.UtcNow,
                    LawyerID = _Transaction.LawyerID.GetValueOrDefault(),
                    TransactionID = _Transaction.TransactionID,
                };
                _LawyerVideoConferenceSchedule.MeetingDate = DateTime.UtcNow;
                _LawyerVideoConferenceSchedule.Status = nameof(VideoConferenceStatus.New);

                var roomParticipants = new List<RoomParticipant>();
                foreach (var participant in _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants)
                {
                    var attendee = await CommunicationIdentityClient.CreateUserAsync();
                    roomParticipants.Add(new RoomParticipant(attendee) { Role = ParticipantRole.Attendee });
                    participant.MeetingRoomTokenID = await GetTokenResponse(attendee);
                    participant.MeetingRoomUserID = attendee.Value.Id;
                }

                var presenter = await CommunicationIdentityClient.CreateUserAsync();
                roomParticipants.Add(new RoomParticipant(presenter) { Role = ParticipantRole.Presenter });
                _LawyerVideoConferenceSchedule.MeetingRoomTokenID = await GetTokenResponse(presenter);
                _LawyerVideoConferenceSchedule.MeetingRoomUserID = presenter.Value.Id;

                var roomsClient = new RoomsClient(CONNECTION_STRING);
                CommunicationRoom room = await roomsClient.CreateRoomAsync(DateTime.Now, DateTime.Now.AddHours(2), roomParticipants);

                _LawyerVideoConferenceSchedule.MeetingRoomID = room.Id;

                if (_LawyerVideoConferenceSchedule.LawyerVideoConferenceScheduleID == 0)
                {
                    _dbContext.Add(_LawyerVideoConferenceSchedule);
                }
                else
                {
                    _dbContext.Update(_LawyerVideoConferenceSchedule);
                }
                _dbContext.SaveChanges();
            }

            var currentUser = _dbContext.Users.FirstOrDefault(u => u.User_UID == _currentUserService.GetUser_UID());
            CommunicationUserToken = currentUser.Role == nameof(UserType.Notary)
                ? _LawyerVideoConferenceSchedule.MeetingRoomTokenID
                : _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID).MeetingRoomTokenID;
            CommunicationRoomId = _LawyerVideoConferenceSchedule.MeetingRoomID;
            CommunicationUserId = currentUser.Role == nameof(UserType.Notary)
                ? _LawyerVideoConferenceSchedule.MeetingRoomUserID
                : _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID).MeetingRoomUserID;


            return Page();
        }

        private static string GetHashCode(string stringValue) => stringValue.GetHashCode().ToString("X8");

        private async Task<string> GetTokenResponse(Response<CommunicationUserIdentifier> user)
        {
            var tokenResponse = await CommunicationIdentityClient.GetTokenAsync(user, new[] { CommunicationTokenScope.VoIP });
            return tokenResponse.Value.Token;
        }

        private bool ShouldCreateRoom() => _LawyerVideoConferenceSchedule == null || DateTime.UtcNow.Subtract(_LawyerVideoConferenceSchedule.MeetingDate).TotalHours > 1 || _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.Any(p => string.IsNullOrEmpty(p.MeetingRoomTokenID));

        public string CommunicationRoomId { get; private set; }
        public string CommunicationUserToken { get; private set; }
        public string CommunicationUserId { get; set; }

        public List<RoomParticipantViewModel> Participants
        {
            get
            {
                var signatoryTypes = _Transaction.TransactionSignatories.Where(t => t.UserID > 0).ToDictionary(k => k.UserID, v => v.Type);
                var dic = _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.ConvertAll(p => new RoomParticipantViewModel
                {
                    Id = p.LawyerVideoConferenceParticipant_UID.ToString(),
                    DisplayName = $"{p.Participant.Firstname} {p.Participant.Lastname}",
                    RoomUserID = p.MeetingRoomUserID,
                    Type = signatoryTypes.GetValueOrDefault(p.ParticipantID, nameof(UserType.Principal))
                });

                var host = _LawyerVideoConferenceSchedule.Lawyer.User;
                dic.Add(new RoomParticipantViewModel
                {
                    DisplayName = $"{host.Firstname} {host.Lastname}",
                    Id = Guid.Empty.ToString(),
                    RoomUserID = _LawyerVideoConferenceSchedule.MeetingRoomUserID,
                    Type = nameof(UserType.Notary)
                });

                return dic;
            }
        }

        [BindProperty(SupportsGet = true)]
        public Guid Transaction_UID { get; set; }

        private CommunicationIdentityClient CommunicationIdentityClient => _communicationIdentityClient ??= new CommunicationIdentityClient(CONNECTION_STRING);
    }
}