using System;
using System.Collections.Generic;
using System.Linq;
#if !NET8
using System.Web.Mvc;
#endif

#if NET8
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
#endif

using Fab2ApprovalSystem.DMO;
using Fab2ApprovalSystem.Misc;
using Fab2ApprovalSystem.Models;
using Fab2ApprovalSystem.Utilities;
using Fab2ApprovalSystem.ViewModels;

#if !NET8
using Kendo.Mvc.Extensions;
#endif

namespace Fab2ApprovalSystem.Controllers;

[Authorize]
#if !NET8
[SessionExpireFilter]
#endif
#if NET8
[Route("[controller]")]
#endif
public class TrainingController : Controller {

    private readonly UserAccountDMO userDMO = new();
    private readonly AdminDMO adminDMO = new();
    private readonly TrainingDMO trainingDMO = new();
    private readonly ECN_DMO ecnDMO = new();
    private readonly AppSettings? _AppSettings = GlobalVars.AppSettings;

    // GET: Training
    public ActionResult Index() {
        return View();
    }

    public int Create(int ecnId) {
        ECN_DMO ecnDMO = new();

        // Delete old training if exists
        int oldTrainingId = trainingDMO.GetTrainingId(ecnId);
        if (oldTrainingId != 0) {
            trainingDMO.DeleteTraining(oldTrainingId);
        }

        int trainingId = trainingDMO.Create(ecnId);
        List<int> TrainingGroups = new();
        TrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(ecnId);
        string ECNTitle = ecnDMO.GetECN(ecnId).Title;
        List<int> Trainees = new();
        foreach (int group in TrainingGroups) {
            Trainees.AddRange(trainingDMO.GetTrainees(group));
        }
        Trainees = (from a in Trainees select a).Distinct().ToList();

        foreach (int trainee in Trainees) {
            int assignmentId = trainingDMO.CreateAssignment(trainingId, trainee);
            NotifyTrainee(trainee, assignmentId, ecnId, ECNTitle);
        }

        return trainingId;
    }

    public ActionResult AddUserToTrainingAdHoc(int trainingId, int traineeId, int ecnId) {
        if (GlobalVars.IsAdmin(GetSession())) {
            // Get ECN
            ECN ecn = ecnDMO.GetECN(ecnId);

            // Get User
            LoginModel user = userDMO.GetUserByID(traineeId);

            // Get Training
            Training training = trainingDMO.GetTraining(trainingId);
            if (ecn != null) {
                if (user != null) {
                    if (training != null) {
                        if (!trainingDMO.IsUserAssigned(traineeId, trainingId)) {
                            if (training.DeletedDate == null && !ecn.Deleted) {
                                // Both the ECN and training still exist
                                if (training.CompletedDate != null) {
                                    // Training is completed and now we need to re-open it.
                                    trainingDMO.reOpenTraining(trainingId);
                                    int assignmentId = trainingDMO.CreateAssignment(trainingId, traineeId);
                                    NotifyTrainee(traineeId, assignmentId, ecnId, ecn.Title);
                                    return Content("Success");
                                } else {
                                    // training is still open, just add a user and notify
                                    int assignmentId = trainingDMO.CreateAssignment(trainingId, traineeId);
                                    NotifyTrainee(traineeId, assignmentId, ecnId, ecn.Title);
                                    return Content("Success");
                                }
                            } else {
                                // Ecn or training task have been deleted.
                                return Content("Training or ECN has been deleted.");
                            }
                        } else {
                            return Content("User already has an open or completed assignment for this training.");
                        }
                    } else {
                        return Content("Invalid training id.");
                    }
                } else {
                    return Content("invalid userId");
                }
            } else {
                return Content("ECN invalid");
            }
        } else {
            return Content("Not Authorized");
        }
    }

