using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Configuration;
using OnlineAssessment.Common;
using OnlineAssessment.Data.EFCore;
using OnlineAssessment.Domain.Models;
using OnlineAssessment.Domain.ViewModels;
using Razorpay.Api;

namespace OnlineAssessment.V1.Controllers
{
    [Route("v{version:apiVersion}/[controller]")]
    [ApiController]
    [ApiVersion("1.0")]
    public class UsersController : BaseController<Users, EfCoreUserRepository>
    {
        private readonly IConfiguration _config;
        EfCoreUserRepository _repository;
        string responseMessage;
        public UsersController(EfCoreUserRepository repository, IConfiguration config) : base(repository)
        {
            _repository = repository;
            _config = config;
        }


        /// <summary>
        /// Create a new user
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        public IActionResult SignUp([FromBody] UserAddModel user)
        {
            int returnCode = 0;
            string returnMessage = string.Empty;
            IActionResult returnResponse;
            UserViewModel newUser = _repository.SignUp(user, out returnCode, out returnMessage);
            if (newUser != null)
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(newUser as dynamic));
            else
            {
                responseMessage = _repository.GetMessageByCode(Message.ObjectNotAdded.ToString(), Constant.User);
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage + " " + returnMessage));
            }

            return returnResponse;
        }

        /// <summary>
        /// User Log in 
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("[Action]")]
        [Authorize]
        public async System.Threading.Tasks.Task<IActionResult> SignIn()
        {
            string returnMessage = string.Empty;
            IActionResult returnResponse;
            int role_id = -1;

            ClaimsIdentity identity = HttpContext.User.Identity as ClaimsIdentity;

            string role = Security.GetValueFromToken("RoleId", identity);
            if (role == null) role_id = -1;
            else role_id = int.Parse(role);

            if (role_id > 0 && role_id != 4)
            {
                responseMessage = _repository.GetMessageByCode(Message.NotAllowedToResource.ToString(), Constant.User);
                returnResponse = BadRequest(ReturnResponse.GetFailureStatus((int)Message.NotAllowedToResource, responseMessage));
                return returnResponse;
            }

            string email_verify = Security.GetValueFromToken("email_verified", identity);
            if (email_verify != "true")
            {
                responseMessage = _repository.GetMessageByCode(Message.AuthenticationFailed.ToString(), Constant.User);
                returnResponse = BadRequest(ReturnResponse.GetFailureStatus((int)Message.AuthenticationFailed, responseMessage));
                return returnResponse;
            }

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

            
            LoginViewModel login = _repository.SignUpStudent(identity, out returnMessage);
            if(login != null)
            {
                string token = await Security.GetFirebaseTokenAsync(uuid, login.id, login.role_id, login.institute_id);
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(login));
            }
                
            else
            {
                responseMessage = _repository.GetMessageByCode(Message.NotAllowedToResource.ToString(), Constant.User);
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage + " " + returnMessage));
            }

            return returnResponse;
        }
