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 readonly ICurrentUserService _currentUserService;
        private readonly NotaryoDBContext _dbContext;
        private readonly IVideoConferenceService _videoConferenceService;
        private Transaction _Transaction;

        public RoomModel(ICurrentUserService currentUserService, NotaryoDBContext dbContext,
            IVideoConferenceService videoConferenceService)
        {
            _currentUserService = currentUserService;
            _dbContext = dbContext;
            _videoConferenceService = videoConferenceService;
        }

        public async Task<IActionResult> OnGetAsync()
        {
            LoadTransaction();
            if (_Transaction == null)
            {
                return NotFound();
            }

            var currentUser = _dbContext.Users.Single(u => u.User_UID == _currentUserService.GetUser_UID());
            var canStart = _videoConferenceService.CanStart(Transaction_UID);
            if (!canStart)
            {
                if (_Transaction.PrincipalID == currentUser.UserID)
                {
                    return Redirect($"/Principal/TransactionStatus/{Transaction_UID}");
                }

                if (currentUser.Role.IsInList(UserType.Principal, UserType.Witness))
                {
                    return Redirect($"/Participant/VideoCall/Waiting/{Transaction_UID}");
                }

                return Redirect("/");
            }

            if (_videoConferenceService.HasExpired(Transaction_UID))
            {
                TempData["Warning"] = "The video conference has expired.";
                return Redirect("/");
            }

            DocumentType = _Transaction.TransactionDocument.DocumentType;

            var schedule_UID = await _videoConferenceService.StartAsync(Transaction_UID);

            CommunicationUserToken = currentUser.Role == nameof(UserType.Notary)
                ? _Transaction.Schedule.MeetingRoomTokenID
                : GetParticipant(currentUser).MeetingRoomTokenID;
            CommunicationRoomId = _Transaction.Schedule.MeetingRoomID;
            CommunicationUserId = currentUser.Role == nameof(UserType.Notary)
                ? _Transaction.Schedule.MeetingRoomUserID
                : GetParticipant(currentUser).MeetingRoomUserID;
            DisplayName = currentUser.Role == nameof(UserType.Notary)
                ? _Transaction.Lawyer.User.Fullname
                : GetParticipant(currentUser).Participant.Fullname.DefaultIfEmpty(currentUser.Email);
            ParticipantType = currentUser.Role == nameof(UserType.Notary)
                ? nameof(UserType.Notary)
                : GetParticipant(currentUser).Participant.Role;

            return Page();
        }

        public IActionResult OnGetDocument()
        {
            var document = _dbContext.TransactionDocuments
                .Include(doc => doc.Transaction)
                .FirstOrDefault(doc => doc.Transaction.Transaction_UID == Transaction_UID);

            return new FileContentResult(document.File, "application/pdf");
        }

        public IActionResult OnGetIdentificationDocument(string meetingRoomUserID)
        {
            var participant = _dbContext.LawyerVideoConferenceParticipants
                .AsNoTracking()
                .FirstOrDefault(participant => participant.MeetingRoomUserID == meetingRoomUserID);
            if (participant == null)
            {
                return NotFound();
            }
            var identificationDocumentID = _dbContext.TransactionSelfies.AsNoTracking()
                .Where(selfie => selfie.UserID == participant.ParticipantID && selfie.Transaction.Transaction_UID == Transaction_UID)
                .Select(selfie => selfie.IdentificationDocumentID).FirstOrDefault();
            if (identificationDocumentID == 0)
            {
                return NotFound();
            }

            var identificationDocument = _dbContext.IdentificationDocuments.FirstOrDefault(id => id.IdentificationDocumentID == identificationDocumentID);
            if (identificationDocument == null)
            {
                return NotFound();
            }

            return new FileContentResult(identificationDocument.File, "image/jpeg");
        }

        public IActionResult OnGetSelfieImage(string meetingRoomUserID)
        {
            var participant = _dbContext.LawyerVideoConferenceParticipants
                .AsNoTracking()
                .FirstOrDefault(participant => participant.MeetingRoomUserID == meetingRoomUserID);
            if (participant == null)
            {
                return NotFound();
            }
            var transactionSelfie = _dbContext.TransactionSelfies.FirstOrDefault(selfie => selfie.UserID == participant.ParticipantID && selfie.Transaction.Transaction_UID == Transaction_UID);
            if (transactionSelfie == null)
            {
                return NotFound();
            }

            return new FileContentResult(transactionSelfie.File, "image/jpeg");
        }

        public async Task<JsonResult> OnPostApproveAsync()
        {
            await _videoConferenceService.ApproveTransactionAsync(Transaction_UID);
            return new JsonResult(true);
        }

        public async Task<JsonResult> OnPostStartRecordingAsync()
        {
            LoadTransaction();
            await _videoConferenceService.StartRecordingAsync(Transaction_UID, ServerCallID);
            return new JsonResult(true);
        }

        private LawyerVideoConferenceParticipant GetParticipant(User currentUser) => _Transaction.Schedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID);

        private void LoadTransaction() => _Transaction = _dbContext.Transactions
            .Include(t => t.TransactionSignatories)
            .Include(t => t.Lawyer)
            .ThenInclude(l => l.User)
            .Include(t => t.Principal)
            .Include(t => t.Schedule)
            .ThenInclude(sch => sch.LawyerVideoConferenceParticipants)
            .ThenInclude(p => p.Participant)
            .Include(t => t.TransactionDocument)
            .FirstOrDefault(t => t.Transaction_UID == Transaction_UID);

        public string CommunicationRoomId { get; private set; }

        public string CommunicationUserId { get; set; }

        public string CommunicationUserToken { get; private set; }

        public string DisplayName { get; private set; }

        public string DocumentType { get; set; }

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

                var host = _Transaction.Schedule.Lawyer.User;
                participants.Add(new RoomParticipantViewModel
                {
                    DisplayName = $"{host.Firstname} {host.Lastname}".Trim().DefaultIfEmpty(host.Email),
                    UID = Guid.Empty.ToString(),
                    RoomUserID = _Transaction.Schedule.MeetingRoomUserID,
                    Type = nameof(UserType.Notary)
                });

                return participants;
            }
        }

        public string ParticipantType { get; set; }
        [BindProperty(SupportsGet = true)] public string ServerCallID { get; set; }

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