    public ActionResult AddGroupToTrainingAdHoc(int trainingId, int groupId, int ecnId) {
        if (GlobalVars.IsAdmin(GetSession())) {
            ECN ecn = ecnDMO.GetECN(ecnId);
            Training training = trainingDMO.GetTraining(trainingId);
            TrainingGroup group = trainingDMO.GetTrainingGroupByID(groupId);
            List<int> groupMemberIds = trainingDMO.GetTrainees(groupId);
            int usersAdded = 0;

            if (ecn != null) {
                if (training != null) {
                    if (training.DeletedDate == null && !ecn.Deleted) {
                        if (training.CompletedDate != null) {
                            // Training is completed and now we need to re-open it.
                            foreach (int id in groupMemberIds) {
                                // Check to make sure user doesn't have an active assignment for this training
                                if (!trainingDMO.IsUserAssigned(id, trainingId)) {
                                    usersAdded++;
                                    int assignmentId = trainingDMO.CreateAssignment(trainingId, id);
                                    NotifyTrainee(id, assignmentId, ecnId, ecn.Title);
                                }
                            }
                            if (usersAdded > 0) {
                                trainingDMO.reOpenTraining(trainingId);
                            }
                        } else {
                            // training is still open, just add a users and notify
                            foreach (int id in groupMemberIds) {
                                // Check to make sure user doesn't have an active assignment for this training
                                if (!trainingDMO.IsUserAssigned(id, trainingId)) {
                                    usersAdded++;
                                    int assignmentId = trainingDMO.CreateAssignment(trainingId, id);
                                    NotifyTrainee(id, assignmentId, ecnId, ecn.Title);
                                }
                            }
                        }
                        if (usersAdded > 0) {
                            try {
                                trainingDMO.AddTrainingGroupToECN(ecnId, groupId);
                            } catch (Exception e) {
                                return Content(e.ToString());
                            }
                        }
                        return Content("Success. " + usersAdded + " users added.");
                    } else {
                        return Content("Training or ECN has been deleted.");
                    }
                } else {
                    return Content("Invalid training id.");
                }
            } else {
                return Content("ECN invalid");
            }
        } else {
            return Content("Not Authorized");
        }
    }

    public ActionResult DeleteTrainingByECN(int ECNNumber) {
        int trainingId = trainingDMO.GetTrainingId(ECNNumber);
        if (trainingId != 0) {
            trainingDMO.DeleteTraining(trainingId);
        }
        return RedirectToAction("ViewTrainings");
    }

    public ActionResult DeleteTrainingByID(int trainingId) {
        if (trainingId != 0) {
            trainingDMO.DeleteTraining(trainingId);
        }
        return RedirectToAction("ViewTrainings");
    }

    public void NotifyTrainee(int userId, int assignmentId, int ecnId, string title) {
        try {
            string recipient = userDMO.GetUserEmailByID(userId);
            TrainingHelper.NotifyTrainee(_AppSettings, userId, assignmentId, ecnId, title, recipient);
        } catch (Exception e) {
            string detailedException = "";
            try {
                detailedException = e.InnerException.ToString();
            } catch {
                detailedException = e.Message;
            }
        }
    }

    public ActionResult ViewTrainingPartial(int trainingID, int userID) {
        List<TrainingAssignment> TrainingData = trainingDMO.GetTrainingAssignmentsByUser(trainingID, userID);
        if (trainingID > 0) {
            ViewBag.ECNNumber = trainingDMO.GetTraining(trainingID).ECN;
        }
        return PartialView(TrainingData);
    }

    public ActionResult ViewTrainingDocsPartial(int trainingAssignmentId) {
        ViewBag.trainingAssignmentId = trainingAssignmentId;
        IEnumerable<TrainingDocAck> attachments = trainingDMO.GetAssignedDocs(trainingAssignmentId);
        return PartialView(attachments);
    }