/*
        /// <summary>
        /// User Log in 
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("[Action]")]
        [Authorize]
        public async System.Threading.Tasks.Task<IActionResult> SignUpAdmin()
        {
            string returnMessage = string.Empty;
            IActionResult returnResponse;

            ClaimsIdentity identity = HttpContext.User.Identity as ClaimsIdentity;
            string uuid = Security.GetValueFromToken("user_id", identity);


            LoginViewModel login = _repository.SignUpAdmin(identity, out returnMessage);
            if (login != null)
            {
                string token = await Security.GetFirebaseTokenAsync(uuid, login.id, login.role_id);
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(login));
            }

            else
            {
                responseMessage = _repository.GetMessageByCode(Message.NotAllowedToResource.ToString(), Constant.User);
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage + " " + returnMessage));
            }

            return returnResponse;
        }

        /// <summary>
        /// Get All Users (accessible to SuperAdmin only)
        /// </summary>
        /// <returns>All Users of all the institutes</returns>
        [HttpGet]
        [Authorize(Roles = "SuperAdmin")]
        public override IActionResult GetAll()
        {

            IActionResult returnResponse;
            dynamic userList = _repository.GetUsersList();
            if (userList == null)
            {
                responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(userList));
            }
            return returnResponse;

        }


        /// <summary>
        /// Get details of an user  (accessible to SuperAdmin only)
        /// </summary>
        /// <param name="id">Id of the user</param>
        /// <returns>The user's information</returns>
        [HttpGet("{id}")]
        [Authorize(Roles = "SuperAdmin")]
        public override IActionResult Get(int id)
        {
            IActionResult returnResponse;
            dynamic entity = _repository.GetUserById(id);
            if (entity == null)
            {
                responseMessage = _repository.GetMessageByCode(Message.ObjectNotFound.ToString(), Constant.User);
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(entity));
            }
            return returnResponse;
        }

        /// <summary>
        /// Edit an user (accessible to SuperAdmin only)
        /// </summary>
        /// <param name="id">The id of the user to edit</param>
        /// <param name="userEdit">User's data to edit</param>
        /// <returns></returns>
        [HttpPut("{id}")]
        [Authorize(Roles = "SuperAdmin")]
        public IActionResult Put(int id, [FromBody] UserEditModel userEdit)
        {
            IActionResult returnResponse = null;
            if (id != userEdit.Id)
            {
                responseMessage = _repository.GetMessageByCode(Message.IdMismatchBetweenBodyAndQueryString.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
                return returnResponse;
            }
            else
            {
                string returnMessage = string.Empty;
                UserViewModel uvm = _repository.UpdateUser(id, userEdit, out returnMessage);
                if (uvm != null)
                {
                    returnResponse = Ok(ReturnResponse.GetSuccessStatus(uvm));
                }
                else
                {
                    responseMessage = _repository.GetMessageByCode(Message.ObjectNotUpdated.ToString(), Constant.User);
                    returnResponse = Ok(ReturnResponse.GetFailureStatus(new List<string> { responseMessage, returnMessage }));
                }
            }
            return returnResponse;
        }


        [HttpPost]
        [AllowAnonymous]
        [Route("RegUser")]
        public IActionResult RegisterUser(StudentAddModel data)
        {
            int returnCode = -1;
            string returnMessage = string.Empty;
            IActionResult returnResponse = null;
            int userID = -1;
            try
            {
                userID = _repository.RegisterUser(data, out returnCode, out returnMessage);
                if(userID > 0)
                {
                    responseMessage = _repository.GetMessageByCode(Message.SucessfullyAdded.ToString(), Constant.User);
                    returnResponse = Ok(ReturnResponse.GetSuccessStatus(responseMessage));
                }
                else if(userID == (int)UserMessage.UserAlreadyExists)
                {
                    responseMessage = _repository.GetMessageByCode(UserMessage.UserAlreadyExists.ToString(), Constant.User);
                    returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
                }
                else
                {
                    responseMessage = _repository.GetMessageByCode(Message.ObjectNotAdded.ToString(), Constant.User);
                    returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
                }
            }
            catch (ApplicationException ex)
            {
                responseMessage = _repository.GetMessageByCode(Message.ObjectNotAdded.ToString(), Constant.User);
                returnResponse = Ok(ReturnResponse.GetFailureStatus(new List<string> { responseMessage, ex.Message.ToString(), ex.InnerException.Message.ToString() }));
            }

            return returnResponse;
        }

        [HttpGet]
        [AllowAnonymous]
        [Route("VerifyAccount/{code}")]
        public IActionResult ActivateUser(string code)
        {
            string returnMessage = string.Empty;
            IActionResult returnResponse = null;
            int userID = -1;
            try
            {
                userID = _repository.VerifyAccount(code, out returnMessage);
                if (userID > 0)
                {
                    responseMessage = _repository.GetMessageByCode(Message.Success.ToString(), Constant.User);
                    returnResponse = Ok(ReturnResponse.GetSuccessStatus(responseMessage));
                }
                else
                {
                    responseMessage = _repository.GetMessageByCode(Message.NotAllowedToResource.ToString(), Constant.User);
                    returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
                }
            }
            catch (ApplicationException ex)
            {
                responseMessage = _repository.GetMessageByCode(Message.NotAllowedToResource.ToString(), Constant.User);
                returnResponse = Ok(ReturnResponse.GetFailureStatus(new List<string> { responseMessage, ex.Message.ToString(), ex.InnerException.Message.ToString() }));
            }

            return returnResponse;
        }
*/
        /// <summary>
        /// Update language
        /// </summary>
        /// <param name="language"></param>
        /// <returns></returns>
        [HttpPost("{language}/UpdatePreference")]
        [Authorize(Roles = "Student")]
        public IActionResult UpdatePreference(string language)
        {
            IActionResult returnResponse = null;
            string return_message = string.Empty;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);
            
            int language_id = _repository.GetLanguageIdByCode(language);

            if (language_id <= 0)
            {
                responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));

                return returnResponse;
            }

            //TODO: check if works fine
            int langId = _repository.UpdateMyLanguage(user_id, language_id, out return_message);
            if (langId < 0)
            {
                responseMessage = _repository.GetMessageByCode(Message.FailedToAttach.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));

                return returnResponse;
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(return_message));
            }

            return returnResponse;
        }

        /// <summary>
        /// Update user details
        /// </summary>
        /// <param name="profileDetailView"></param>
        /// <returns></returns>
        [HttpPost("UpdateMyDetails")]
        [Authorize(Roles = "Student")]
        public IActionResult UpdateMyDetails([FromBody] ProfileDetailView profileDetailView)
        {
            IActionResult returnResponse = null;
            string return_message = string.Empty;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            if (profileDetailView == null)
            {
                responseMessage = _repository.GetMessageByCode(Message.MustNotNull.ToString(), Constant.User);
                returnResponse = BadRequest(ReturnResponse.GetFailureStatus(responseMessage + " " + return_message));
                return returnResponse;
            }

            int id = _repository.UpdateMyDetails(user_id, profileDetailView, out return_message);
            if (id < 0 || id != user_id)
            {
                responseMessage = _repository.GetMessageByCode(Message.FailedToAttach.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(return_message));
            }

            return returnResponse;
        }

        /// <summary>
        /// Get user details
        /// </summary>
        /// <returns></returns>
        [HttpGet("MyDetails")]
        [Authorize(Roles = "Student")]
        public IActionResult GetMyDetails()
        {
            IActionResult returnResponse = null;
            string return_message = string.Empty;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            dynamic details = _repository.GetMyDetails(user_id, out return_message);
            if (details is ProfileDetailView && details != null)
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(details)); 
            }
            else
            {
                responseMessage = _repository.GetMessageByCode(Message.NotAllowedToResource.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }

            return returnResponse;
        }

        /// <summary>
        /// Attch me to usergroup
        /// </summary>
        /// <param name="user_group_id"></param>
        /// <param name="defaultGroup"></param>
        /// <returns></returns>
        [HttpPost("{user_group_id}/AttachBatch")]
        [Authorize(Roles = "Student")]
        public IActionResult AttachUserGroup(int user_group_id, [FromBody] DefaultGroup defaultGroup)
        {
            IActionResult returnResponse = null;
            string return_message = string.Empty;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            //TODO: check if works fine
            ClassStructureViewModel csvm = _repository.AttachMeToUserGroup(base.InstituteId, user_group_id, user_id, defaultGroup.isDefault, out return_message);
            if (csvm == null)
            {
                responseMessage = _repository.GetMessageByCode(Message.FailedToAttach.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(csvm));
            }

            return returnResponse;
        }

        /// <summary>
        /// Detach user group of a user
        /// </summary>
        /// <param name="user_group_id"></param>
        /// <returns></returns>
        [HttpPost("{user_group_id}/Detach")]
        [Authorize(Roles = "Student")]
        public IActionResult DetachUserGroup(int user_group_id)
        {
            IActionResult returnResponse = null;
            string return_message = string.Empty;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            int recordsEffected = _repository.DetachUserGroup(base.InstituteId, user_id, user_group_id, out return_message);
            if (recordsEffected < 0)
            {
                responseMessage = _repository.GetMessageByCode(Message.FailedToDetach.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(return_message));
            }
            
            return returnResponse;
        }


        //it will return all teachers who has created atleast one sessions (exam / practices) in the selected batch. Total likes, plays etc will be related to these sessions
        /// <summary>
        /// Get the teachers of an institute
        /// </summary>
        /// <param name="batch_id"></param>
        /// <param name="sortBy"></param>
        /// <param name="sortOrder"></param>
        /// <returns></returns>
        [HttpGet("Batches/{batch_id}/Teachers")]
        [Authorize(Roles = "Admin, Teacher, Student")]
        public IActionResult GetTeacherOfTheInstitution(int batch_id, [FromQuery] int author_id, [FromQuery] string sortBy, string sortOrder, [FromQuery] int? pageNumber, [FromQuery] int? pageSize)
        {
            IActionResult returnResponse;
            TeacherViewAllPagedModel teacherListPaged = new TeacherViewAllPagedModel();

            if (pageNumber == null) pageNumber = 1;
            if (pageSize == null) pageSize = 20;

            if (sortOrder == null) sortOrder = "D";

            List<TeacherViewModel> teacherList = _repository.GetTeachersOfTheInstitution(base.InstituteId, batch_id, author_id, sortBy, sortOrder);

            if (teacherList == null || teacherList.Count.Equals(0))
            {
                responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                if (pageNumber != null && pageSize != null)
                {
                    PaginatedList<TeacherViewModel> pList = PaginatedList<TeacherViewModel>.CreateAsync(teacherList, (int)pageNumber, (int)pageSize);
                    teacherListPaged.total_count = teacherList.Count;
                    teacherListPaged.total_pages = pList.TotalPages;
                    teacherListPaged.page_index = pList.PageIndex;
                    teacherListPaged.next = pList.HasNextPage;
                    teacherListPaged.previous = pList.HasPreviousPage;
                    teacherListPaged.users = pList;
                }
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(teacherListPaged));

            }
            return returnResponse;
        }


        /// <summary>
        /// Get all Plans
        /// </summary>
        /// <param name="sortBy"></param>
        /// <param name="sortOrder"></param>
        /// <returns></returns>
        [HttpGet("Plans")]
        [Authorize(Roles = "Student")]
        public IActionResult GetAllPlans([FromQuery] string sortBy, string sortOrder)
        {
            IActionResult returnResponse;
            List<PlanViewModel> theList = _repository.GetPlans(base.InstituteId, sortBy, sortOrder);
            if (theList == null || theList.Count.Equals(0))
            {
                responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                returnResponse = Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(theList));
            }
            return returnResponse;
        }

        /// <summary>
        /// Get all Plans
        /// </summary>
        /// <param name="plan_code"></param>
        /// <returns></returns>
        [HttpGet("Plans/{plan_code}")]
        [Authorize(Roles = "Student")]
        public IActionResult GetPlanByCode([FromQuery] string plan_code)
        {
            IActionResult returnResponse;
            PlanViewModel plan = _repository.GetPlanByCode(base.InstituteId, plan_code);
            if (plan == null)
            {
                responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                return Ok(ReturnResponse.GetFailureStatus(responseMessage));
            }
            else
            {
                returnResponse = Ok(ReturnResponse.GetSuccessStatus(plan));
            }
            return returnResponse;
        }

        /// <summary>
        /// Create Order ID
        /// </summary>
        /// <returns></returns>
        [HttpPost("{plan_code}/Order")]
        [Authorize(Roles = "Student")]
        public IActionResult CreateOrder(string plan_code)
        {
            IActionResult returnResponse;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            try
            {
                //if already subscribed then dont allow to create a new subscription
                dynamic svm = _repository.GetCurrentSubscriptionDetails(base.InstituteId, user_id);
                if(svm is SubscriptionViewModel && (svm.remaining_paid_exams > 0 || svm.remaining_paid_practices > 0))
                {
                    responseMessage = _repository.GetMessageByCode(Message.AlreadyExist.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.AlreadyExist, responseMessage));
                }

                int plan_id = _repository.GetPlanIdByCode(base.InstituteId, plan_code);
                if (plan_id <= 0)
                {
                    responseMessage = _repository.GetMessageByCode(Message.InvalidInput.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.InvalidInput, responseMessage));
                }

                PlanViewModel plan = _repository.GetPlanByCode(base.InstituteId, plan_code);
                if (plan == null)
                {
                    responseMessage = _repository.GetMessageByCode(Message.InvalidInput.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.InvalidInput, responseMessage));
                }

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

                dynamic new_order = _repository.CreateOrder(user_id, plan_id, plan.final_price);  

                returnResponse = Ok(ReturnResponse.GetSuccessStatus(new_order));
            }
            catch (Exception e)
            {
                return BadRequest(ReturnResponse.GetFailureStatus((int)Message.Failure, responseMessage));
            }

            return returnResponse;
        }

        /// <summary>
        /// Verify user Payment
        /// </summary>
        /// <returns></returns>
        [HttpPost("VerifyPayment")]
        [Authorize(Roles = "Student")]
        public IActionResult VerifyPayment([FromBody] VerifyPaymentView verifyDetails)
        {
            IActionResult returnResponse;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            try
            {
                dynamic order_id = _repository.verifyOrder(user_id, verifyDetails.order_id);
                
                if(order_id is int && order_id == (int)Message.NoData)
                {
                    responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.NoData, responseMessage));
                }
                else if (order_id is int && order_id < 0)
                {
                    responseMessage = _repository.GetMessageByCode(Message.Failure.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.Failure, responseMessage));
                }

                Dictionary<string, string> attributes = new Dictionary<string, string>();

                attributes.Add("razorpay_payment_id", verifyDetails.payment_id);
                attributes.Add("razorpay_order_id", order_id);
                attributes.Add("razorpay_signature", verifyDetails.signature);

                Utils.verifyPaymentSignature(attributes);

                dynamic svm = _repository.createSubscription(base.InstituteId, user_id, attributes);

                if(svm is SubscriptionViewModel)
                {
                    return Ok(ReturnResponse.GetSuccessStatus(svm));
                }
                else if(svm is int && svm == (int)Message.NoData)
                {
                    responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.NoData, responseMessage));
                }
                else
                {
                    responseMessage = _repository.GetMessageByCode(Message.Failure.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.Failure, responseMessage));
                }
            }
            catch (Exception e)
            {
                returnResponse = BadRequest(ReturnResponse.GetFailureStatus(e.Message));
            }

            return returnResponse;
        }

        /// <summary>
        /// User Current Subscription
        /// </summary>
        /// <returns></returns>
        [HttpGet("CurrentSubscription")]
        [Authorize(Roles = "Student")]
        public IActionResult CurrentSubscription()
        {
            IActionResult returnResponse;
            int user_id = Security.GetIdFromJwtToken(UserClaim.UserId, HttpContext.User.Identity as ClaimsIdentity);

            try
            {
                dynamic svm = _repository.GetCurrentSubscriptionDetails(base.InstituteId, user_id);

                if (svm is SubscriptionViewModel)
                {
                    return Ok(ReturnResponse.GetSuccessStatus(svm));
                }
                else if (svm is int && svm == (int)Message.NoData)
                {
                    responseMessage = _repository.GetMessageByCode(Message.NoData.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.NoData, responseMessage));
                }
                else
                {
                    responseMessage = _repository.GetMessageByCode(Message.Failure.ToString());
                    return BadRequest(ReturnResponse.GetFailureStatus((int)Message.Failure, responseMessage));
                }
            }
            catch (Exception e)
            {
                returnResponse = BadRequest(ReturnResponse.GetFailureStatus(e.Message));
                return returnResponse;
            }
            
        }
    }
}