using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Security.Claims;
using System.Xml.Linq;
using AutoMapper;
using Azure.Core;
using EFCore.BulkExtensions;
using FirebaseAdmin.Auth;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using OnlineAssessment.Common;
using OnlineAssessment.Domain.Models;
using OnlineAssessment.Domain.ViewModels;
using Razorpay.Api;

namespace OnlineAssessment.Data.EFCore
{
    public class EfCoreUserRepository : EfCoreRepository<Users, OnlineAssessmentContext>
    {
        public readonly OnlineAssessmentContext _context;
        public readonly EfCoreCommonRepository _common;
        private readonly IMapper _mapper;
        //private readonly IConfiguration _config;
        public EfCoreUserRepository(OnlineAssessmentContext context, IMapper mapper) : base(context)
        {
            _context = context;
            _common = new EfCoreCommonRepository(_context, mapper);
            _mapper = mapper;

        }

        public EfCoreUserRepository(OnlineAssessmentContext context, IMapper mapper, IOptionsSnapshot<ResponseMessage> responseMessage) : base(context, responseMessage)
        {
            _context = context;
            _common = new EfCoreCommonRepository(_context, mapper);
            _mapper = mapper;
        }

        public int GetLanguageIdByCode(string language_code)
        {
            Language language = _common.GetLanguageByCode(language_code);

            if (language == null) return -1;
            int language_id = (int)language.Id;

            return language_id;
        }