    public ActionResult AcknowledgeDocument(int trainingAssignmentID, int trainingDocAckID) {
        // Check to see if acknowledgement is valid(Security Feature to protect data integrity)
        if (trainingDMO.CheckValidDocAck(trainingDocAckID)) {
            trainingDMO.AcknowledgeDocument(trainingDocAckID);
            bool isFinishedTrainingAssignment = trainingDMO.CheckTrainingAssignmentStatus(trainingAssignmentID);

            if (isFinishedTrainingAssignment) {
                try {
                    trainingDMO.UpdateAssignmentStatus(trainingAssignmentID);
                    bool isFinishedTraining = trainingDMO.CheckTrainingStatus(trainingAssignmentID);
                    if (isFinishedTraining) {
                        int TrainingID = trainingDMO.GetTrainingIdByAssignment(trainingAssignmentID);
                        trainingDMO.UpdateTrainingStatus(TrainingID);
                    }
                } catch (Exception e) {
                    string exception = e.ToString();
                    return Content(exception);
                }
            }
        }

        return Content("Marked Succesfully.");
    }

    public ActionResult AcknowledgeReviewNoDocuments(int trainingAssignmentID) {
        try {
            trainingDMO.UpdateAssignmentStatus(trainingAssignmentID);
            bool isFinishedTraining = trainingDMO.CheckTrainingStatus(trainingAssignmentID);
            if (isFinishedTraining) {
                int TrainingID = trainingDMO.GetTrainingIdByAssignment(trainingAssignmentID);
                trainingDMO.UpdateTrainingStatus(TrainingID);
            }
        } catch (Exception e) {
            string exception = e.ToString();
            return Content(exception, "application/json");
        }

        return Json(new { test = "Succesfully saved" });
    }

    public ActionResult TrainingReports() {
        return View();
    }

    public ActionResult TrainingReportsView(int? filterType, string filterValue) {
        ViewBag.TrainingGroups = adminDMO.GetTrainingGroups();
        IEnumerable<Training> trainingList = trainingDMO.GetAllTrainings();
        // Group Filter
        if (filterType == 1 && filterValue != "") {
            ViewBag.GroupFilter = filterValue;
            List<Training> filteredTraining = new();
            foreach (Training item in trainingList) {
                List<int> assignedTrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(item.ECN);
                foreach (int id in assignedTrainingGroups) {
                    if (filterValue == id.ToString()) {
                        filteredTraining.Add(item);
                    }
                }
            }
            trainingList = filteredTraining;
            return PartialView(trainingList);
        }
        // Status Filter
        if (filterType == 2 && filterValue != "") {
            List<Training> filteredTraining = new();
            switch (filterValue) {
                case "1":
                    // Completed
                    filteredTraining = (from a in trainingList where a.Status == true && a.Deleted != true select a).ToList();
                    break;
                case "2":
                    // In Progress
                    filteredTraining = (from a in trainingList where a.Status != true && a.Deleted != true select a).ToList();
                    break;
                case "3":
                    // Cancelled
                    filteredTraining = (from a in trainingList where a.Deleted == true select a).ToList();
                    break;
            }
            trainingList = filteredTraining;
            return PartialView(trainingList);
        }
        // Default return all.
        else {
            return PartialView(trainingList);
        }
    }

