using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using System.Xml.Linq;
using Microsoft.Ajax.Utilities;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        private ApplicationSignInManager _signInManager;
        private ApplicationUserManager _userManager;

        public AccountController()
        {
        }

        public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
        {
            UserManager = userManager;
            SignInManager = signInManager;
        }

        public ApplicationSignInManager SignInManager
        {
            get
            {
                return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
            }
            private set
            {
                _signInManager = value;
            }
        }

        public ApplicationUserManager UserManager
        {
            get
            {
                return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            private set
            {
                _userManager = value;
            }
        }

        //
        // GET: /Account/Login
        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, change to shouldLockout: true
            var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
            }
        }

        //
        // GET: /Account/VerifyCode
        [AllowAnonymous]
        public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
        {
            // Require that the user has already logged in via username/password or external login
            if (!await SignInManager.HasBeenVerifiedAsync())
            {
                return View("Error");
            }
            return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
        }

        //
        // POST: /Account/VerifyCode
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // The following code protects for brute force attacks against the two factor codes. 
            // If a user enters incorrect codes for a specified amount of time then the user account 
            // will be locked out for a specified amount of time. 
            // You can configure the account lockout settings in IdentityConfig
            var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(model.ReturnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid code.");
                    return View(model);
            }
        }

        //
        // GET: /Account/Register
        [AllowAnonymous]
        public ActionResult Register()
        {
            var model = new RegisterViewModel
            {
                Topics = new List<string>(),
                Subjects = new List<string>(),
                Projects = new List<Project>(),
            };
            return View(model);
        }

        //
        // POST: /Account/Register
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                ApplicationUser user;
                if (model.UserType == "Student")
                {
                    user = new Student
                    {
                        UserName = model.Email,
                        Email = model.Email,
                        Name = model.Name,
                        Surname = model.Surname,
                        Biography = model.Biography,
                        Projects = model.Projects,
                        Index = model.Index,
                        Major = model.Major,
                        Cycle = model.Cycle,
                        Semester = model.Semester
                    };

                }
                else
                {
                    user = new Mentor
                    {
                        UserName = model.Email,
                        Email = model.Email,
                        Name = model.Name,
                        Surname = model.Surname,
                        Biography = model.Biography,
                        Projects = model.Projects,
                        Timeslots = model.Timeslots,
                        TypesOfProject = model.TypesOfProject,
                        Available = model.Available,
                        ImageURL = model.ImageURL
                    };
                }

                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    if (model.UserType == "Student")
                    {
                        var student = db.Users
                        .OfType<Student>()
                        .Include(s => s.Subjects)
                        .Include(s => s.Topics)
                        .Single(u => u.Id == user.Id);

                        student.Subjects.Clear();
                        student.Topics.Clear();

                        foreach (var subjName in model.Subjects ?? Enumerable.Empty<string>())
                            student.Subjects.Add(new Subject { Name = subjName, UserId = student.Id });

                        foreach (var topicName in model.Topics ?? Enumerable.Empty<string>())
                            student.Topics.Add(new Topic { Name = topicName, UserId = student.Id });
                    }
                    else // Mentor
                    {
                        var mentor = db.Users
                        .OfType<Mentor>()
                        .Include(m => m.Subjects)
                        .Include(m => m.Topics)
                        .Single(u => u.Id == user.Id);

                        mentor.Subjects.Clear();
                        mentor.Topics.Clear();

                        foreach (var subjName in model.Subjects ?? Enumerable.Empty<string>())
                            mentor.Subjects.Add(new Subject { Name = subjName, UserId = mentor.Id });

                        foreach (var topicName in model.Topics ?? Enumerable.Empty<string>())
                            mentor.Topics.Add(new Topic { Name = topicName, UserId = mentor.Id });
                    }

                    db.SaveChanges();

                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                    // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
                    // Send an email with this link
                    // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                    // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                    // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

                    if (model.UserType == "Student")
                        return RedirectToAction("StudentProfile", "Account", new { id = user.Id });
                    else
                        return RedirectToAction("MentorProfile", "Account", new { id = user.Id });
                }
                AddErrors(result);
            }

            return View(model);
        }

        [HttpGet]
        public ActionResult ProfileRedirect()
        {
            var userId = User.Identity.GetUserId();

            var mentor = db.Users
                   .OfType<Mentor>()
                   .SingleOrDefault(u => u.Id == userId);

            if (mentor != null)
                return RedirectToAction("MentorProfile", new { id = userId });

            var student = db.Users
                            .OfType<Student>()
                            .SingleOrDefault(u => u.Id == userId);

            if (student != null)
                return RedirectToAction("StudentProfile", new { id = userId });

            return RedirectToAction("Index", "Home");
        }

        public ActionResult StudentProfile(string id)
        {
            var student = db.Users
                            .OfType<Student>()
                            .Include(u => u.Subjects)
                            .Include(u => u.Topics)
                            .SingleOrDefault(u => u.Id == id);
            if (student == null)
            {
                return HttpNotFound();
            }

            var currentUserId = User.Identity.GetUserId();
            bool isOwner = student.Id == currentUserId;

            ViewBag.IsOwner = isOwner;

            var vm = new EditStudentModel
            {
                Id = student.Id,
                Name = student.Name,
                Surname = student.Surname,
                Email = student.Email,
                Biography = student.Biography,
                Subjects = student.Subjects.Select(s => s.Name).ToList(),
                Topics = student.Topics.Select(t => t.Name).ToList(),
                Index = student.Index,
                Major = student.Major,
                Cycle = student.Cycle,
                Semester = student.Semester
            };

            return View(vm);
        }
        public ActionResult MentorProfile(string id)
        {
            var mentor = db.Users
                            .OfType<Mentor>()
                            .Include(u => u.Subjects)
                            .Include(u => u.Topics)
                            .SingleOrDefault(u => u.Id == id);
            if (mentor == null)
            {
                return HttpNotFound();
            }

            var currentUserId = User.Identity.GetUserId();
            bool isOwner = mentor.Id == currentUserId;
            ViewBag.IsOwner = isOwner;

            ViewBag.IsMentor = db.Users.OfType<Mentor>().Any(m => m.Id == currentUserId);

            var vm = new EditMentorModel
            {
                Id = mentor.Id,
                Name = mentor.Name,
                Surname = mentor.Surname,
                Email = mentor.Email,
                Biography = mentor.Biography,
                Subjects = mentor.Subjects.Select(s => s.Name).ToList(),
                Topics = mentor.Topics.Select(t => t.Name).ToList(),
                Timeslots = mentor.Timeslots,
                TypesOfProject = mentor.TypesOfProject,
                Available = mentor.Available,
                ImageURL = mentor.ImageURL
            };

            // Load approved opinions (for public display)
            var approvedOpinions = db.Opinions
                .Where(o => o.MentorId == mentor.Id && o.Status == OpinionStatus.Approved)
                .Include(o => o.Student)
                .OrderByDescending(o => o.Timestamp)
                .Take(50)
                .ToList() // materialize to avoid proxy/lazy load issues
                .Select(o => new OpinionDto
                {
                    Id = o.Id,
                    StudentId = o.StudentId,
                    StudentName = o.Student != null ? (o.Student.Name + " " + o.Student.Surname) : "",
                    Content = o.Content,
                    Status = o.Status,
                    Timestamp = o.Timestamp
                })
                .ToList();

            vm.ApprovedOpinions = approvedOpinions;

            // If the viewer is the profile owner (mentor), also load pending opinions for moderation
            if (isOwner)
            {
                var pendingOpinions = db.Opinions
                    .Where(o => o.MentorId == mentor.Id && o.Status == OpinionStatus.Pending)
                    .Include(o => o.Student)
                    .OrderByDescending(o => o.Timestamp)
                    .ToList()
                    .Select(o => new OpinionDto
                    {
                        Id = o.Id,
                        StudentId = o.StudentId,
                        StudentName = o.Student != null ? (o.Student.Name + " " + o.Student.Surname) : "",
                        Content = o.Content,
                        Status = o.Status,
                        Timestamp = o.Timestamp
                    })
                    .ToList();

                vm.PendingOpinions = pendingOpinions;
            }
            else
            {
                vm.PendingOpinions = new List<OpinionDto>();
            }

            return View(vm);
        }

        public ActionResult EditStudentProfile(string id)
        {
            var student = db.Users
                            .OfType<Student>()
                            .Include(u => u.Subjects)
                            .Include(u => u.Topics)
                            .SingleOrDefault(u => u.Id == id);

            if (student == null)
                return HttpNotFound();

            var vm = new EditStudentModel
            {
                Id = student.Id,
                Name = student.Name,
                Surname = student.Surname,
                Biography = student.Biography,
                Email = student.Email,
                Subjects = student.Subjects.Select(s => s.Name).ToList(),
                Topics = student.Topics.Select(t => t.Name).ToList(),
                Index = student.Index,
                Major = student.Major,
                Cycle = student.Cycle,
                Semester = student.Semester
            };

            return View(vm);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult EditStudentProfile(EditStudentModel model)
        {
            if (!ModelState.IsValid)
                return View(model);

            var student = db.Users
                            .OfType<Student>()
                            .Include(u => u.Subjects)
                            .Include(u => u.Topics)
                            .SingleOrDefault(u => u.Id == model.Id);
            if (student == null)
                return HttpNotFound();

            student.Name = model.Name;
            student.Surname = model.Surname;
            student.Biography = model.Biography;
            student.Email = model.Email;
            student.Index = model.Index;
            student.Major = model.Major;
            student.Cycle = model.Cycle;
            student.Semester = model.Semester;

            if (student.Subjects.Any())
            {
                db.Subjects.RemoveRange(student.Subjects);
            }

            foreach (var subjName in model.Subjects ?? Enumerable.Empty<string>())
            {
                student.Subjects.Add(new Subject
                {
                    Name = subjName,
                    UserId = student.Id
                });
            }

            if (student.Topics.Any())
            {
                db.Topics.RemoveRange(student.Topics);
            }
            foreach (var topicName in model.Topics ?? Enumerable.Empty<string>())
            {
                student.Topics.Add(new Topic
                {
                    Name = topicName,
                    UserId = student.Id
                });
            }

            db.SaveChanges();
            return RedirectToAction("StudentProfile", new { id = student.Id });
        }

        public ActionResult EditMentorProfile(string id)
        {
            var mentor = db.Users
                            .OfType<Mentor>()
                            .Include(u => u.Subjects)
                            .Include(u => u.Topics)
                            .SingleOrDefault(u => u.Id == id);

            if (mentor == null)
                return HttpNotFound();

            var vm = new EditMentorModel
            {
                Id = mentor.Id,
                Name = mentor.Name,
                Surname = mentor.Surname,
                Email = mentor.Email,
                Biography = mentor.Biography,
                Subjects = mentor.Subjects.Select(s => s.Name).ToList(),
                Topics = mentor.Topics.Select(t => t.Name).ToList(),
                Timeslots = mentor.Timeslots,
                TypesOfProject = mentor.TypesOfProject,
                Available = mentor.Available,
                ImageURL = mentor.ImageURL
            };

            return View(vm);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult EditMentorProfile(EditMentorModel model)
        {
            if (!ModelState.IsValid)
                return View(model);

            var mentor = db.Users
                            .OfType<Mentor>()
                            .Include(u => u.Subjects)
                            .Include(u => u.Topics)
                            .SingleOrDefault(u => u.Id == model.Id);
            if (mentor == null)
                return HttpNotFound();

            mentor.Name = model.Name;
            mentor.Surname = model.Surname;
            mentor.Biography = model.Biography;
            mentor.Email = model.Email;
            mentor.Timeslots = model.Timeslots;
            mentor.TypesOfProject = model.TypesOfProject;
            mentor.Available = model.Available;
            mentor.ImageURL = model.ImageURL;

            mentor.Subjects.Clear();
            foreach (var subjName in model.Subjects ?? new List<string>())
            {
                mentor.Subjects.Add(new Subject { Name = subjName, UserId = mentor.Id });
            }

            // Clear and replace Topics
            mentor.Topics.Clear();
            foreach (var topicName in model.Topics ?? new List<string>())
            {
                mentor.Topics.Add(new Topic { Name = topicName, UserId = mentor.Id });
            }

            db.SaveChanges();

            return RedirectToAction("MentorProfile", new { id = mentor.Id });
        }

        protected bool IsStudent()
        {
            var userId = User.Identity.GetUserId();
            return db.Users.OfType<Student>().Any(u => u.Id == userId);
        }
        public ActionResult ViewMentors(string name, string surname, string subject, string topic, string available)
        {
            using (var db = new ApplicationDbContext())
            {
                var mentors = db.Mentors.Include(m => m.Subjects).Include(m => m.Topics).ToList();

                var viewModel = mentors.Select(m => new EditMentorModel
                {
                    Id = m.Id,
                    Name = m.Name,
                    Surname = m.Surname,
                    Biography = m.Biography,
                    Email = m.Email,
                    Subjects = m.Subjects?.Select(s => s.Name).ToList() ?? new List<string>(),
                    Topics = m.Topics?.Select(t => t.Name).ToList() ?? new List<string>(),
                    Projects = m.Projects,
                    Timeslots = m.Timeslots,
                    TypesOfProject = m.TypesOfProject,
                    Available = m.Available,
                    ImageURL = m.ImageURL
                }).ToList();

                if (!string.IsNullOrWhiteSpace(name))
                {
                    viewModel = viewModel.Where(m =>
                        m.Name.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0 ||
                        m.Surname.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0
                    ).ToList();
                }

                if (!string.IsNullOrWhiteSpace(surname))
                {
                    viewModel = viewModel.Where(m =>
                        m.Surname.IndexOf(surname, StringComparison.OrdinalIgnoreCase) >= 0
                    ).ToList();
                }

                if (!string.IsNullOrWhiteSpace(subject))
                {
                    viewModel = viewModel.Where(m =>
                        m.Subjects.Any(s => s.IndexOf(subject, StringComparison.OrdinalIgnoreCase) >= 0)
                    ).ToList();
                }

                if (!string.IsNullOrWhiteSpace(topic))
                {
                    viewModel = viewModel.Where(m =>
                        m.Topics.Any(s => s.IndexOf(topic, StringComparison.OrdinalIgnoreCase) >= 0)
                    ).ToList();
                }

                if (!string.IsNullOrWhiteSpace(available) && bool.TryParse(available, out bool isAvailable))
                {
                    viewModel = viewModel.Where(m => m.Available == isAvailable).ToList();
                }

                ViewBag.IsStudent = IsStudent();

                return View(viewModel);
            }
        }

        [HttpGet]
        public ActionResult FindBestMatch(string matchType)
        {
            var currentUserId = User.Identity.GetUserId();
            var student = db.Users
                    .OfType<Student>()
                    .Include(u => u.Subjects)
                    .Include(u => u.Topics)
                    .SingleOrDefault(u => u.Id == currentUserId);

            if (student == null)
                return HttpNotFound("Student not found.");

            var mentors = db.Users
                            .OfType<Mentor>()
                            .Include(m => m.Subjects)
                            .Include(m => m.Topics)
                            .Where(m => m.Available)
                            .ToList();

            var studentSubjects = student.Subjects?.Select(s => s.Name).ToList() ?? new List<string>();
            var studentTopics = student.Topics?.Select(t => t.Name).ToList() ?? new List<string>();

            var matchedMentors = mentors
                .Select(m => new
                {
                    Mentor = m,
                    Score = CalculateMatchScore(
                        m.Subjects?.Select(s => s.Name).ToList() ?? new List<string>(),
                        m.Topics?.Select(t => t.Name).ToList() ?? new List<string>(),
                        studentSubjects,
                        studentTopics,
                        matchType)
                })
                .Where(x => x.Score > 0)
                .OrderByDescending(x => x.Score)
                .Select(x => new EditMentorModel
                {
                    Id = x.Mentor.Id,
                    Name = x.Mentor.Name,
                    Surname = x.Mentor.Surname,
                    Email = x.Mentor.Email,
                    Biography = x.Mentor.Biography,
                    Subjects = x.Mentor.Subjects?.Select(s => s.Name).ToList() ?? new List<string>(),
                    Topics = x.Mentor.Topics?.Select(t => t.Name).ToList() ?? new List<string>(),
                    Timeslots = x.Mentor.Timeslots,
                    TypesOfProject = x.Mentor.TypesOfProject,
                    Available = x.Mentor.Available,
                    ImageURL = x.Mentor.ImageURL
                })
                .ToList();

            return View("ViewMentors", matchedMentors);
        }

        private int CalculateMatchScore(
            List<string> mentorSubjects,
            List<string> mentorTopics,
            List<string> studentSubjects,
            List<string> studentTopics,
            string matchType)
        {
            int score = 0;

            if (matchType == "subjects" || matchType == "both")
            {
                score += mentorSubjects.Intersect(studentSubjects, StringComparer.OrdinalIgnoreCase).Count() * 2;
            }

            if (matchType == "interests" || matchType == "both")
            {
                score += mentorTopics.Intersect(studentTopics, StringComparer.OrdinalIgnoreCase).Count() * 3;
            }

            return score;
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult SendContact(MentorContact model)
        {
            if (string.IsNullOrWhiteSpace(model.MentorId))
            {
                ModelState.AddModelError("MentorId", "Mentor is required.");
            }

            if (!ModelState.IsValid)
            {
                if (Request.IsAjaxRequest())
                {
                    var errors = ModelState
                        .Where(kv => kv.Value.Errors.Any())
                        .SelectMany(kv => kv.Value.Errors.Select(e => (kv.Key == "" ? "" : kv.Key + ": ") + e.ErrorMessage))
                        .ToArray();

                    return Json(new { success = false, errors = errors });
                }

                return PartialView("ContactForm", model);
            }

            var currentUserId = User.Identity.GetUserId();
            var student = db.Users.OfType<Student>().FirstOrDefault(s => s.Id == currentUserId);

            if (student == null)
            {
                if (Request.IsAjaxRequest())
                    return Json(new { success = false, message = "You must be logged in as a student to contact a mentor." });

                return new HttpUnauthorizedResult();
            }

            model.StudentId = student.Id;
            model.Timestamp = DateTime.Now;
            model.Status = ContactStatus.Pending;

            db.MentorContacts.Add(model);
            db.SaveChanges();

            if (Request.IsAjaxRequest())
            {
                return Json(new { success = true, message = "Успешно испратена порака до менторот." });
            }

            TempData["Success"] = "Успешен контакт со ментор!";
            return RedirectToAction("MentorProfile", new { id = model.MentorId });
        }

        [Authorize]
        public ActionResult Inbox()
        {
            var currentUserId = User.Identity.GetUserId();
            var vm = new WebApplication1.Models.InboxCombinedViewModel();

            // is mentor?
            var mentor = db.Users.OfType<Mentor>().FirstOrDefault(m => m.Id == currentUserId);
            vm.IsMentor = mentor != null;

            // is student?
            var student = db.Users.OfType<Student>().FirstOrDefault(s => s.Id == currentUserId);
            vm.IsStudent = student != null;

            if (vm.IsMentor)
            {
                // contact requests
                var contactItems = db.MentorContacts
                    .Where(c => c.MentorId == currentUserId)
                    .Include(c => c.Student)
                    .OrderByDescending(c => c.Timestamp)
                    .ToList()
                    .Select(c => new RequestItemDto
                    {
                        Id = c.Id,
                        ItemType = "Contact",
                        StudentId = c.StudentId,
                        StudentName = c.Student != null ? (c.Student.Name + " " + c.Student.Surname) : "",
                        Subject = c.SubjectName,
                        ProjectType = c.Type,
                        TeamSize = c.TeamSize,
                        Message = c.Message,
                        Timestamp = c.Timestamp,
                        ContactStatus = c.Status
                    }).ToList();

                // opinion requests (pending & all, show everywhere but you can filter)
                var opinionItems = db.Opinions
                    .Where(o => o.MentorId == currentUserId)
                    .Include(o => o.Student)
                    .OrderByDescending(o => o.Timestamp)
                    .ToList()
                    .Select(o => new RequestItemDto
                    {
                        Id = o.Id,
                        ItemType = "Opinion",
                        StudentId = o.StudentId,
                        StudentName = o.Student != null ? (o.Student.Name + " " + o.Student.Surname) : "",
                        Subject = (o.Content != null && o.Content.Length > 80) ? (o.Content.Substring(0, 80) + "…") : o.Content,
                        ProjectType = null,
                        TeamSize = null,
                        Message = o.Content,
                        Timestamp = o.Timestamp,
                        OpinionStatus = o.Status
                    }).ToList();

                // combine and order by timestamp (most recent first)
                vm.MentorRequests = contactItems
                    .Concat(opinionItems)
                    .OrderByDescending(x => x.Timestamp)
                    .ToList();
            }

            if (vm.IsStudent)
            {
                // load messages addressed to this student
                vm.StudentMessages = db.Messages
                    .Where(m => m.ToUserId == currentUserId)
                    .Include(m => m.FromUser)
                    .OrderByDescending(m => m.Timestamp)
                    .ToList();
            }

            return View(vm);
        }


        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult AcceptMessage(int id)
        {
            var msg = db.MentorContacts
                .Include(c => c.Student)
                .Include(c => c.Mentor)
                .FirstOrDefault(c => c.Id == id);

            if (msg == null)
                return HttpNotFound();

            // mark request accepted
            msg.Status = ContactStatus.Accepted;
            db.SaveChanges();

            // return JSON so client can open reply modal with student info
            return Json(new
            {
                success = true,
                requestId = msg.Id,
                studentId = msg.StudentId,
                studentName = (msg.Student != null) ? (msg.Student.Name + " " + msg.Student.Surname) : "",
                subject = msg.SubjectName ?? ""
            });
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult SendReplyToStudent(ReplyViewModel model)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToArray();
                return Json(new { success = false, errors = errors });
            }

            var currentUserId = User.Identity.GetUserId();
            if (string.IsNullOrEmpty(currentUserId))
                return new HttpUnauthorizedResult();

            // create Message entity
            var message = new Message
            {
                FromUserId = currentUserId,
                ToUserId = model.ToUserId,
                Subject = model.Subject,
                Body = model.Body,
                Timestamp = DateTime.UtcNow,
                IsRead = false
            };

            db.Messages.Add(message);
            db.SaveChanges();

            return Json(new { success = true, message = "Reply sent to student." });
        }

        [HttpPost]
        public ActionResult RejectMessage(int id)
        {
            var msg = db.MentorContacts.Find(id);
            if (msg == null) return HttpNotFound();

            msg.Status = ContactStatus.Rejected;
            db.SaveChanges();
            return new HttpStatusCodeResult(200);
        }

        [Authorize]
        public JsonResult GetNotifications()
        {
            var currentUserId = User.Identity.GetUserId();

            var prevProxy = db.Configuration.ProxyCreationEnabled;
            var prevLazy = db.Configuration.LazyLoadingEnabled;
            db.Configuration.ProxyCreationEnabled = false;
            db.Configuration.LazyLoadingEnabled = false;

            try
            {
                var mentor = db.Users.OfType<Mentor>().FirstOrDefault(m => m.Id == currentUserId);

                if (mentor != null)
                {
                    var contactNotifications = db.MentorContacts
                        .Include(c => c.Student)
                        .Where(c => c.MentorId == mentor.Id && c.Status == ContactStatus.Pending)
                        .OrderByDescending(c => c.Timestamp)
                        .Take(5)
                        .ToList()
                        .Select(c => new {
                            Id = c.Id,
                            Type = "MentorRequest",
                            Title = "New contact request",
                            From = c.Student != null ? (c.Student.Name + " " + c.Student.Surname) : "Unknown student",
                            Subject = c.SubjectName,
                            Timestamp = c.Timestamp.ToString("g")
                        }).ToList();

                    var opinionNotifications = db.Opinions
                        .Include(o => o.Student)
                        .Where(o => o.MentorId == mentor.Id && o.Status == OpinionStatus.Pending)
                        .OrderByDescending(o => o.Timestamp)
                        .Take(5)
                        .ToList()
                        .Select(o => new {
                            Id = o.Id,
                            Type = "OpinionPending",
                            Title = "New opinion pending",
                            From = o.Student != null ? (o.Student.Name + " " + o.Student.Surname) : "Student",
                            Subject = (o.Content.Length > 120 ? o.Content.Substring(0, 120) + "…" : o.Content),
                            Timestamp = o.Timestamp.ToString("g")
                        }).ToList();

                    var combined = contactNotifications.Concat(opinionNotifications).OrderByDescending(x => DateTime.Parse(x.Timestamp)).Take(5).ToList();

                    return Json(new
                    {
                        success = true,
                        role = "Mentor",
                        count = combined.Count,
                        notifications = combined
                    }, JsonRequestBehavior.AllowGet);
                }

                var student = db.Users.OfType<Student>().FirstOrDefault(s => s.Id == currentUserId);

                if (student != null)
                {
                    var studentNotifications = db.Messages
                        .Include(m => m.FromUser)
                        .Where(m => m.ToUserId == student.Id && !m.IsRead)
                        .OrderByDescending(m => m.Timestamp)
                        .Take(5)
                        .ToList()
                        .Select(m => new
                        {
                            Id = m.Id,
                            Type = "MentorReply",
                            Title = "Mentor replied",
                            From = m.FromUser != null ? (m.FromUser.Name + " " + m.FromUser.Surname) : "Mentor",
                            Subject = m.Subject,
                            Timestamp = m.Timestamp.ToString("g")
                        })
                        .ToList();

                    return Json(new
                    {
                        success = true,
                        role = "Student",
                        count = studentNotifications.Count,
                        notifications = studentNotifications
                    }, JsonRequestBehavior.AllowGet);
                }

                return Json(new { success = false, message = "Unknown user type" }, JsonRequestBehavior.AllowGet);
            }
            finally
            {
                db.Configuration.ProxyCreationEnabled = prevProxy;
                db.Configuration.LazyLoadingEnabled = prevLazy;
            }
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult MarkMessageRead(int id)
        {
            var userId = User.Identity.GetUserId();
            var msg = db.Messages.FirstOrDefault(m => m.Id == id && m.ToUserId == userId);
            if (msg == null) return HttpNotFound();

            if (!msg.IsRead)
            {
                msg.IsRead = true;
                db.SaveChanges();
            }
            return Json(new { success = true });
        }


        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult SubmitOpinion(OpinionViewModel model)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToArray();
                return Json(new { success = false, errors = errors });
            }

            var studentId = User.Identity.GetUserId();
            if (string.IsNullOrEmpty(studentId))
                return new HttpUnauthorizedResult();

            // validate mentor exists
            var mentor = db.Users.OfType<Mentor>().FirstOrDefault(m => m.Id == model.MentorId);
            if (mentor == null)
                return Json(new { success = false, message = "Mentor not found." });

            var opinion = new Opinion
            {
                StudentId = studentId,
                MentorId = model.MentorId,
                Content = model.Content,
                Status = OpinionStatus.Pending,
                Timestamp = DateTime.UtcNow
            };

            db.Opinions.Add(opinion);
            db.SaveChanges();

            // optional: return the created id so you can link to it
            return Json(new { success = true, message = "Вашето мислење е пратено и чека одобрување.", id = opinion.Id });
        }


        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult ApproveOpinion(int id)
        {
            var userId = User.Identity.GetUserId();
            var opinion = db.Opinions.Include(o => o.Mentor).FirstOrDefault(o => o.Id == id);
            if (opinion == null) return HttpNotFound();
            if (opinion.MentorId != userId) return new HttpUnauthorizedResult();

            opinion.Status = OpinionStatus.Approved;
            db.SaveChanges();

            // optionally notify the student (create a Message entity)
            // var message = new Message { FromUserId = userId, ToUserId = opinion.StudentId, Subject = "Your opinion was approved", Body = "...", Timestamp = DateTime.UtcNow, IsRead = false };
            // db.Messages.Add(message); db.SaveChanges();

            return Json(new { success = true });
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult RejectOpinion(int id)
        {
            var userId = User.Identity.GetUserId();
            var opinion = db.Opinions.Include(o => o.Mentor).FirstOrDefault(o => o.Id == id);
            if (opinion == null) return HttpNotFound();
            if (opinion.MentorId != userId) return new HttpUnauthorizedResult();

            opinion.Status = OpinionStatus.Rejected;
            db.SaveChanges();

            return Json(new { success = true });
        }

        //
        // GET: /Account/ConfirmEmail
        [AllowAnonymous]
        public async Task<ActionResult> ConfirmEmail(string userId, string code)
        {
            if (userId == null || code == null)
            {
                return View("Error");
            }
            var result = await UserManager.ConfirmEmailAsync(userId, code);
            return View(result.Succeeded ? "ConfirmEmail" : "Error");
        }

        //
        // GET: /Account/ForgotPassword
        [AllowAnonymous]
        public ActionResult ForgotPassword()
        {
            return View();
        }

        //
        // POST: /Account/ForgotPassword
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindByNameAsync(model.Email);
                if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
                {
                    // Don't reveal that the user does not exist or is not confirmed
                    return View("ForgotPasswordConfirmation");
                }

                // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
                // Send an email with this link
                // string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
                // var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);		
                // await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
                // return RedirectToAction("ForgotPasswordConfirmation", "Account");
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // GET: /Account/ForgotPasswordConfirmation
        [AllowAnonymous]
        public ActionResult ForgotPasswordConfirmation()
        {
            return View();
        }

        //
        // GET: /Account/ResetPassword
        [AllowAnonymous]
        public ActionResult ResetPassword(string code)
        {
            return code == null ? View("Error") : View();
        }

        //
        // POST: /Account/ResetPassword
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            var user = await UserManager.FindByNameAsync(model.Email);
            if (user == null)
            {
                // Don't reveal that the user does not exist
                return RedirectToAction("ResetPasswordConfirmation", "Account");
            }
            var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("ResetPasswordConfirmation", "Account");
            }
            AddErrors(result);
            return View();
        }

        //
        // GET: /Account/ResetPasswordConfirmation
        [AllowAnonymous]
        public ActionResult ResetPasswordConfirmation()
        {
            return View();
        }

        //
        // POST: /Account/ExternalLogin
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            // Request a redirect to the external login provider
            return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
        }

        //
        // GET: /Account/SendCode
        [AllowAnonymous]
        public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
        {
            var userId = await SignInManager.GetVerifiedUserIdAsync();
            if (userId == null)
            {
                return View("Error");
            }
            var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
            var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
            return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
        }

        //
        // POST: /Account/SendCode
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> SendCode(SendCodeViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View();
            }

            // Generate the token and send it
            if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
            {
                return View("Error");
            }
            return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
        }

        //
        // GET: /Account/ExternalLoginCallback
        [AllowAnonymous]
        public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                return RedirectToAction("Login");
            }

            // Sign in the user with this external login provider if the user already has a login
            var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
                case SignInStatus.Failure:
                default:
                    // If the user does not have an account, then prompt the user to create an account
                    ViewBag.ReturnUrl = returnUrl;
                    ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                    return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
            }
        }

        //
        // POST: /Account/ExternalLoginConfirmation
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
        {
            if (User.Identity.IsAuthenticated)
            {
                return RedirectToAction("Index", "Manage");
            }

            if (ModelState.IsValid)
            {
                // Get the information about the user from the external login provider
                var info = await AuthenticationManager.GetExternalLoginInfoAsync();
                if (info == null)
                {
                    return View("ExternalLoginFailure");
                }
                var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
                var result = await UserManager.CreateAsync(user);
                if (result.Succeeded)
                {
                    result = await UserManager.AddLoginAsync(user.Id, info.Login);
                    if (result.Succeeded)
                    {
                        await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
                        return RedirectToLocal(returnUrl);
                    }
                }
                AddErrors(result);
            }

            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }

        //
        // POST: /Account/LogOff
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
            return RedirectToAction("Index", "Home");
        }

        //
        // GET: /Account/ExternalLoginFailure
        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }

        private ApplicationDbContext db = new ApplicationDbContext();
        public ActionResult ProfileView(string id)
        {
            var user = db.Users.Find(id);
            if (user == null)
            {
                return HttpNotFound();
            }
            return View(user);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_userManager != null)
                {
                    _userManager.Dispose();
                    _userManager = null;
                }

                if (_signInManager != null)
                {
                    _signInManager.Dispose();
                    _signInManager = null;
                }
            }

            base.Dispose(disposing);
        }

        #region Helpers
        // Used for XSRF protection when adding external logins
        private const string XsrfKey = "XsrfId";

        private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

        private void AddErrors(IdentityResult result)
        {
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError("", error);
            }
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            return RedirectToAction("Index", "Home");
        }

        internal class ChallengeResult : HttpUnauthorizedResult
        {
            public ChallengeResult(string provider, string redirectUri)
                : this(provider, redirectUri, null)
            {
            }

            public ChallengeResult(string provider, string redirectUri, string userId)
            {
                LoginProvider = provider;
                RedirectUri = redirectUri;
                UserId = userId;
            }

            public string LoginProvider { get; set; }
            public string RedirectUri { get; set; }
            public string UserId { get; set; }

            public override void ExecuteResult(ControllerContext context)
            {
                var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
                if (UserId != null)
                {
                    properties.Dictionary[XsrfKey] = UserId;
                }
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
            }
        }
        #endregion
    }
}