using Microsoft.Ajax.Utilities;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
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 WebApplication1.Models;
using static WebApplication1.Controllers.ManageController;

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);
        }
        // AccountController.cs

        // GET: MentorProfile
        public ActionResult MentorProfile(string id)
        {
            // Eagerly include subjects, topics and topic suggestions + assigned student to avoid lazy loading issues
            var mentor = db.Users
                           .OfType<Mentor>()
                           .Include(u => u.Subjects)
                           .Include(u => u.Topics)
                           .Include(u => u.TopicSuggestions.Select(ts => ts.AssignedStudent)) // EF6 syntax
                           .SingleOrDefault(u => u.Id == id);

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

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

            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() ?? new List<string>(),
                Topics = mentor.Topics?.Select(t => t.Name).ToList() ?? new List<string>(),
                Timeslots = mentor.Timeslots,
                TypesOfProject = mentor.TypesOfProject,
                Available = mentor.Available,
                ImageURL = mentor.ImageURL,
                // initialize empty list; will populate below
                TopicSuggestions = new List<TopicSuggestionDto>()
            };

            // Map topic suggestions into DTOs.
            // If viewer is the owner, include assigned student name; otherwise don't expose student identity.
            var suggestions = (mentor.TopicSuggestions ?? Enumerable.Empty<TopicSuggestion>())
                .OrderByDescending(ts => ts.CreatedAt)
                .Select(ts => new TopicSuggestionDto
                {
                    Id = ts.Id,
                    Title = ts.Title,
                    Description = ts.Description,
                    IsAssigned = ts.IsAssigned,
                    // only expose the assigned student id/name to the owner or to the assigned student themself
                    AssignedStudentId = (isOwner || ts.AssignedStudentId == currentUserId) ? ts.AssignedStudentId : null,
                    AssignedStudentName = (isOwner || ts.AssignedStudentId == currentUserId) && ts.AssignedStudent != null
                        ? (ts.AssignedStudent.Name + " " + ts.AssignedStudent.Surname)
                        : null,
                    CreatedAt = ts.CreatedAt
                })
                .ToList();

            vm.TopicSuggestions = suggestions;

            // 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()
                .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;

            // Pending opinions visible only to owner
            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)
                            .Include(u => u.TopicSuggestions.Select(ts => ts.AssignedStudent)) // EF6
                            .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() ?? new List<string>(),
                Topics = mentor.Topics?.Select(t => t.Name).ToList() ?? new List<string>(),
                Timeslots = mentor.Timeslots,
                TypesOfProject = mentor.TypesOfProject,
                Available = mentor.Available,
                ImageURL = mentor.ImageURL,
                TopicSuggestions = new List<TopicSuggestionDto>() // populate below
            };

            var suggestions = (mentor.TopicSuggestions ?? Enumerable.Empty<TopicSuggestion>())
                .OrderByDescending(ts => ts.CreatedAt)
                .Select(ts => new TopicSuggestionDto
                {
                    Id = ts.Id,
                    Title = ts.Title,
                    Description = ts.Description,
                    IsAssigned = ts.IsAssigned,
                    AssignedStudentId = ts.AssignedStudentId,
                    AssignedStudentName = ts.AssignedStudent != null
                        ? (ts.AssignedStudent.Name + " " + ts.AssignedStudent.Surname)
                        : null,
                    CreatedAt = ts.CreatedAt
                })
                .ToList();

            vm.TopicSuggestions = suggestions;

            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;
        }

        //contact

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<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();

            // Email mentor (non-blocking to user flow, but await so exceptions are catchable)
            try
            {
                var mentor = db.Users.OfType<Mentor>().FirstOrDefault(m => m.Id == model.MentorId);
                if (mentor != null && !string.IsNullOrEmpty(mentor.Email))
                {
                    string selectedTopicTitle = null;
                    if (model.TopicSuggestionId.HasValue)
                    {
                        var ts = db.TopicSuggestions.Find(model.TopicSuggestionId.Value);
                        if (ts != null) selectedTopicTitle = ts.Title;
                    }

                    var subject = "Ново барање за контакт на NajdiMentor";
                    var body = $@"
                    <p>Почитуван/а {HttpUtility.HtmlEncode(mentor.Name)} {HttpUtility.HtmlEncode(mentor.Surname)},</p>
                    <p>Добијавте ново барање за менторство од <strong>{HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)}</strong>.</p>
                    <p><strong>Предмет:</strong> {HttpUtility.HtmlEncode(model.SubjectName ?? "—")}</p>
                    <p><strong>Тип:</strong> {HttpUtility.HtmlEncode(model.Type ?? "—")}<br/>
                    <strong>Големина на тим:</strong> {(model.TeamSize.HasValue ? model.TeamSize.Value.ToString() : "—")}</p>
                    " + (selectedTopicTitle != null ? $"<p><strong>Предлог тема:</strong> {HttpUtility.HtmlEncode(selectedTopicTitle)}</p>" : $"<p><strong>Предлог тема:</strong> —</p>") +
                                    $@"
                    <p>Порака:<br/>{HttpUtility.HtmlEncode(model.Message)}</p>
                    <p>Прегледајте го барањето: <a href='{Url.Action("Inbox", "Account", null, Request?.Url?.Scheme)}'>Inbox</a></p>
                    <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(mentor.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (SendContact): " + ex);
            }

            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)
            {
                var contactItems = db.MentorContacts
                    .Where(c => c.MentorId == currentUserId)
                    .Include(c => c.Student)
                    .Include(c => c.TopicSuggestion) // <-- include topic
                    .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,
                        SelectedTopicTitle = c.TopicSuggestion != null ? c.TopicSuggestion.Title : null // <-- map title
                    }).ToList();

                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();

                vm.MentorRequests = contactItems
                    .Concat(opinionItems)
                    .OrderByDescending(x => x.Timestamp)
                    .ToList();
            }

            if (vm.IsStudent)
            {
                vm.StudentMessages = db.Messages
                    .Where(m => m.ToUserId == currentUserId)
                    .Include(m => m.FromUser)
                    .OrderByDescending(m => m.Timestamp)
                    .ToList();
            }

            return View(vm);
        }


        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<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();

            // update DB
            msg.Status = ContactStatus.Accepted;
            db.SaveChanges();

            // notify student by email 
            try
            {
                var student = msg.Student;
                var mentor = msg.Mentor;
                if (student != null && !string.IsNullOrEmpty(student.Email))
                {
                    var subject = $"Вашето барање е прифатено од {HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                <p>Вашето барање за контакт со <strong>{HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}</strong> беше <strong>прифатено</strong>.</p>
                <p>Можете да ја продолжите конверзацијата преку внатрешните пораки на сајтот: <a href='{Url.Action("Inbox", "Account", null, Request?.Url?.Scheme)}'>Inbox</a></p>
                <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(student.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (AcceptMessage): " + ex);
            }

            return Json(new
            {
                success = true,
                requestId = msg.Id,
                studentId = msg.StudentId,
                studentName = (msg.Student != null) ? (msg.Student.Name + " " + msg.Student.Surname) : "",
                subject = msg.SubjectName ?? "",
                selectedTopic = msg.TopicSuggestion != null ? msg.TopicSuggestion.Title : ""
            });
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<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();

            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();

            // Email the student
            try
            {
                var student = db.Users.Find(model.ToUserId) as Student;
                var mentor = db.Users.Find(currentUserId) as Mentor;
                if (student != null && !string.IsNullOrEmpty(student.Email))
                {
                    var subject = $"Порака од {HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)} на NajdiMentor";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                <p>Менторот <strong>{HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}</strong> ви испрати порака:</p>
                <hr/>
                <p>{HttpUtility.HtmlEncode(model.Body)}</p>
                <hr/>
                <p>За да одговорите, најавете се на: <a href='{Url.Action("ProfileRedirect", "Account", null, Request?.Url?.Scheme)}'>NajdiMentor</a></p>
                <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(student.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (SendReplyToStudent): " + ex);
            }

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

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public async Task<ActionResult> RejectMessage(int id)
        {
            var msg = db.MentorContacts.Include(c => c.Student).Include(c => c.Mentor).FirstOrDefault(c => c.Id == id);
            if (msg == null) return HttpNotFound();

            // optional: verify current user is owner (mentor)
            var currentUserId = User.Identity.GetUserId();
            if (msg.MentorId != currentUserId) return new HttpUnauthorizedResult();

            msg.Status = ContactStatus.Rejected;
            db.SaveChanges();

            // notify student by email (best-effort)
            try
            {
                var student = msg.Student;
                var mentor = msg.Mentor;
                if (student != null && !string.IsNullOrWhiteSpace(student.Email))
                {
                    var subject = $"Вашето барање за контакт е одбиено";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                <p>Вашето барање до <strong>{HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}</strong> беше <strong>одбиено</strong>.</p>
                <p>Ако сакате, можете да побарате друг ментор или да се обидете повторно.</p>
                <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(student.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (RejectMessage): " + ex);
            }

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

        [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)
                        .Include(c => c.TopicSuggestion)
                        .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,
                            Topic = c.TopicSuggestion != null ? c.TopicSuggestion.Title : null,
                            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 != null && o.Content.Length > 120) ? (o.Content.Substring(0, 120) + "…") : o.Content,
                            Topic = (string)null,                              // <-- make shapes identical
                            Timestamp = o.Timestamp.ToString("g")
                        }).ToList();

                    // now both lists have the same anonymous type, Concat works
                    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 async Task<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();

            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();

            // Email the mentor (best-effort)
            try
            {
                if (!string.IsNullOrWhiteSpace(mentor.Email))
                {
                    var student = db.Users.Find(studentId) as Student;
                    var subject = $"Ново мислење чека одобрување на NajdiMentor";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(mentor.Name)} {HttpUtility.HtmlEncode(mentor.Surname)},</p>
                <p>Студентот <strong>{HttpUtility.HtmlEncode(student?.Name)} {HttpUtility.HtmlEncode(student?.Surname)}</strong> остави мислење кое чека одобрување:</p>
                <blockquote>{HttpUtility.HtmlEncode(opinion.Content)}</blockquote>
                <p>Прегледајте го во вашиот <a href='{Url.Action("Inbox", "Account", null, Request?.Url?.Scheme)}'>Inbox</a>.</p>
                <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(mentor.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (SubmitOpinion): " + ex);
            }

            return Json(new { success = true, message = "Вашето мислење е пратено и чека одобрување.", id = opinion.Id });
        }


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

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

            try
            {
                var student = opinion.Student;
                var mentor = opinion.Mentor;
                if (student != null && !string.IsNullOrEmpty(student.Email))
                {
                    var subject = $"Вашето мислење е одобрено од {HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                <p>Вашето мислење за <strong>{HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}</strong> беше <strong>одобрено</strong> и ќе биде прикажано на профилот на менторот.</p>
                <p>Погледнете го: <a href='{Url.Action("MentorProfile", "Account", new { id = mentor?.Id }, Request?.Url?.Scheme)}'>Профил на менторот</a></p>
                <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(student.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (ApproveOpinion): " + ex);
            }

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

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

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

            try
            {
                var student = opinion.Student;
                var mentor = opinion.Mentor;
                if (student != null && !string.IsNullOrEmpty(student.Email))
                {
                    var subject = $"Вашето мислење е отфрлено од {HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                <p>Вашето мислење за <strong>{HttpUtility.HtmlEncode(mentor?.Name)} {HttpUtility.HtmlEncode(mentor?.Surname)}</strong> беше <strong>отфрлено</strong> од менторот.</p>
                <p>Ако имате прашања, контактирајте го менторот преку Inbox.</p>
                <hr/><p>Ова е автоматска порака.</p>";

                    await _emailService.SendEmailAsync(student.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (RejectOpinion): " + ex);
            }

            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
            var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
            return new ChallengeResult(provider, redirectUrl);
        }

        //
        // 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");
            }

            // If already has local account, sign in
            var signInResult = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
            if (signInResult == SignInStatus.Success)
                return RedirectToLocal(returnUrl);

            // Not linked: show ExternalLoginConfirmation view prefilled from claims
            var email = loginInfo.Email ?? loginInfo.ExternalIdentity?.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;
            var given = loginInfo.ExternalIdentity?.FindFirst(System.Security.Claims.ClaimTypes.GivenName)?.Value;
            var family = loginInfo.ExternalIdentity?.FindFirst(System.Security.Claims.ClaimTypes.Surname)?.Value;
            // fallback: try name claim
            if (string.IsNullOrWhiteSpace(given) && string.IsNullOrWhiteSpace(family))
            {
                var nameClaim = loginInfo.ExternalIdentity?.FindFirst(System.Security.Claims.ClaimTypes.Name)?.Value;
                if (!string.IsNullOrWhiteSpace(nameClaim))
                {
                    var parts = nameClaim.Split(' ');
                    if (parts.Length > 0) given = parts[0];
                    if (parts.Length > 1) family = string.Join(" ", parts.Skip(1));
                }
            }

            var vm = new WebApplication1.Models.ExternalLoginConfirmationViewModel
            {
                Email = email,
                Name = given,
                Surname = family,
                UserType = "Student" // default suggestion
            };

            ViewBag.ReturnUrl = returnUrl;
            ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
            return View("ExternalLoginConfirmation", vm);
        }

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

            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // Obtain external login info again
            var info = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (info == null)
            {
                ModelState.AddModelError("", "Не можам да ги вчитам информации за надворешната најава.");
                return View(model);
            }

            ApplicationUser user;
            if (model.UserType == "Student")
            {
                user = new Student
                {
                    UserName = model.Email,
                    Email = model.Email,
                    Name = model.Name,
                    Surname = model.Surname,
                    Biography = model.Biography,
                    Index = model.Index,
                    Major = model.Major,
                    Cycle = model.Cycle,
                    Semester = model.Semester
                };
            }
            else // Mentor
            {
                user = new Mentor
                {
                    UserName = model.Email,
                    Email = model.Email,
                    Name = model.Name,
                    Surname = model.Surname,
                    Biography = model.Biography,
                    Timeslots = model.Timeslots,
                    TypesOfProject = model.TypesOfProject,
                    Available = model.Available,
                    ImageURL = null
                };
            }

            // Create user 
            var result = await UserManager.CreateAsync(user);
            if (!result.Succeeded)
            {
                AddErrors(result);
                return View(model);
            }

            // Add the external login
            result = await UserManager.AddLoginAsync(user.Id, info.Login);
            if (!result.Succeeded)
            {
                AddErrors(result);
                return View(model);
            }

            if (model.UserType == "Student")
            {
                var student = db.Users
                                .OfType<Student>()
                                .Include(s => s.Subjects)
                                .Include(s => s.Topics)
                                .SingleOrDefault(u => u.Id == user.Id);

                if (student != null)
                {
                    // clear any placeholder (should be empty for a brand new user) and add incoming items
                    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 });

                    db.SaveChanges();
                }
            }
            else // Mentor
            {
                var mentor = db.Users
                               .OfType<Mentor>()
                               .Include(m => m.Subjects)
                               .Include(m => m.Topics)
                               .SingleOrDefault(u => u.Id == user.Id);

                if (mentor != null)
                {
                    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);

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

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LinkLogin(string provider)
        {
            // Request redirect to external login provider to link to current user
            return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
        }

        [AllowAnonymous]
        public async Task<ActionResult> LinkLoginCallback()
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
            if (loginInfo == null)
            {
                return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
            }
            var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
            if (result.Succeeded)
            {
                return RedirectToAction("Manage", new { Message = ManageMessageId.AddLoginSuccess });
            }
            return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
        }

        //
        // 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);
        }

        // POST: /Account/AddTopic
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult AddTopic(TopicSuggestionCreateModel model)
        {
            var currentUserId = User.Identity.GetUserId();
            var mentor = db.Users.OfType<Mentor>().FirstOrDefault(m => m.Id == currentUserId);
            if (mentor == null) return new HttpUnauthorizedResult();

            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToArray();
                return Json(new { success = false, errors });
            }

            var topic = new TopicSuggestion
            {
                MentorId = currentUserId,
                Title = model.Title,
                Description = model.Description,
                CreatedAt = DateTime.UtcNow
            };

            db.TopicSuggestions.Add(topic);
            db.SaveChanges();

            return Json(new
            {
                success = true,
                topic = new { topic.Id, topic.Title, topic.Description, topic.IsAssigned, topic.AssignedStudentId }
            });
        }

        // POST: /Account/EditTopic
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult EditTopic(int id, TopicSuggestionCreateModel model)
        {
            var currentUserId = User.Identity.GetUserId();
            var topic = db.TopicSuggestions.Find(id);
            if (topic == null) return HttpNotFound();
            if (topic.MentorId != currentUserId) return new HttpUnauthorizedResult();

            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToArray();
                return Json(new { success = false, errors });
            }

            topic.Title = model.Title;
            topic.Description = model.Description;
            db.SaveChanges();

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

        // POST: /Account/DeleteTopic
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult DeleteTopic(int id)
        {
            var currentUserId = User.Identity.GetUserId();
            var topic = db.TopicSuggestions.Find(id);
            if (topic == null) return HttpNotFound();
            if (topic.MentorId != currentUserId) return new HttpUnauthorizedResult();

            if (topic.IsAssigned)
            {
                // optional: prevent deletion of assigned topics
                return Json(new { success = false, message = "Cannot delete an assigned topic." });
            }

            db.TopicSuggestions.Remove(topic);
            db.SaveChanges();

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

        // POST: /Account/AssignTopic
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public async Task<ActionResult> AssignTopic(int topicId, string studentId)
        {
            var currentUserId = User.Identity.GetUserId();
            var topic = db.TopicSuggestions.Include(t => t.Mentor).FirstOrDefault(t => t.Id == topicId);
            if (topic == null) return HttpNotFound();
            if (topic.MentorId != currentUserId) return new HttpUnauthorizedResult();
            if (topic.IsAssigned) return Json(new { success = false, message = "Topic already assigned." });

            var student = db.Users.OfType<Student>().FirstOrDefault(s => s.Id == studentId);
            if (student == null) return Json(new { success = false, message = "Student not found." });

            topic.IsAssigned = true;
            topic.AssignedStudentId = studentId;
            topic.AssignedAt = DateTime.UtcNow;
            db.SaveChanges();

            // create an on-site message to student so it shows in their notifications/inbox
            var msg = new Message
            {
                FromUserId = currentUserId,
                ToUserId = studentId,
                Subject = $"Ви е доделенa тема: {topic.Title}",
                Body = $"Ви е доделена тема од менторот {topic.Mentor.Name} {topic.Mentor.Surname}: {topic.Title}\n\n{topic.Description}",
                Timestamp = DateTime.UtcNow,
                IsRead = false
            };
            db.Messages.Add(msg);
            db.SaveChanges();

            // Email the student (best-effort)
            try
            {
                if (!string.IsNullOrWhiteSpace(student.Email))
                {
                    var subject = $"Доделена ви е тема: {topic.Title}";
                    var body = $@"
                <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                <p>Менторот <strong>{HttpUtility.HtmlEncode(topic.Mentor.Name)} {HttpUtility.HtmlEncode(topic.Mentor.Surname)}</strong> ви додели тема:</p>
                <h4>{HttpUtility.HtmlEncode(topic.Title)}</h4>
                <p>{HttpUtility.HtmlEncode(topic.Description)}</p>
                <hr/><p>Ова е автоматска порака.</p>";
                    await _emailService.SendEmailAsync(student.Email, subject, body);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Email send failed (AssignTopic): " + ex);
            }

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

        // POST: /Account/UnassignTopic
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public async Task<ActionResult> UnassignTopic(int topicId)
        {
            var currentUserId = User.Identity.GetUserId();
            var topic = db.TopicSuggestions.Include(t => t.Mentor).Include(t => t.AssignedStudent).FirstOrDefault(t => t.Id == topicId);
            if (topic == null) return HttpNotFound();
            if (topic.MentorId != currentUserId) return new HttpUnauthorizedResult();
            if (!topic.IsAssigned) return Json(new { success = false, message = "Topic not assigned." });

            var student = topic.AssignedStudent;
            topic.IsAssigned = false;
            topic.AssignedStudentId = null;
            topic.AssignedAt = null;
            db.SaveChanges();

            // notify student with message
            if (student != null)
            {
                var msg = new Message
                {
                    FromUserId = currentUserId,
                    ToUserId = student.Id,
                    Subject = $"Тема {topic.Title} е оневозможена",
                    Body = $"Менторот {topic.Mentor.Name} {topic.Mentor.Surname} ја оневозможи или одзеде темата {topic.Title}.",
                    Timestamp = DateTime.UtcNow,
                    IsRead = false
                };
                db.Messages.Add(msg);
                db.SaveChanges();

                try
                {
                    if (!string.IsNullOrWhiteSpace(student.Email))
                    {
                        var subject = $"Тема {topic.Title} е одземена/оневозможена";
                        var body = $@"
                    <p>Почитуван/а {HttpUtility.HtmlEncode(student.Name)} {HttpUtility.HtmlEncode(student.Surname)},</p>
                    <p>Менторот <strong>{HttpUtility.HtmlEncode(topic.Mentor.Name)} {HttpUtility.HtmlEncode(topic.Mentor.Surname)}</strong> ја одзеде темата <strong>{HttpUtility.HtmlEncode(topic.Title)}</strong>.</p>
                    <hr/><p>Ова е автоматска порака.</p>";
                        await _emailService.SendEmailAsync(student.Email, subject, body);
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("Email send failed (UnassignTopic): " + ex);
                }
            }

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

        // GET: /Account/GetMentorTopics (returns JSON list of available topics for a mentor)
        [Authorize]
        public JsonResult GetMentorTopics(string mentorId)
        {
            var topics = db.TopicSuggestions
                           .Where(t => t.MentorId == mentorId && !t.IsAssigned)
                           .OrderByDescending(t => t.CreatedAt)
                           .Select(t => new { t.Id, t.Title, t.Description })
                           .ToList();

            return Json(new { success = true, topics }, JsonRequestBehavior.AllowGet);
        }

        [Authorize]
        public JsonResult GetTopicCandidates(int topicId)
        {
            var topic = db.TopicSuggestions.Find(topicId);
            if (topic == null) return Json(new { success = false, message = "Topic not found" }, JsonRequestBehavior.AllowGet);

            // candidates: students who contacted this mentor and optionally selected this topic, fallback to all students who contacted
            var candidates = db.MentorContacts
                .Where(c => c.MentorId == topic.MentorId)
                .Include(c => c.Student)
                .GroupBy(c => c.StudentId)
                .Select(g => new
                {
                    id = g.Key,
                    name = g.Select(x => x.Student.Name + " " + x.Student.Surname).FirstOrDefault()
                })
                .Where(x => x.id != null)
                .ToList();

            // ensure distinct and non-null
            candidates = candidates.GroupBy(x => x.id).Select(g => g.First()).ToList();

            return Json(new { success = true, candidates }, JsonRequestBehavior.AllowGet);
        }


        // debugger
        private readonly EmailService _emailService = new EmailService();

        [HttpGet]
        [Authorize]
        public async Task<ActionResult> SendTestEmail()
        {
            try
            {
                var userId = User.Identity.GetUserId();
                string to = null;
                if (!string.IsNullOrEmpty(userId))
                {
                    var user = db.Users.Find(userId);
                    to = user?.Email;
                }
                if (string.IsNullOrEmpty(to))
                    to = User.Identity.Name;

                if (string.IsNullOrEmpty(to))
                    return Content("No recipient email available for current user. Ensure user.Email or User.Identity.Name is set.");

                await _emailService.SendEmailAsync(
                    to,
                    "Test email from Најди Ментор",
                    "<p>This is a test message from your application. If you received this, SMTP is working.</p>",
                    "This is a test message from your application. If you received this, SMTP is working."
                );

                return Content("Test email sent successfully to " + to);
            }
            catch (Exception ex)
            {
                return Content("Email failed: " + ex.GetBaseException().Message);
            }
        }

        #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 string LoginProvider { get; set; }
            public string RedirectUri { get; set; }
            public string UserId { get; set; }

            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 override void ExecuteResult(ControllerContext context)
            {
                // Build authentication properties for the challenge
                var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
                if (UserId != null)
                    properties.Dictionary[XsrfKey] = UserId;

                // Issue the OWIN challenge; do NOT perform a manual redirect
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);

                // Ensure the response status is 401 so OWIN will convert it to a 302 AND write the correlation cookie
                // (HttpUnauthorizedResult sets this, so call base.ExecuteResult)
                base.ExecuteResult(context);
            }
        }
        #endregion
    }
}