    public ActionResult ViewTrainingAssignmentsReportView(int trainingID, string statusFilter, string groupFilter) {
        bool? trainingStatus = trainingDMO.GetTraining(trainingID).Status;
        int ECNNumber = trainingDMO.GetTraining(trainingID).ECN;
        string ECNTitle = ecnDMO.GetECN(ECNNumber).Title;
        ViewBag.TrainingGroups = adminDMO.GetTrainingGroups();
        ViewBag.TrainingStatus = trainingStatus;
        ViewBag.ECNNumber = ECNNumber;
        ViewBag.ECNTitle = ECNTitle;
        ViewBag.trainingID = trainingID;
        IEnumerable<TrainingAssignment> trainingAssignments = trainingDMO.GetAllTrainingAssignments(trainingID);
        // Calculate Percent Complete:
        float percentComplete = 0;
        float assignmentCount = (from a in trainingAssignments where a.Deleted != true select a).Count();
        float totalCompleted = 0;
        foreach (TrainingAssignment assignment in trainingAssignments) {
            if (assignment.status && assignment.Deleted != true) {
                totalCompleted++;
            }
        }
        percentComplete = totalCompleted / assignmentCount * 100;
        ViewBag.PercentComplete = percentComplete.ToString("0.00") + "%";

        if (groupFilter != "" && groupFilter != null) {
            ViewBag.GroupFilter = groupFilter;
            List<TrainingAssignment> groupFilteredTraining = new();
            List<int> groupMemberIds = trainingDMO.GetTrainees(Convert.ToInt32(groupFilter));
            foreach (TrainingAssignment assignment in trainingAssignments) {
                if (trainingDMO.isUserTrainingMember(Convert.ToInt32(groupFilter), assignment.UserID)) {
                    groupFilteredTraining.Add(assignment);
                }
            }
            trainingAssignments = groupFilteredTraining;
        }
        if (statusFilter != "" && statusFilter != null) {
            List<TrainingAssignment> filteredTraining = new();
            switch (statusFilter) {
                case "1":
                    // Completed
                    filteredTraining = (from a in trainingAssignments where a.status && a.Deleted != true select a).ToList();
                    break;
                case "2":
                    // In Progress
                    filteredTraining = (from a in trainingAssignments where !a.status && a.Deleted != true select a).ToList();
                    break;
                case "3":
                    // Cancelled
                    filteredTraining = (from a in trainingAssignments where a.Deleted == true select a).ToList();
                    break;
                default:
                    filteredTraining = (from a in trainingAssignments select a).ToList();
                    break;
            }
            trainingAssignments = filteredTraining;
        }

        return PartialView(trainingAssignments);
    }

    public ActionResult ViewTrainingAssignments(int trainingID) {
        bool? trainingStatus = trainingDMO.GetTraining(trainingID).Status;
        int ECNNumber = trainingDMO.GetTraining(trainingID).ECN;
        string ECNTitle = ecnDMO.GetECN(ECNNumber).Title;
        ViewBag.TrainingStatus = trainingStatus;
        ViewBag.ECNNumber = ECNNumber;
        ViewBag.ECNTitle = ECNTitle;
        ViewBag.TrainingId = trainingID;
        ViewBag.AllUsers = userDMO.GetAllActiveUsers();
        ViewBag.AllGroups = trainingDMO.GetTrainingGroups();
        IEnumerable<TrainingAssignment> trainingAssignments = trainingDMO.GetTrainingAssignments(trainingID);

        return View(trainingAssignments);
    }

    /// <summary>
    /// Method to return all the training assignments for a specified user
    /// </summary>
    public ActionResult ViewMyTrainingAssignments() {
        int userID = GlobalVars.GetUserId(GetSession());
        List<TrainingAssignment> assignments = trainingDMO.GetTrainingAssignmentsByUserID(userID);
        List<ECNTrainingAssignments> ViewData = new();
        foreach (TrainingAssignment assignment in assignments) {
            Training training = trainingDMO.GetTraining(assignment.TrainingID);
            if (training != null && !assignment.status) {
                int ecnID = training.ECN;
                ViewData.Add(new ECNTrainingAssignments {
                    TrainingAssignmentID = assignment.ID,
                    ECN_ID = ecnID,
                    TrainingID = assignment.TrainingID,
                    DateAssigned = assignment.DateAssigned,
                    DateCompleted = assignment.DateCompleted,
                    Status = assignment.status
                });
            }
        }

        return View(ViewData);
    }

    /// <summary>
    /// Method to return all assigned documents for a specified training assignment
    /// </summary>
    public ActionResult ViewMyTrainingAssignment(int assignmentID, int ECNNumber) {
        ViewBag.ECNNumber = ECNNumber;
        ViewBag.AssignmentID = assignmentID;
        ViewBag.IsCompleted = trainingDMO.GetAssignment(assignmentID).status;
        return View(trainingDMO.GetAssignedDocs(assignmentID));
    }