        public int UpdateMyLanguage(int user_id, int language_id, out string return_message)
        {
            return_message = "";

            try
            {
                Users theUser = _context.Users.FirstOrDefault(u => u.Id.Equals(user_id));
                if(theUser.LanguageId != language_id)
                {
                    theUser.LanguageId = language_id;
                    _context.Users.Update(theUser);
                    _context.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                return_message = ex.InnerException.Message.ToString();
                return -1;
            }
            return_message = "Language updated";
            return language_id;
        }


        public int UpdateMyDetails(int user_id, ProfileDetailView details, out string return_message)
        {
            return_message = "";

            try
            {
                Users theUser = _context.Users.FirstOrDefault(u => u.Id.Equals(user_id));
                if(theUser == null)
                {
                    return_message = Message.ObjectNotFound.ToString();
                    return -1;
                }


                if (details.FirstName != null && details.FirstName != theUser.FirstName) theUser.FirstName = details.FirstName;
                if (details.LastName != null && details.LastName != theUser.LastName) theUser.LastName = details.LastName;
                if (details.Gender != null && details.Gender != theUser.Gender && System.Enum.IsDefined(typeof(GenderCode), details.Gender)) theUser.Gender = details.Gender;
                if (details.State != null && details.State != theUser.StateCode && System.Enum.IsDefined(typeof(StateCode), details.State)) theUser.StateCode = details.State;
                if (details.City != null && details.City != theUser.City) theUser.City = details.City;

                _context.Users.Update(theUser);
                _context.SaveChanges();
            }
            catch (Exception ex)
            {
                return_message = ex.InnerException.Message.ToString();
                return -1;
            }
            return_message = "Profile updated";
            return user_id;
        }

        public dynamic GetMyDetails(int user_id, out string return_message)
        {
            return_message = "";
            ProfileDetailView details = new ProfileDetailView();
            try
            {
                Users theUser = _context.Users.FirstOrDefault(u => u.Id.Equals(user_id));
                if (theUser == null)
                {
                    return_message = Message.ObjectNotFound.ToString();
                    return -1;
                }

                details.FirstName = theUser.FirstName;
                details.LastName = theUser.LastName;
                details.Gender = theUser.Gender;
                details.State = theUser.StateCode;
                details.City = theUser.City;
            }
            catch (Exception ex)
            {
                return_message = ex.InnerException.Message.ToString();
                return -1;
            }

            return_message = "Profile updated";
            return details;
        }

        /// <summary>
        /// Log in to the system
        /// </summary>
        /// <param name="userLoginCredentials"></param>
        /// <param name="returnValue"></param>
        /// <returns></returns>
        public LoginViewModel SignIn(UserLogin userLoginCredentials, out int returnValue)
        {
            returnValue = 0;
            LoginViewModel returnUser = null;
            try
            {
                //Check if the user is trying to login with username
                var quizUser = _context.Users.Where(u => u.EmailId == userLoginCredentials.EmailId).ToList();
                if (quizUser == null || quizUser.Count.Equals(0) || string.IsNullOrEmpty(userLoginCredentials.EmailId))
                {
                    returnValue = (int)UserMessage.InvalidUser;
                    return null;
                }

                //Get the user information
                Users user = quizUser[0];

                //Check for password
                string suppliedHasedPassword = Security.GetSaltedHashPassword(user.UserSalt, userLoginCredentials.UserPassword);
                string actualHashedPassword = user.UserPassword;
                if (!(suppliedHasedPassword.Equals(actualHashedPassword)))
                {
                    //Password mismatch
                    returnValue = (int)UserMessage.InvalidPasword;
                }
                else if (user.IsActive.Equals(false))
                {
                    //User not allowed to login
                    returnValue = (int)UserMessage.UserNotActive;
                }
                else
                {
                    //Get user details
                    returnUser = _common.GetLoginDetails(user.Id);

                    //Return positive value in output parameter
                    returnValue = user.Id;

                }

            }
            catch (Exception ex)
            {
                returnValue = (int)Message.InvalidOperation;
                returnUser = null;
                throw new Exception(ex.Message);
            }

            return returnUser;
        }

        //private string GenerateJsonWebToken(UserViewModel userInfo)
        //{
        //    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes());
        //}

        /// <summary>
        /// Get list of users
        /// </summary>
        /// <returns></returns>
        public List<UserViewModel> GetUsersList()
        {
            List<UserViewModel> userList = new List<UserViewModel>();


            //Check if the user is trying to login with username
            var users = _context.Users.ToList();
            if (users == null || users.Count.Equals(0))
            {
                return null;
            }
            else
            {
                for (int x = 0; x <= users.Count - 1; x++)
                {
                    UserViewModel usr = _common.GetUserById(users[x].Id);
                    userList.Add(usr);
                }
            }
            return userList;

        }

        public UserViewModel UpdateUser(int id, UserEditModel userEdit, out string returnMessage)
        {
            UserViewModel uvm;
            returnMessage = "";
            try
            {
                Users theUser = _context.Users.FirstOrDefault(u => u.Id.Equals(id));

                theUser.InstituteId = userEdit.InstituteId.Equals(0) ? theUser.InstituteId : userEdit.InstituteId;
                theUser.RoleId = userEdit.RoleId.Equals(0) ? theUser.RoleId : userEdit.RoleId;
                theUser.LanguageId = userEdit.LanguageId.Equals(0) ? theUser.LanguageId : userEdit.LanguageId;
                theUser.FirstName = string.IsNullOrEmpty(userEdit.FirstName) ? theUser.FirstName : userEdit.FirstName;
                theUser.LastName = string.IsNullOrEmpty(userEdit.LastName) ? theUser.LastName : userEdit.LastName;
                theUser.Address = string.IsNullOrEmpty(userEdit.LastName) ? theUser.LastName : userEdit.LastName;
                theUser.City = string.IsNullOrEmpty(userEdit.City) ? theUser.City : userEdit.City;
                theUser.Country = _common.GetCountryByStateId(userEdit.StateId);
                theUser.PinCode = string.IsNullOrEmpty(userEdit.PinCode) ? theUser.PinCode : userEdit.PinCode;
                //theUser.EmailId = userEdit.EmailId;
                theUser.MobileNo = string.IsNullOrEmpty(userEdit.MobileNo) ? theUser.MobileNo : userEdit.MobileNo;
                theUser.Longitude = string.IsNullOrEmpty(userEdit.Longitude) ? theUser.Longitude : userEdit.Longitude;
                theUser.Latitude = string.IsNullOrEmpty(userEdit.Latitude) ? theUser.Latitude : userEdit.Latitude;
                theUser.RegistrationId = userEdit.RegistrationId.Equals(0) ? theUser.RegistrationId : userEdit.RegistrationId;
                theUser.RegistrationDatetime = _common.NowIndianTime();
                theUser.Photo = null;//string.IsNullOrEmpty(userEdit.LastName) ? theUser.Photo : base.ConvertStringToBytes(userEdit.Photo);

                theUser.UserPassword = string.IsNullOrEmpty(userEdit.UserPassword) ? theUser.UserPassword : Security.GetSaltedHashPassword(theUser.UserSalt, userEdit.UserPassword);

                _context.Users.Update(theUser);
                _context.SaveChanges();

                uvm = _common.GetUserById(id);
            }
            catch (Exception ex)
            {
                returnMessage = ex.InnerException.Message.ToString();
                uvm = null;
            }
            return uvm;
        }

        /// <summary>
        /// Register user
        /// </summary>
        /// <param name="user"></param>
        /// <param name="returnCode"></param>
        /// <param name="returnMessage"></param>
        /// <returns></returns>
        public UserViewModel SignUp(UserAddModel user, out int returnCode, out string returnMessage)
        {
            UserViewModel newReturnUser = null;
            int newUserId = 0;
            returnMessage = string.Empty;
            using (var tx = _context.Database.BeginTransaction())
            {
                try
                {

                    Users newUser = new Users
                    {
                        InstituteId = user.InstituteId,
                        RoleId = user.RoleId,
                        LanguageId = user.LanguageId,
                        FirstName = user.FirstName,
                        LastName = user.LastName,
                        Address = user.Address,
                        City = user.City,
                        Country = _common.GetCountryByStateId(user.StateId),
                        PinCode = user.PinCode,
                        EmailId = user.EmailId,
                        MobileNo = user.MobileNo,
                        Longitude = user.Longitude,
                        Latitude = user.Latitude,
                        RegistrationId = user.RegistrationId,
                        RegistrationDatetime = _common.NowIndianTime(),
                        UserSalt = Security.GetNewSalt(8),
                        Photo = null//base.ConvertStringToBytes(user.Photo)
                    };
                    newUser.UserPassword = Security.GetSaltedHashPassword(newUser.UserSalt, user.UserPassword);

                    _context.Users.Add(newUser);
                    _context.SaveChanges();

                    newUserId = newUser.Id;

                    // Add classes of the user
                    //foreach (int userClassId in user.Classes)
                    //{
                    //    ClassesUsers cu = new ClassesUsers
                    //    {
                    //        UserId = newUserId,
                    //        ClassId = userClassId
                    //    };

                    //    _context.ClassesUsers.Add(cu);
                    //    _context.SaveChanges();
                    //}

                    // Add subjects of the user
                    //foreach (int userSubjectId in user.Subjects)
                    //{
                    //    UserSubjects us = new UserSubjects
                    //    {
                    //        UserId = newUserId,
                    //        SubjectId = userSubjectId
                    //    };

                    //    _context.UserSubjects.Add(us);
                    //    _context.SaveChanges();
                    //}
                    _context.Database.CommitTransaction();


                    returnCode = newUserId;
                }
                catch (Exception ex)
                {
                    //throw new Exception(ex.Message);
                    _context.Database.RollbackTransaction();
                    returnMessage = ex.InnerException.Message.ToString();
                    returnCode = -111;
                    return null;
                }
            }
            newReturnUser = _common.GetUserById(newUserId);

            return newReturnUser;
        }

        /// <summary>
        /// Register Student
        /// </summary>
        /// <param name="user"></param>
        /// <param name="returnCode"></param>
        /// <param name="returnMessage"></param>
        /// <returns></returns>
        public LoginViewModel SignUpStudent(ClaimsIdentity identity, out string returnMessage)
        {
            int newUserId = 0;
            returnMessage = string.Empty;

            string uuid = Security.GetValueFromToken("user_id", identity);

            Users user = _context.Users.Where(u => u.Uuid == uuid).FirstOrDefault();
            if (user != null)
            {
                if (user.IsActive == false) return null;
                return _common.GetLoginDetails(user.Id);
            }

            string email_id = Security.GetValueFromToken(ClaimTypes.Email, identity);
            string name = Security.GetValueFromToken(ClaimTypes.Name, identity);

            using (var tx = _context.Database.BeginTransaction())
            {
                try
                {
                    Users newUser = new Users
                    {
                        InstituteId = 1,
                        RoleId = 4,
                        Uuid = uuid,
                        EmailId = email_id,
                        FirstName = name,
                    };
                    
                    _context.Users.Add(newUser);
                    _context.SaveChanges();

                    newUserId = newUser.Id;

                    _context.Database.CommitTransaction();

                }
                catch (Exception ex)
                {
                    //throw new Exception(ex.Message);
                    _context.Database.RollbackTransaction();
                    returnMessage = ex.InnerException.Message.ToString();
                    return null;
                }
            }
            return _common.GetLoginDetails(newUserId);
        }

        /// <summary>
        /// Add  Admin 
        /// </summary>
        /// <param name="user"></param>
        /// <param name="returnCode"></param>
        /// <param name="returnMessage"></param>
        /// <returns></returns>
        public dynamic AddAdminTeacher(int institute_id, string emailId, int roleId, out string returnMessage)
        {
            int newUserId = 0;
            returnMessage = string.Empty;

            Users user = _context.Users.Where(u => u.EmailId == emailId).FirstOrDefault();
            if (user != null)
            {
                if (user.IsActive == false && user.InstituteId != institute_id) return (int)UserMessage.InvalidUser;
                if (user.InstituteId != institute_id) return (int)Message.NotAllowedToResource;
                if (user.RoleId == roleId) return (int)UserMessage.UserAlreadyExists;                                
            }

            using (var tx = _context.Database.BeginTransaction())
            {
                try
                {
                    if (user != null)
                    {
                        _context.Database.BeginTransaction();
                        user.RoleId = roleId;
                        _context.Users.Update(user);
                        _context.SaveChanges();
                        _context.Database.CommitTransaction();
                    }
                    else
                    {
                        Users newUser = new Users
                        {
                            InstituteId = institute_id,
                            RoleId = roleId,
                            EmailId = emailId
                        };

                        _context.Users.Add(newUser);
                        _context.SaveChanges();

                        newUserId = newUser.Id;

                        _context.Database.CommitTransaction();
                    }
                }
                catch (Exception ex)
                {
                    //throw new Exception(ex.Message);
                    _context.Database.RollbackTransaction();
                    returnMessage = ex.InnerException.Message.ToString();
                    return null;
                }
            }
            return _common.GetLoginDetails(newUserId);
        }


        /// <summary>
        /// Register Student
        /// </summary>
        /// <param name="user"></param>
        /// <param name="returnCode"></param>
        /// <param name="returnMessage"></param>
        /// <returns></returns>
        public LoginViewModel SignUpAdmin(ClaimsIdentity identity, out string returnMessage)
        {
            int newUserId = 0;
            returnMessage = string.Empty;

            string uuid = Security.GetValueFromToken("user_id", identity);
            
            Users user = _context.Users.Where(u => u.Uuid == uuid).FirstOrDefault();
            if (user != null)
            {
                if (user.RoleId != 3 || user.IsActive == false)
                {
                    user.RoleId = 3;
                    user.IsActive = true;

                    _context.Users.Update(user);
                    _context.SaveChanges();
                }
                return _common.GetLoginDetails(user.Id);
            }

            using (var tx = _context.Database.BeginTransaction())
            {
                try
                {
                    string email_id = Security.GetValueFromToken(ClaimTypes.Email, identity);
                    string name = Security.GetValueFromToken(ClaimTypes.Name, identity);


                    Users newUser = new Users
                    {
                        InstituteId = 1,
                        RoleId = 3,
                        Uuid = uuid,
                        EmailId = email_id,
                        FirstName = name,
                    };

                    _context.Users.Add(newUser);
                    _context.SaveChanges();

                    newUserId = newUser.Id;

                    _context.Database.CommitTransaction();

                }
                catch (Exception ex)
                {
                    //throw new Exception(ex.Message);
                    _context.Database.RollbackTransaction();
                    returnMessage = ex.InnerException.Message.ToString();
                    return null;
                }
            }
            return _common.GetLoginDetails(newUserId);
        }


        /// <summary>
        /// Signin Admin/Teacher
        /// </summary>
        /// <param name="user"></param>
        /// <param name="returnCode"></param>
        /// <param name="returnMessage"></param>
        /// <returns></returns>
        public dynamic SignInAdminTeacher(ClaimsIdentity identity, out string returnMessage)
        {
            returnMessage = string.Empty;

            string uuid = Security.GetValueFromToken("user_id", identity);
            Users user = _context.Users.Where(u => u.Uuid == uuid).FirstOrDefault();
            if (user != null)
            {
                if (user.IsActive == false)
                    return (int)UserMessage.InvalidUser;

                if (user.RoleId == 2 || user.RoleId == 3)
                    return _common.GetLoginDetails(user.Id);
                else
                {
                    return (int)Message.NotAllowedToResource;
                }
            }
            
            string email_id = Security.GetValueFromToken("emailaddress", identity);
            user = _context.Users.Where(u => u.EmailId == email_id && (u.RoleId == 2 || u.RoleId == 3) && u.IsActive == true).FirstOrDefault();
            if(user != null)
            {
                try
                {
                    _context.Database.BeginTransaction();
                    user.Uuid = uuid;
                    _context.Users.Update(user);
                    _context.SaveChanges();
                    _context.Database.CommitTransaction();

                    return _common.GetLoginDetails(user.Id);
                }
                catch
                {
                    _context.Database.RollbackTransaction();
                    return null;
                }
            }
            return null;
        }


        /// <summary>
        /// Get user by id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public UserViewModel GetUserById(int id)
        {
            return _common.GetUserById(id);
        }

        /// <summary>
        /// Get List of users of an institution
        /// </summary>
        /// <param name="instituteId">Id of institute for which the list of students needed</param>
        /// <returns>List of users of the institute</returns>
        public List<UserViewModel> GetUsersOfAnInstitution(int instituteId)
        {
            List<UserViewModel> userList = new List<UserViewModel>();
            //Check if the user is trying to login with username
            var users = _context.Users.AsNoTracking().Where(u => u.InstituteId == instituteId).ToList();
            if (users == null || users.Count.Equals(0))
            {
                return null;
            }
            else
            {
                for (int x = 0; x <= users.Count - 1; x++)
                {
                    UserViewModel usr = _common.GetUserById(users[0].Id);
                    userList.Add(usr);
                }
            }
            return userList;
        }

        //===========================================================================================================================
        // User registration
        //===========================================================================================================================

        public int RegisterUser(StudentAddModel user, out int returnCode, out string returnMessage)
        {
            returnMessage = string.Empty;
            returnCode = -1;

            Users theUser = _context.Users.FirstOrDefault(u => u.EmailId == user.EmailId);
            if(theUser != null && theUser.IsActive == true)
            {
                returnMessage = "User already exists";
                returnCode = (int)UserMessage.UserAlreadyExists;
                return returnCode;
            }

            int newUserId = 0;

            if (theUser == null)
            {
                using (var tx = _context.Database.BeginTransaction())
                {
                    try
                    {
                        string activationCode = Guid.NewGuid().ToString();
                        Users newUser = new Users
                        {
                            FirstName = "Preet1",
                            InstituteId = 1, //HARDCODED considering our instituteID would be 1
                            RoleId = 4, //HARDCODED as this is for student registration
                            EmailId = user.EmailId,
                            UserSalt = Security.GetNewSalt(8),
                            CreatedOn = _common.NowIndianTime(),
                            IsActive = false, //TODO: create a new filed called email verified and use that
                            Address = activationCode //TODO: create a new filed for activation code 
                        };
                        newUser.UserPassword = Security.GetSaltedHashPassword(newUser.UserSalt, user.UserPassword);

                        _context.Users.Add(newUser);
                        _context.SaveChanges();

                        newUserId = newUser.Id;

                        _context.Database.CommitTransaction();

                    }
                    catch (Exception ex)
                    {
                        //throw new Exception(ex.Message);
                        _context.Database.RollbackTransaction();
                        returnMessage = ex.InnerException.Message.ToString();
                        returnCode = -111;
                        return returnCode;
                    }
                }
            }

            theUser = _context.Users.FirstOrDefault(u => u.Id.Equals(newUserId));

            if (theUser == null)
            {
                returnMessage = "Registration has been Faild";
                returnCode = (int)Message.ObjectNotAdded;
                return returnCode;
            }

            int link = -1;

            link = SendVerificationLinkEmail(theUser.EmailId, theUser.Address.ToString(), DOMAIN.SCHEME, DOMAIN.HOST, DOMAIN.PORT);
            if (link < 0)
            {
                returnMessage = "Registration has been Faild";
                returnCode = (int)Message.ObjectNotAdded;
                return returnCode;
            }
            returnMessage = "Registration has been done,And Account activation link" + "has been sent your eamil id:" + user.EmailId;
            returnCode = newUserId;

            return returnCode;
            
        }

        public int SendVerificationLinkEmail(string emailId, string activationcode, string scheme, string host, string port)
        {
            var varifyUrl = scheme + "://" + host + ":" + port + "/Student/ActivateAccount/" + activationcode;
            var fromMail = new MailAddress("preetsagarparida@gmail.com", "Welcome User");
            var toMail = new MailAddress(emailId);
            var frontEmailPassowrd = "Odiware@123";
            string subject = "Your account is successfull created";
            string body = "<br/><br/>We are excited to tell you that your account is" +
                  " successfully created. Please click on the below link to verify your account" +
                  " <br/><br/><a href='" + varifyUrl + "'>" + varifyUrl + "</a> ";

            try
            {
                var smtp = new SmtpClient
                {
                    Host = "smtp.gmail.com",
                    Port = 587,
                    EnableSsl = true,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    UseDefaultCredentials = false,
                    Credentials = new NetworkCredential(fromMail.Address, frontEmailPassowrd)

                };
                using (var message = new MailMessage(fromMail, toMail)
                {
                    Subject = subject,
                    Body = body,
                    IsBodyHtml = true
                })

                    smtp.Send(message);
            }
            catch (Exception ex)
            {
                return -1;
            }

            return 1;
         }

      public string SendEmailLink(string emailId, string link)
        {
            var varifyUrl = link;
            var fromMail = new MailAddress("preetsagarparida@gmail.com", "Welcome User");
            var toMail = new MailAddress(emailId);
            var frontEmailPassowrd = "yhzdqwotiyuhzxch";
            string subject = "Verify your account !!!";
            string body = "<br/><br/>We are excited to tell you that your account is" +
                  " successfully created. Please click on the below link to verify your account" +
                  " <br/><br/><a href='" + varifyUrl + "'>" + varifyUrl + "</a> ";

            try
            {
                var smtp = new SmtpClient
                {
                    Host = "smtp.gmail.com",
                    Port = 587,
                    EnableSsl = true,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    UseDefaultCredentials = false,
                    Credentials = new NetworkCredential(fromMail.Address, frontEmailPassowrd)

                };
                using (var message = new MailMessage(fromMail, toMail)
                {
                    Subject = subject,
                    Body = body,
                    IsBodyHtml = true
                })

                    smtp.Send(message);
            }
            catch (Exception ex)
            {
                return ex.Message;
            }

            return "True";
         }


        public int VerifyAccount(string code, out string returnMessage)
        {
            returnMessage = string.Empty;

            string str = "";

            try
            {
                var user = _context.Users.Where(a => a.Address == new Guid(code).ToString()).FirstOrDefault();
                if (user != null && user.IsActive == false)
                {
                    _context.Database.BeginTransaction();
                    user.IsActive = true;
                    _context.SaveChanges();
                    _context.Database.CommitTransaction();
                    returnMessage = "Email successfully activated now you can able to login";
                }
                else if(user != null && user.IsActive == true)
                {
                    returnMessage = "Email already activated";
                    return -1;
                }
                else
                {
                    returnMessage = "Email not activated";
                    return -1;
                }

                return user.Id;
            }
            
            catch (Exception ex)
            {
                returnMessage = "Email not activated";
                return -1;
            }
        }

        public string ConvertToSlug(string Name)
        {
            return _common.convertToSlug(Name);            
        }

        public bool isUniqueSlug(string Name)
        {
            List<Institutes> listInst = _context.Institutes.Where(i => i.Domain == Name).ToList();
            if (listInst != null && listInst.Count > 0)
                return false;
            
            return true;
        }

        public bool isUniqueEmail(string email)
        {
            int bEmailId = _context.Users.Where(u => u.EmailId == email).ToList().Count;
            if (bEmailId > 0) return false;

            return true;
        }

        public Users SignUpDB(UserRecord record, string domainName)
        {
            try
            {
                //Verify email id and name exists
                int bEmailId = _context.Users.Where(u => u.EmailId == record.Email).ToList().Count;
                if (bEmailId > 0) return null;
                int bOrgName = _context.Institutes.Where(i => i.Domain == domainName && i.IsActive == true).ToList().Count;
                if (bOrgName > 0) return null;

                Institutes newInstitute = new Institutes
                {
                    Name = domainName,
                    Domain = domainName,
                    IsActive = true,
                    CreatedOn = DateTime.Now,
                    UpdatedOn = DateTime.Now
                };
                _context.Database.BeginTransaction();
                _context.Institutes.Add(newInstitute);
                _context.SaveChanges();
                _context.Database.CommitTransaction();

                //Create user row in our DB
                Users newUser = new Users
                {
                    FirstName = record.DisplayName,
                    InstituteId = _context.Institutes.Where(i => i.Domain == domainName).FirstOrDefault().Id, 
                    RoleId = 2, //HARDCODED as this is for admin registration
                    EmailId = record.Email,
                    Uuid = record.Uid,
                    CreatedOn = _common.NowIndianTime(),
                    IsActive = true
                };
                 
                _context.Database.BeginTransaction();
                _context.Users.Add(newUser);
                _context.SaveChanges();

                int newUserId = newUser.Id;

                _context.Database.CommitTransaction();


                // Assign custom claims
                //Security.GetFirebaseTokenAsync(userRecord.Uid, newUserId, roleID, instituteId);

                // Return the response
                return _context.Users.Where(u => u.Id == newUserId).FirstOrDefault();
            }
            catch (Exception ex)
            {
                return null;
            }
        }


        public Users CreateUser(UserRecord record, int institute_id, int roleId)
        {
            try
            {
                //Verify email id and name exists
                int bEmailId = _context.Users.Where(u => u.EmailId == record.Email).ToList().Count;
                if (bEmailId > 0) return null;
                
                
                //Create user row in our DB
                Users newUser = new Users
                {
                    FirstName = record.DisplayName,
                    InstituteId = institute_id,
                    RoleId = roleId, //Student/teacher
                    EmailId = record.Email,
                    Uuid = record.Uid,
                    CreatedOn = _common.NowIndianTime(),
                    IsActive = true
                };

                _context.Database.BeginTransaction();
                _context.Users.Add(newUser);
                _context.SaveChanges();

                int newUserId = newUser.Id;

                _context.Database.CommitTransaction();

                // Return the response
                return _context.Users.Where(u => u.Id == newUserId).FirstOrDefault();
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        //Update User detail
        public int updateOdooUser(int userId, int odooEmpId)
        {
            
            try
            {
                Users user = _context.Users.Where(u => u.Id == userId && u.IsActive == true).FirstOrDefault();
                if (user == null) return -1;

                _context.Database.BeginTransaction();
                user.RegistrationId = odooEmpId;
                _context.Users.Update(user);
                _context.SaveChanges();
                _context.Database.CommitTransaction();
            }
            catch (Exception ex)
            {
                _context.Database.RollbackTransaction();
                return -1;
            }

            return odooEmpId;
        }

        //Update Institute detail
        public int updateOdooCompany(int instituteId, int odooCompanyId)
        {
            try
            {
                Institutes institutes = _context.Institutes.Where(i => i.Id == instituteId && i.IsActive == true).FirstOrDefault();
                if (institutes == null) return -1;

                _context.Database.BeginTransaction();
                institutes.ApiKey = odooCompanyId.ToString();
                _context.Institutes.Update(institutes);
                _context.SaveChanges();
                _context.Database.CommitTransaction();
            }
            catch (Exception ex)
            {
                _context.Database.RollbackTransaction();
                return -1;
            }

            return odooCompanyId;
        }


        //Get Odoo user ID
        public int getOdooEmpID(int userId)
        {
            int odooEmpId = -1;

            try
            {
                Users user = _context.Users.Where(u => u.Id == userId && u.IsActive == true).FirstOrDefault();
                if (user == null) return -1;
                if(user.RegistrationId != null)
                    odooEmpId = (int)user.RegistrationId;
            }
            catch (Exception ex)
            {
                return -1;
            }

            return odooEmpId;
        }

        //Get Odoo user ID
        public int getOdooEmpID(string uuid)
        {
            int odooEmpId = -1;

            try
            {
                Users user = _context.Users.Where(u => u.Uuid == uuid && u.IsActive == true).FirstOrDefault();
                if (user == null) return -1;
                if (user.RegistrationId != null)
                    odooEmpId = (int)user.RegistrationId;
            }
            catch (Exception ex)
            {
                return -1;
            }

            return odooEmpId;
        }

        //Get Odoo user ID
        public List<int> getOdooEmpIDList(List<int> userIdList)
        {
            List<int> odooEmpId = new List<int>();

            try
            {
                var users = _context.Users.Where(u => userIdList.Contains(u.Id) && u.IsActive == true).ToList();
                if (users == null || users.Count == 0)
                    return new List<int>();

                var registrationIds = users
                        .Where(u => u.RegistrationId.HasValue) // Filter out invalid keys (assuming 0 is invalid)
                        .Select(u => u.RegistrationId.Value)
                        .ToList();
                
                return registrationIds;
            }
            catch (Exception ex)
            {
                return null;
            }

        }

        //get Odoo Company ID
        public int getOdooCompanyID(int instituteId)
        {
            int odooCompanyId = -1;

            try
            {
                Institutes institutes = _context.Institutes.Where(i => i.Id == instituteId && i.IsActive == true).FirstOrDefault();
                if (institutes == null) return -1;
                if (institutes.ApiKey != null)
                    odooCompanyId = int.Parse(institutes.ApiKey);
            }
            catch (Exception ex)
            {
                return -1;
            }

            return odooCompanyId;
        }

        public bool isValidUser(int instituteId, int userId)
        {
            try
            {
                Users user = _context.Users.Where(u => u.Id == userId && u.IsActive == true).FirstOrDefault();
                if (user == null) return false;
                if (user.InstituteId != instituteId) return false;

            }
            catch (Exception ex)
            {
                return false;
            }

            return true;

        }

        //This endpoint allows you to associate question to subcategories
        public ClassStructureViewModel AttachMeToUserGroup(int institute_id, int user_group_id, int user_id, bool isDefault, out string return_message)
        {
            int return_value = 0;
            ClassStructureViewModel csvm;

            UserGroups ug = _context.UserGroups.Where(u => u.Id == user_group_id).FirstOrDefault();
            return_message = string.Format("0 users attached the usergroup");
            if (ug == null) return null;

            try
            {
                List<UserGroupMembers> userGroupMembersList = new List<UserGroupMembers>();
                bool isUser = _common.isUser(institute_id, user_id);
                
                if (isUser == false)
                    return null;
                else
                {
                    UserGroupMembers um = _common.GetUserGroupMembers(user_group_id, user_id);
                    _context.Database.BeginTransaction();
                    if (um == null)
                    {
                        UserGroupMembers newMember = new UserGroupMembers
                        {
                            UserId = user_id,
                            UserGroupId = user_group_id,
                            IsActive = true,
                            CreatedOn = _common.NowIndianTime(),
                            UpdatedOn = _common.NowIndianTime()
                        };
                        _context.UserGroupMembers.Add(newMember);
                    }

                    if (isDefault == true)
                    {
                        Users user = _context.Users.Where(u => u.Id == user_id && u.IsActive == true).FirstOrDefault();
                        if(user.BatchId != user_group_id)
                        {
                            user.BatchId = user_group_id;
                            _context.Users.Update(user);
                        }                            
                    }

                    _context.SaveChanges();
                    _context.Database.CommitTransaction();
                }

                return_message = string.Format("user attached the usergroup");
            }
            catch (Exception ex)
            {
                _context.Database.RollbackTransaction();
                return_value = -1;
                return_message = ex.InnerException.ToString();
            }


            csvm = GetClassStructurebyId(institute_id, user_group_id);

            return csvm;
        }


        public ClassStructureViewModel GetClassStructurebyId(int institute_id, int id)
        {
            ClassStructureViewModel cStruct = new ClassStructureViewModel();

            UserGroups ug = _context.UserGroups.Where(g => g.Id == id && g.IsActive == true).FirstOrDefault();
            if (ug == null) return null;

            cStruct.id = ug.Id;
            cStruct.name = ug.Name;


            if (cStruct == null) return null;

            cStruct.subjects = (from s in _context.Subjects
                                where s.ClassId == ug.ClassId && s.IsActive == true

                                select new SubjectModel
                                {
                                    id = s.Id,
                                    name = s.Name,
                                    topics = (from c in _context.Categories where c.SubjectId == s.Id && c.IsActive == true select new TopicModel { id = c.Id, name = c.Name }).ToList()
                                }).ToList();


            return cStruct;
        }


        public int DetachUserGroup(int institute_id, int user_id, int user_group_id, out string return_message)
        {
            try
            {
                List<UserGroupMembers> iList = (from m in _context.UserGroupMembers
                                            join u in _context.UserGroups on m.UserGroupId equals u.Id
                                            join c in _context.Classes on u.ClassId equals c.Id
                                         
                                            where m.IsActive == true && m.UserId == user_id && 
                                            u.IsActive == true && c.InstituteId == institute_id 
                                                
                                                select new UserGroupMembers()
                                     ).ToList();


                if(iList == null || iList.Count < 2)
                {
                    return_message = Message.MustGreaterThanZero.ToString();
                    return (int)Message.MustGreaterThanZero;
                }

                List<UserGroupMembers> memList = iList.Where(m => m.UserGroupId == user_group_id).ToList();
                if(memList == null || memList.Count == 0)
                {
                    return_message = Message.ObjectNotFound.ToString();
                    return (int)Message.ObjectNotFound;
                }
                
                _context.BulkDelete(memList);
                return_message = string.Format("Usergroup deleted.");
            }
            catch (Exception ex)
            {
                return_message = ex.InnerException.ToString();
                return (int)Message.FailedToDelete;
            }

            return_message = Message.SucessfullyDeleted.ToString();
            return (int)Message.SucessfullyDeleted;
        }


        public List<TeacherViewModel> GetTeachersOfTheInstitution(int institute_id, int batch_id, int author_id, string sortBy, string sortOrder)
        {
            List<TeacherViewModel> teacherList = new List<TeacherViewModel>();

            UserGroups ug = _context.UserGroups.Where(g => g.Id == batch_id && g.IsActive == true).FirstOrDefault();
            if (ug == null) return null;
            int class_id = ug.ClassId;

            List<Users> users = _context.Users.Where(u => u.InstituteId == institute_id && (u.RoleId == 3 || u.RoleId == 2) && u.IsActive == true).ToList();

            if(author_id > 0)
            {
                users = users.Where(x => x.Id == author_id).ToList();
            }

            List<int> exams;
            List<int> practices;

            foreach (Users u in users)
            {
                TeacherViewModel teacher = new TeacherViewModel();
                int examLikes = 0;
                int practiceLikes = 0;
                int examPlays = 0;
                int practicePlays = 0;

                teacher.id = u.Id;
                teacher.name = u.FirstName + " " + u.LastName;

                // this is irrespective of batch id
                //exams = _context.Exams.Where(e => e.InstituteId == institute_id && e.CreatedBy == u.Id && e.ExamStatus == StatusCode.PUBLISHED.ToString() && e.IsActive == true).Select(e => e.Id).ToList();
                //practices = _context.Practices.Where(p => p.InstituteId == institute_id && p.CreatedBy == u.Id && p.Status == StatusCode.PUBLISHED.ToString() && p.IsActive == true).Select(p => p.Id).ToList();

                exams = (from e in _context.Exams
                         join uge in _context.UserGroupExams on e.Id equals uge.ExamId

                         where e.InstituteId == institute_id && e.CreatedBy == u.Id && e.ExamStatus == StatusCode.PUBLISHED.ToString() && e.ExamCloseDatetime > _common.NowIndianTime() && e.IsActive == true
                         && uge.IsActive == true && uge.UserGroupId == batch_id

                         select e.Id).ToList();

                practices = (from p in _context.Practices
                             join ugp in _context.UserGroupPractices on p.Id equals ugp.PracticeId

                             where p.InstituteId == institute_id && p.CreatedBy == u.Id && p.Status == StatusCode.PUBLISHED.ToString() && p.IsActive == true
                             && ugp.IsActive == true && ugp.UserGroupId == batch_id

                             select p.Id).ToList();

                if (exams.Count == 0 && practices.Count == 0) continue;

                teacher.total_exams = exams.Count();
                teacher.total_practices = practices.Count();

                examLikes = _context.BookmarkedExams.Where(be => be.IsActive == true && exams.Contains(be.ExamId)).Count();
                practiceLikes = _context.BookmarkedPractices.Where(pe => pe.IsActive == true && practices.Contains(pe.PracticeId)).Count();

                examPlays = _context.ExamAttempts.Where(ea => ea.IsActive == true && exams.Contains(ea.ExamId)).Count();
                practicePlays = _context.PracticeAttempts.Where(pa => pa.IsActive == true && practices.Contains(pa.PracticeId)).Count();

                teacher.total_likes = examLikes + practiceLikes;
                teacher.total_plays = examPlays + practicePlays;
                teacherList.Add(teacher);
            }

            //Sorting 
            if (sortOrder == null || sortOrder.ToUpper().Equals("A"))
            {
                if (sortBy == null)
                {
                    teacherList = teacherList.OrderBy(a => a.total_plays).ToList();
                }
                else if (sortBy.ToUpper().Equals("LIKES"))
                {
                    teacherList = teacherList.OrderBy(a => a.total_likes).ToList();
                }
            }
            else if (sortOrder != null && sortOrder.ToUpper().Equals("D"))
            {
                if (sortBy == null)
                {
                    teacherList = teacherList.OrderByDescending(a => a.total_plays).ToList();
                }
                else if (sortBy.ToUpper().Equals("LIKES"))
                {
                    teacherList = teacherList.OrderByDescending(a => a.total_likes).ToList();
                }
            }

            return teacherList;
        }


        public List<PlanViewModel> GetPlans(int institute_id, string sortBy, string sortOrder)
        {
            List<PlanViewModel> planList = (from p in _context.Plans
                                            where p.InstituteId == institute_id && p.Status == StatusCode.PUBLISHED.ToString() && p.IsActive == true
                                            select new PlanViewModel
                                            {
                                                code = p.Code,
                                                name = p.Name,
                                                description = p.Description,
                                                paid_exams = (int)p.PaidExams,
                                                paid_practices = (int)p.PaidPractices,
                                                duration_days = (int)p.DurationDays,
                                                initial_price = (int)p.InitialPrice,
                                                final_price = (int)p.FinalPrice,
                                                last_updated = (DateTime)p.UpdatedOn
                                            }
                         ).ToList();

            if ((sortOrder != null && sortOrder.Length > 0) || (sortBy != null && sortBy.Length > 0))
            {
                if (sortOrder != null && sortOrder.Equals("D"))
                {
                    switch (sortBy.ToUpper())
                    {
                        case "NAME": planList = planList.OrderByDescending(a => a.name).ToList(); break;
                        case "UPDATED_ON": planList = planList.OrderByDescending(a => a.last_updated).ToList(); break;
                    }

                }
                else
                {
                    switch (sortBy.ToUpper())
                    {
                        case "NAME": planList = planList.OrderBy(a => a.name).ToList(); break;
                        case "UPDATED_ON": planList = planList.OrderBy(a => a.last_updated).ToList(); break;
                    }
                }
            }

            return planList;
        }


        public int GetPlanIdByCode(int institute_id, string code)
        {
            Plans plan = _common.GetPlanByCode(institute_id, code);

            if (plan == null) return -1;
            
            return plan.Id;
        }

        
        public PlanViewModel GetPlanByCode(int institute_id, string code)
        {
            PlanViewModel plan = (from p in _context.Plans
                                            where p.InstituteId == institute_id && p.Code == code &&
                                                p.Status == StatusCode.PUBLISHED.ToString() && 
                                                p.IsActive == true

                                            select new PlanViewModel
                                            {
                                                code = p.Code,
                                                name = p.Name,
                                                description = p.Description,
                                                paid_exams = (int)p.PaidExams,
                                                paid_practices = (int)p.PaidPractices,
                                                duration_days = (int)p.DurationDays,
                                                initial_price = (int)p.InitialPrice,
                                                final_price = (int)p.FinalPrice,
                                                last_updated = (DateTime)p.UpdatedOn
                                            }
                         ).FirstOrDefault();


            return plan;
        }

        public dynamic CreateOrder(int user_id, int plan_id, float amount)
        {
            int returnCode = 0;

            try
            {
                RazorpayClient client = new RazorpayClient("rzp_test_T9n4ai2HS10jMs", "nApJhqrFery11ebXaGWSDoeO");

                Dictionary<string, object> options = new Dictionary<string, object>();
                options.Add("amount", amount * 100); // amount in the smallest currency unit
                options.Add("receipt", "order_rcptid_11");
                options.Add("currency", "INR");
                Order order = client.Order.Create(options);

                string order_id = order["id"];
                
                _context.Database.BeginTransaction();

                Orders newOrder = new Orders
                {
                    OrderId = order_id,
                    Amount = amount,
                    PlanId = plan_id,
                    Status = StatusCode.DRAFT.ToString(),
                    IsActive = true,
                    CreatedBy = user_id,
                    UpdatedBy = user_id,
                    CreatedOn = _common.NowIndianTime(),
                    UpdatedOn = _common.NowIndianTime()
                };

                _context.Orders.Add(newOrder);
                _context.SaveChanges();
                _context.Database.CommitTransaction();

                return order_id;
            }
            catch (Exception ex)
            {
                //throw new Exception(ex.Message);
                _context.Database.RollbackTransaction();
                returnCode = -111;
                return returnCode;
            }

        }

        public dynamic verifyOrder(int user_id, string order_id)
        {
            try
            {
                Orders order = _context.Orders.Where(o => o.OrderId== order_id &&
                                                o.Status == StatusCode.DRAFT.ToString() &&
                                                o.CreatedBy == user_id &&
                                                o.IsActive == true ).FirstOrDefault();

                if (order == null || order.OrderId == null)
                    return (int)Message.NoData;

                return order.OrderId.ToString();
            }
            catch (Exception ex)
            {
                return (int)Message.NoData;
            }
        }

        public dynamic GetCurrentSubscriptionDetails(int institute_id, int user_id)
        {
            SubscriptionViewModel svm = _common.mySubscriptionDetails(user_id);
            if (svm == null)
                return (int)Message.NoValidSubscription;

            return svm;
        }

        public dynamic CancelCurrentSubscription(int institute_id, int user_id)
        {
            DateTime now = _common.NowIndianTime();
            Subscriptions sub = _context.Subscriptions.Where(s => s.UserId == user_id && s.IsActive == true &&
                    s.IsCancelled == false && s.StartDate <= now && s.EndDate >= now).FirstOrDefault();

            if (sub == null)
                return true;

            sub.IsCancelled = true;
            sub.CancelledDate = _common.NowIndianTime();

            _context.Subscriptions.Update(sub);

            return true;

        }

        public dynamic createSubscription(int institute_id, int user_id, Dictionary<string, string> attributes)
        {
            try
            {
                _context.Database.BeginTransaction();

                string order_id = attributes["razorpay_order_id"];

                Orders order = _context.Orders.Where(o => o.OrderId == order_id).FirstOrDefault();
                if (order == null)
                    return (int)Message.InvalidInput;

                Plans plan = _context.Plans.Where(p => p.Id == order.PlanId).FirstOrDefault();
                if(plan == null)
                    return (int)Message.Failure;

                Subscriptions s = _context.Subscriptions.Where(c => c.PgOrderId == order_id).FirstOrDefault();
                if(s != null)
                    return (int)Message.AlreadyExist;

                bool isCancelled = CancelCurrentSubscription(institute_id, user_id);

                Subscriptions subs = new Subscriptions
                {
                    PlanId = order.PlanId,
                    CreatedOn = _common.NowIndianTime(),
                    CreatedBy = user_id,
                    UpdatedBy = user_id,
                    UpdatedOn = _common.NowIndianTime(),
                    IsActive = true,
                    PgOrderId = attributes["razorpay_order_id"],
                    PgPaymentId = attributes["razorpay_payment_id"],
                    PgSignature = attributes["razorpay_signature"],
                    UserId = user_id,
                    Status = StatusCode.PUBLISHED.ToString(),
                    StartDate = _common.NowIndianTime(),
                    EndDate = _common.NowIndianTime().AddDays((double)plan.DurationDays),
                    TotalExamCredits = (short)plan.PaidExams,
                    RemainingExamCredits = (short)plan.PaidExams,
                    TotalPracticeCredits = (short)plan.PaidPractices,
                    RemainingPracticeCredits = (short)plan.PaidPractices,
                    IsCancelled = false
                };

                _context.Subscriptions.Add(subs);
                _context.SaveChanges();

                _context.Database.CommitTransaction();

                SubscriptionViewModel svm = GetCurrentSubscriptionDetails(institute_id, user_id);

                return svm;

            }
            catch (Exception ex)
            {
                _context.Database.RollbackTransaction();

                return (int)Message.NoData;
            }
        }
    }
}