    public ActionResult ViewTrainings(int? filterType, string filterValue) {
        IEnumerable<Training> AllTrainings = trainingDMO.GetTrainings();
        ViewBag.TrainingGroups = adminDMO.GetTrainingGroups();
        ViewBag.AllGroups = trainingDMO.GetTrainingGroups();
        // Group Filter
        if (filterType == 1 && filterValue != "") {
            ViewBag.GroupFilter = filterValue;
            List<Training> filteredTraining = new();
            foreach (Training item in AllTrainings) {
                List<int> assignedTrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(item.ECN);
                foreach (int id in assignedTrainingGroups) {
                    if (filterValue == id.ToString()) {
                        filteredTraining.Add(item);
                    }
                }
            }
            AllTrainings = filteredTraining;
            return View(AllTrainings);
        } else {
            ViewBag.AllGroups = trainingDMO.GetTrainingGroups();
            return View(AllTrainings);
        }
    }

    public ActionResult ViewAllTrainings() {
        return View();
    }

    public ActionResult DeleteAssignment(int assignmentId) {
        trainingDMO.DeleteTrainingAssignment(assignmentId);
        trainingDMO.DeleteTrainingDocAck(assignmentId);

        // Below checks and updates the training status
        // TO-DO Put this in its own method.
        bool isFinishedTrainingAssignment = trainingDMO.CheckTrainingAssignmentStatus(assignmentId);

        if (isFinishedTrainingAssignment) {
            try {
                trainingDMO.UpdateAssignmentStatus(assignmentId);
                bool isFinishedTraining = trainingDMO.CheckTrainingStatus(assignmentId);
                if (isFinishedTraining) {
                    int TrainingID = trainingDMO.GetTrainingIdByAssignment(assignmentId);
                    trainingDMO.UpdateTrainingStatus(TrainingID);
                }
            } catch (Exception e) {
                string exception = e.ToString();
                return Content(exception, "application/json");
            }
        }

        return Json(new { test = "Succesfully saved" });
    }

    public ActionResult ManuallyExecuteECNTraining(int ecnId, int[] trainingGroupsIn) {
        if (GlobalVars.IsAdmin(GetSession())) {
            List<int> newTrainingGroupIds = new(trainingGroupsIn);
            ECN ecn = ecnDMO.GetECN(ecnId);
            if (ecn != null) {
                if (ecn.CloseDate != null) {
                    if (newTrainingGroupIds.Count > 0) {
                        // Check each assigned group id and see if it's already saved to the ECN
                        List<int> assignedTrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(ecnId);
                        IEnumerable<int> onlyNewTrainingIds = newTrainingGroupIds.Except(assignedTrainingGroups);
                        try {
                            foreach (int trainingId in onlyNewTrainingIds) {
                                trainingDMO.AddTrainingGroupToECN(ecnId, trainingId);
                            }

                            trainingDMO.SetTrainingFlag(ecnId);
                            Create(ecnId);
                            return Content("Success");
                        } catch (Exception e) {
                            return Content("Failed: " + e.Message.ToString());
                        }
                    } else {
                        return Content("There were no training groups to assign to. Please select at least one training groups.");
                    }
                } else {
                    return Content("Selected ECN hasn't been approved yet.");
                }
            } else {
                return Content("Invalid ECN");
            }
        } else {
            return Content("Not Autthorized");
        }
    }

    public ActionResult CheckECN(int ecnId) {
        ECN ecn = ecnDMO.GetECN(ecnId);
        if (ecn != null) {
            if (ecn.CloseDate != null) {
                List<int> trainingGroupIds = trainingDMO.GetECNAssignedTrainingGroups(ecnId);
                List<TrainingGroup> assignedGroups = new();
                foreach (int trainingGroupId in trainingGroupIds) {
                    TrainingGroup trainingGroup = trainingDMO.GetTrainingGroupByID(trainingGroupId);
                    assignedGroups.Add(trainingGroup);
                }
                return Json(trainingGroupIds.ToList());
            } else {
                return Content("ECN not yet approved.");
            }
        } else {
            return Content("That ECN wasn't found.");
        }
    }

    public bool RunTrainingReport() {
        bool isSuccess = false;
        try {
            string emailBody = "<h1>Mesa Approval Open Training Assignments Daily Report</h1> <br />";
            emailBody += "<p>The following contains open training assignments in the Mesa Approval system. ";
            emailBody += "Please ensure the following users complete their training assignments. ";
            emailBody += "Dates in red font identify past due training tasks.</p><br />";
            emailBody += "<style>table,th,td{border: 1px solid black;}</style>";
            // Get all users set up to receive the training report email.
            List<TrainingReportUser> trainingReportUsers = adminDMO.GetTrainingReportUsers();
            List<string> emailList = new();
            foreach (TrainingReportUser user in trainingReportUsers) {
                string userEmail = userDMO.GetUserByID(user.UserId).Email;
                emailList.Add(userEmail);
            }
            // Get a list of open trainings
            List<Training> openTrainings = trainingDMO.GetAllOpenTrainings();

            foreach (Training training in openTrainings) {
                string trainingSection = "";
                int trainingSectionUserCount = 0;
                string ecnTitle = ecnDMO.GetECN(training.ECN).Title;
                trainingSection += "<h3>" + training.ECN + " - " + ecnTitle + "</h3>";

                trainingSection += "<table>";
                trainingSection += "<tr><th>Name</th><th>Date Assigned</th></tr>";
                List<TrainingAssignment> openAssignments = trainingDMO.GetOpenAssignmentsByTrainingID(training.TrainingID);
                foreach (TrainingAssignment assignment in openAssignments) {
                    if (!userDMO.GetUserByID(assignment.UserID).OOO) {
                        trainingSectionUserCount++;

                        DateTime? assignmentDate = assignment.DateAssigned;

                        string DateAssigned = assignmentDate.HasValue ? assignmentDate.Value.ToString("MM/dd/yyyy") : "<not available>";

                        if (assignmentDate.HasValue && (DateTime.Now.Date - assignmentDate.Value.Date).TotalDays > 15) {
                            trainingSection += "<tr><td>" + assignment.FullName + "</td><td style=\"color:red;\">" + DateAssigned + "</td>";
                        } else {
                            trainingSection += "<tr><td>" + assignment.FullName + "</td><td>" + DateAssigned + "</td>";
                        }

                        trainingSection += "</tr>";
                    }
                }
                trainingSection += "</table>";
                if (trainingSectionUserCount > 0)
                    emailBody += trainingSection;
            }
            string recipientEmail = "";
            List<string> ccRecipients = emailList;
            EmailUtilities.SendNotification("MesaFabApproval@infineon.com", ccRecipients, "Daily Open Training Report", emailBody);
            isSuccess = true;
        } catch {
            isSuccess = false;
        }
        return isSuccess;
    }

#if !NET8

    private System.Web.HttpSessionStateBase GetSession() =>
        Session;

    private JsonResult GetJsonResult(object? data) =>
        Json(data, JsonRequestBehavior.AllowGet);

    private bool IsAjaxRequest() =>
        Request.IsAjaxRequest();

#endif

#if NET8

    private Microsoft.AspNetCore.Http.ISession GetSession() =>
        HttpContext.Session;

    private JsonResult GetJsonResult(object? data) =>
        Json(data);

    private bool IsAjaxRequest() =>
        Request.Headers.TryGetValue("X-Requested-With", out Microsoft.Extensions.Primitives.StringValues strings) && strings[0] == "XMLHttpRequest";

#endif

    private string GetUserIdentityName() =>
        @User.Identity.Name;

}