using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NTDLS.DelegateThreadPooling;
using NTDLS.Helpers;
using System.Reflection;
using System.Security.Claims;
using ZelWiki.Caching;
using ZelWiki.Engine.Implementation;
using ZelWiki.Engine.Implementation.Utility;
using ZelWiki.Engine.Library.Interfaces;
using ZelWiki.Library;
using ZelWiki.Models;
using ZelWiki.Models.DataModels;
using ZelWiki.Models.ViewModels.Admin;
using ZelWiki.Models.ViewModels.Page;
using ZelWiki.Models.ViewModels.Profile;
using ZelWiki.Models.ViewModels.Shared;
using ZelWiki.Models.ViewModels.Utility;
using ZelWiki.Repository;
using static ZelWiki.Library.Constants;
using Constants = ZelWiki.Library.Constants;

namespace ZelWiki.Controllers
{
    [Authorize]
    [Route("[controller]")]
    public class AdminController(IZelEngine zelEngine, SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager)
        : WikiControllerBase(signInManager, userManager)
    {
        #region Metrics.

        [Authorize]
        [HttpGet("Database")]
        public ActionResult Database()
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Database";

            var versions = SpannedRepository.GetDatabaseVersions();
            var pageCounts = SpannedRepository.GetDatabasePageCounts();
            var pageSizes = SpannedRepository.GetDatabasePageSizes();

            var info = new List<DatabaseInfo>();

            foreach (var version in versions)
            {
                var pageCount = pageCounts.FirstOrDefault(o => o.Name == version.Name).PageCount;
                var pageSize = pageSizes.FirstOrDefault(o => o.Name == version.Name).PageSize;

                info.Add(new DatabaseInfo
                {
                    Name = version.Name,
                    Version = version.Version,
                    PageCount = pageCount,
                    PageSize = pageSize,
                    DatabaseSize = pageCount * pageSize
                });
            }

            var model = new DatabaseViewModel()
            {
                Info = info
            };

            return View(model);
        }

        [Authorize]
        [HttpPost("Database/{databaseAction}/{database}")]
        public ActionResult Database(ConfirmActionViewModel model, string databaseAction, string database)
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Database";

            if (model.UserSelection == true)
            {
                try
                {
                    switch (databaseAction)
                    {
                        case "Optimize":
                            {
                                var resultText = SpannedRepository.OptimizeDatabase(database);
                                return NotifyOfSuccess($"操作成功  {resultText}", model.YesRedirectURL);
                            }
                        case "Vacuum":
                            {
                                var resultText = SpannedRepository.OptimizeDatabase(database);
                                return NotifyOfSuccess($"操作成功 {resultText}", model.YesRedirectURL);
                            }
                        case "Verify":
                            {
                                var resultText = SpannedRepository.OptimizeDatabase(database);
                                return NotifyOfSuccess($"验证完成 {resultText}", model.YesRedirectURL);
                            }
                    }
                }
                catch (Exception ex)
                {
                    return NotifyOfError($"操作失败: {ex.Message}.", model.YesRedirectURL);
                }

                return NotifyOfError($"Unknown database action: '{databaseAction}'.", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        #endregion

        #region Metrics.

        [Authorize]
        [HttpGet("Metrics")]
        public ActionResult Metrics()
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Metrics";

            var version = string.Join('.', (Assembly.GetExecutingAssembly()
                .GetName().Version?.ToString() ?? "0.0.0.0").Split('.').Take(3)); //Major.Minor.Patch

            var model = new MetricsViewModel()
            {
                Metrics = ConfigurationRepository.GetWikiDatabaseMetrics(),
                ApplicationVersion = version
            };

            return View(model);
        }

        [Authorize]
        [HttpPost("PurgeCompilationStatistics")]
        public ActionResult PurgeCompilationStatistics(ConfirmActionViewModel model)
        {
            SessionState.RequireAdminPermission();

            if (model.UserSelection == true)
            {
                StatisticsRepository.PurgeCompilationStatistics();
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("PurgeMemoryCache")]
        public ActionResult PurgeMemoryCache(ConfirmActionViewModel model)
        {
            SessionState.RequireAdminPermission();

            if (model.UserSelection == true)
            {
                WikiCache.Clear();
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        #endregion

        #region Compilation Statistics.

        [Authorize]
        [HttpGet("CompilationStatistics")]
        public ActionResult CompilationStatistics()
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Compilations Statistics";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new PageCompilationStatisticsViewModel()
            {
                Statistics = StatisticsRepository.GetCompilationStatisticsPaged(pageNumber, orderBy, orderByDirection),
            };

            model.PaginationPageCount = (model.Statistics.FirstOrDefault()?.PaginationPageCount ?? 0);

            model.Statistics.ForEach(o =>
            {
                o.LatestBuild = SessionState.LocalizeDateTime(o.LatestBuild);
            });

            return View(model);
        }

        #endregion

        #region Moderate.

        [Authorize]
        [HttpGet("Moderate")]
        public ActionResult Moderate()
        {
            SessionState.RequireModeratePermission();
            SessionState.Page.Name = $"Page Moderation";

            var instruction = GetQueryValue("Instruction");
            if (instruction != null)
            {
                var model = new PageModerateViewModel()
                {
                    Pages = PageRepository.GetAllPagesByInstructionPaged(GetQueryValue("page", 1), instruction),
                    Instruction = instruction,
                    Instructions = typeof(WikiInstruction).GetProperties().Select(o => o.Name).ToList()
                };

                model.PaginationPageCount = (model.Pages.FirstOrDefault()?.PaginationPageCount ?? 0);

                if (model.Pages != null && model.Pages.Count > 0)
                {
                    model.Pages.ForEach(o =>
                    {
                        o.CreatedDate = SessionState.LocalizeDateTime(o.CreatedDate);
                        o.ModifiedDate = SessionState.LocalizeDateTime(o.ModifiedDate);
                    });
                }

                return View(model);
            }

            return View(new PageModerateViewModel()
            {
                Pages = new(),
                Instruction = string.Empty,
                Instructions = typeof(WikiInstruction).GetProperties().Select(o => o.Name).ToList()
            });
        }

        #endregion

        #region Missing Pages.

        [Authorize]
        [HttpGet("MissingPages")]
        public ActionResult MissingPages()
        {
            SessionState.RequireModeratePermission();
            SessionState.Page.Name = $"Missing Pages";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new MissingPagesViewModel()
            {
                Pages = PageRepository.GetMissingPagesPaged(pageNumber, orderBy, orderByDirection)
            };

            model.PaginationPageCount = (model.Pages.FirstOrDefault()?.PaginationPageCount ?? 0);

            return View(model);
        }

        #endregion

        #region Namespaces.

        [Authorize]
        [HttpGet("Namespaces")]
        public ActionResult Namespaces()
        {
            SessionState.RequireModeratePermission();
            SessionState.Page.Name = $"Namespaces";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new NamespacesViewModel()
            {
                Namespaces = PageRepository.GetAllNamespacesPaged(pageNumber, orderBy, orderByDirection),
            };

            model.PaginationPageCount = (model.Namespaces.FirstOrDefault()?.PaginationPageCount ?? 0);

            return View(model);
        }

        [Authorize]
        [HttpGet("Namespace/{namespaceName?}")]
        public ActionResult Namespace(string? namespaceName = null)
        {
            SessionState.RequireModeratePermission();
            SessionState.Page.Name = $"Namespace";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new NamespaceViewModel()
            {
                Pages = PageRepository.GetAllNamespacePagesPaged(pageNumber, namespaceName ?? string.Empty, orderBy, orderByDirection),
                Namespace = namespaceName ?? string.Empty
            };

            model.PaginationPageCount = (model.Pages.FirstOrDefault()?.PaginationPageCount ?? 0);

            if (model.Pages != null && model.Pages.Count > 0)
            {
                model.Pages.ForEach(o =>
                {
                    o.CreatedDate = SessionState.LocalizeDateTime(o.CreatedDate);
                    o.ModifiedDate = SessionState.LocalizeDateTime(o.ModifiedDate);
                });
            }

            return View(model);
        }

        #endregion

        #region Pages.

        [Authorize]
        [HttpGet("Pages")]
        public ActionResult Pages()
        {
            SessionState.RequireModeratePermission();
            SessionState.Page.Name = $"Pages";

            var searchString = GetQueryValue("SearchString");
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new PagesViewModel()
            {
                Pages = PageRepository.GetAllPagesPaged(GetQueryValue("page", 1), orderBy, orderByDirection, Utility.SplitToTokens(searchString)),
                SearchString = searchString ?? string.Empty
            };

            model.PaginationPageCount = (model.Pages.FirstOrDefault()?.PaginationPageCount ?? 0);

            if (model.Pages != null && model.Pages.Count > 0)
            {
                model.Pages.ForEach(o =>
                {
                    o.CreatedDate = SessionState.LocalizeDateTime(o.CreatedDate);
                    o.ModifiedDate = SessionState.LocalizeDateTime(o.ModifiedDate);
                });
            }

            return View(model);
        }

        #endregion

        #region Revisions.

        [Authorize]
        [HttpPost("RevertPageRevision/{givenCanonical}/{revision:int}")]
        public ActionResult Revert(string givenCanonical, int revision, ConfirmActionViewModel model)
        {
            SessionState.RequireModeratePermission();

            var pageNavigation = NamespaceNavigation.CleanAndValidate(givenCanonical);

            if (model.UserSelection == true)
            {
                var page = PageRepository.GetPageRevisionByNavigation(pageNavigation, revision).EnsureNotNull();

                int currentPageRevision = PageRepository.GetCurrentPageRevision(page.Id);
                if (revision >= currentPageRevision)
                {
                    return NotifyOfError("操作失败");
                }

                Helpers.UpsertPage(zelEngine, page, SessionState);

                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpGet("DeletedPageRevisions/{pageId:int}")]
        public ActionResult DeletedPageRevisions(int pageId)
        {
            SessionState.RequireModeratePermission();

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new DeletedPagesRevisionsViewModel()
            {
                Revisions = PageRepository.GetDeletedPageRevisionsByIdPaged(pageId, pageNumber, orderBy, orderByDirection)
            };

            var page = PageRepository.GetLimitedPageInfoByIdAndRevision(pageId);
            if (page == null)
            {
                return NotifyOfError("找不到指定页面");
            }

            model.Name = page.Name;
            model.Namespace = page.Namespace;
            model.Navigation = page.Navigation;
            model.PageId = pageId;
            model.PaginationPageCount = (model.Revisions.FirstOrDefault()?.PaginationPageCount ?? 0);

            model.Revisions.ForEach(o =>
            {
                o.DeletedDate = SessionState.LocalizeDateTime(o.DeletedDate);
            });

            return View(model);
        }

        [Authorize]
        [HttpGet("DeletedPageRevision/{pageId:int}/{revision:int}")]
        public ActionResult DeletedPageRevision(int pageId, int revision)
        {
            SessionState.RequireModeratePermission();

            var model = new DeletedPageRevisionViewModel();

            var page = PageRepository.GetDeletedPageRevisionById(pageId, revision);

            if (page != null)
            {
                var state = zelEngine.Transform(SessionState, page);
                model.PageId = pageId;
                model.Revision = pageId;
                model.Body = state.HtmlResult;
                model.DeletedDate = SessionState.LocalizeDateTime(page.DeletedDate);
                model.DeletedByUserName = page.DeletedByUserName;
            }

            return View(model);
        }

        [Authorize]
        [HttpGet("PageRevisions/{givenCanonical}")]
        public ActionResult PageRevisions(string givenCanonical)
        {
            SessionState.RequireModeratePermission();

            var pageNavigation = NamespaceNavigation.CleanAndValidate(givenCanonical);

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new PageRevisionsViewModel()
            {
                Revisions = PageRepository.GetPageRevisionsInfoByNavigationPaged(pageNavigation, pageNumber, orderBy, orderByDirection)
            };

            model.PaginationPageCount = (model.Revisions.FirstOrDefault()?.PaginationPageCount ?? 0);

            model.Revisions.ForEach(o =>
            {
                o.CreatedDate = SessionState.LocalizeDateTime(o.CreatedDate);
                o.ModifiedDate = SessionState.LocalizeDateTime(o.ModifiedDate);
            });

            foreach (var p in model.Revisions)
            {
                var thisRev = PageRepository.GetPageRevisionByNavigation(p.Navigation, p.Revision);
                var prevRev = PageRepository.GetPageRevisionByNavigation(p.Navigation, p.Revision - 1);
                p.ChangeSummary = Differentiator.GetComparisonSummary(thisRev?.Body ?? "", prevRev?.Body ?? "");
            }

            if (model.Revisions != null && model.Revisions.Count > 0)
            {
                SessionState.SetPageId(model.Revisions.First().PageId);
            }

            return View(model);
        }

        [Authorize]
        [HttpPost("DeletePageRevision/{givenCanonical}/{revision:int}")]
        public ActionResult DeletePageRevision(ConfirmActionViewModel model, string givenCanonical, int revision)
        {
            SessionState.RequireModeratePermission();

            var pageNavigation = NamespaceNavigation.CleanAndValidate(givenCanonical);

            if (model.UserSelection == true)
            {
                var page = PageRepository.GetPageInfoByNavigation(pageNavigation);
                if (page == null)
                {
                    return NotifyOfError("无法找到指定页面");
                }

                int revisionCount = PageRepository.GetPageRevisionCountByPageId(page.Id);
                if (revisionCount <= 1)
                {
                    return NotifyOfError("唯一修订无法删除");
                }

                //If we are deleting the latest revision, then we need to grab the previous
                //  version and make it the latest then delete the specified revision.
                if (revision >= page.Revision)
                {
                    int previousRevision = PageRepository.GetPagePreviousRevision(page.Id, revision);
                    var previousPageRevision = PageRepository.GetPageRevisionByNavigation(pageNavigation, previousRevision).EnsureNotNull();
                    Helpers.UpsertPage(zelEngine, previousPageRevision, SessionState);
                }

                PageRepository.MovePageRevisionToDeletedById(page.Id, revision, SessionState.Profile.EnsureNotNull().UserId);

                return NotifyOfSuccess("操作成功页面修订已移至删除队列", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        #endregion

        #region Deleted Pages.

        [Authorize]
        [HttpGet("DeletedPage/{pageId}")]
        public ActionResult DeletedPage(int pageId)
        {
            SessionState.RequireModeratePermission();

            var model = new DeletedPageViewModel();

            var page = PageRepository.GetDeletedPageById(pageId);

            if (page != null)
            {
                var state = zelEngine.Transform(SessionState, page);
                model.PageId = pageId;
                model.Body = state.HtmlResult;
                model.DeletedDate = SessionState.LocalizeDateTime(page.ModifiedDate);
                model.DeletedByUserName = page.DeletedByUserName;
            }

            return View(model);
        }

        [Authorize]
        [HttpGet("DeletedPages")]
        public ActionResult DeletedPages()
        {
            SessionState.RequireModeratePermission();

            var searchString = GetQueryValue("SearchString", string.Empty);
            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new DeletedPagesViewModel()
            {
                Pages = PageRepository.GetAllDeletedPagesPaged(pageNumber, orderBy, orderByDirection, Utility.SplitToTokens(searchString)),
                SearchString = searchString
            };

            model.PaginationPageCount = (model.Pages.FirstOrDefault()?.PaginationPageCount ?? 0);

            return View(model);
        }

        [Authorize]
        [HttpPost("RebuildAllPages")]
        public ActionResult RebuildAllPages(ConfirmActionViewModel model)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                foreach (var page in PageRepository.GetAllPages())
                {
                    Helpers.RefreshPageMetadata(zelEngine, page, SessionState);
                }
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("PreCacheAllPages")]
        public ActionResult PreCacheAllPages(ConfirmActionViewModel model)
        {
            SessionState.RequireModeratePermission();

            int threadCount = Environment.ProcessorCount > 1 ? Environment.ProcessorCount / 2 : Environment.ProcessorCount;

            var pool = new DelegateThreadPool(threadCount, 0);

            if (model.UserSelection == true)
            {
                var workload = pool.CreateChildPool();

                foreach (var page in PageRepository.GetAllPages())
                {
                    workload.Enqueue(() =>
                    {
                        string queryKey = string.Empty;
                        foreach (var query in Request.Query)
                        {
                            queryKey += $"{query.Key}:{query.Value}";
                        }

                        var cacheKey = WikiCacheKeyFunction.Build(WikiCache.Category.Page, [page.Navigation, page.Revision, queryKey]);
                        if (WikiCache.Contains(cacheKey) == false)
                        {
                            var state = zelEngine.Transform(SessionState, page, page.Revision);
                            page.Body = state.HtmlResult;

                            if (state.ProcessingInstructions.Contains(WikiInstruction.NoCache) == false)
                            {
                                WikiCache.Put(cacheKey, state.HtmlResult); //This is cleared with the call to Cache.ClearCategory($"Page:{page.Navigation}");
                            }
                        }
                    });
                }

                workload.WaitForCompletion();

                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("TruncatePageRevisions")]
        public ActionResult TruncatePageRevisions(ConfirmActionViewModel model)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.TruncateAllPageRevisions("YES");
                WikiCache.Clear();
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("PurgeDeletedPageRevisions/{pageId:int}")]
        public ActionResult PurgeDeletedPageRevisions(ConfirmActionViewModel model, int pageId)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.PurgeDeletedPageRevisionsByPageId(pageId);
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("PurgeDeletedPageRevision/{pageId:int}/{revision:int}")]
        public ActionResult PurgeDeletedPageRevision(ConfirmActionViewModel model, int pageId, int revision)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.PurgeDeletedPageRevisionByPageIdAndRevision(pageId, revision);
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("RestoreDeletedPageRevision/{pageId:int}/{revision:int}")]
        public ActionResult RestoreDeletedPageRevision(ConfirmActionViewModel model, int pageId, int revision)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.RestoreDeletedPageRevisionByPageIdAndRevision(pageId, revision);
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("PurgeDeletedPages")]
        public ActionResult PurgeDeletedPages(ConfirmActionViewModel model)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.PurgeDeletedPages();
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("PurgeDeletedPage/{pageId:int}")]
        public ActionResult PurgeDeletedPage(ConfirmActionViewModel model, int pageId)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.PurgeDeletedPageByPageId(pageId);
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("DeletePage/{pageId:int}")]
        public ActionResult DeletePage(ConfirmActionViewModel model, int pageId)
        {
            SessionState.RequireAdminPermission();

            if (model.UserSelection == true)
            {
                PageRepository.MovePageToDeletedById(pageId, SessionState.Profile.EnsureNotNull().UserId);
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        [Authorize]
        [HttpPost("RestoreDeletedPage/{pageId:int}")]
        public ActionResult RestoreDeletedPage(ConfirmActionViewModel model, int pageId)
        {
            SessionState.RequireModeratePermission();

            if (model.UserSelection == true)
            {
                PageRepository.RestoreDeletedPageByPageId(pageId);
                var page = PageRepository.GetLatestPageRevisionById(pageId);
                if (page != null)
                {
                    Helpers.RefreshPageMetadata(zelEngine, page, SessionState);
                }
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        #endregion

        #region Files.

        [Authorize]
        [HttpGet("OrphanedPageAttachments")]
        public ActionResult OrphanedPageAttachments()
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Orphaned Page Attachments";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new OrphanedPageAttachmentsViewModel()
            {
                Files = PageFileRepository.GetOrphanedPageAttachmentsPaged(pageNumber, orderBy, orderByDirection),
            };

            model.PaginationPageCount = (model.Files.FirstOrDefault()?.PaginationPageCount ?? 0);

            /* Localization:
            if (model.Files != null && model.Files.Count > 0)
            {
                model.Files.ForEach(o =>
                {
                    o.CreatedDate = SessionState.LocalizeDateTime(o.CreatedDate);
                    o.ModifiedDate = SessionState.LocalizeDateTime(o.ModifiedDate);
                });
            }
            */

            return View(model);
        }

        [Authorize]
        [HttpPost("PurgeOrphanedAttachments")]
        public ActionResult PurgeOrphanedAttachments(ConfirmActionViewModel model)
        {
            SessionState.RequireAdminPermission();

            if (model.UserSelection == true)
            {
                PageFileRepository.PurgeOrphanedPageAttachments();
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }


        [Authorize]
        [HttpPost("PurgeOrphanedAttachment/{pageFileId:int}/{revision:int}")]
        public ActionResult PurgeOrphanedAttachment(ConfirmActionViewModel model, int pageFileId, int revision)
        {
            SessionState.RequireAdminPermission();

            if (model.UserSelection == true)
            {
                PageFileRepository.PurgeOrphanedPageAttachment(pageFileId, revision);
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        #endregion

        #region Menu Items.

        [Authorize]
        [HttpGet("MenuItems")]
        public ActionResult MenuItems()
        {
            SessionState.RequireAdminPermission();

            //var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new MenuItemsViewModel()
            {
                Items = ConfigurationRepository.GetAllMenuItems(orderBy, orderByDirection)
            };

            return View(model);
        }

        [Authorize]
        [HttpGet("MenuItem/{id:int?}")]
        public ActionResult MenuItem(int? id)
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Menu Item";

            if (id != null)
            {
                var menuItem = ConfigurationRepository.GetMenuItemById((int)id);
                return View(menuItem.ToViewModel());
            }
            else
            {
                var model = new MenuItemViewModel
                {
                    Link = "/"
                };
                return View(model);
            }

        }

        /// <summary>
        /// Save site menu item.
        /// </summary>
        /// <param name="profile"></param>
        /// <returns></returns>
        [Authorize]
        [HttpPost("MenuItem/{id:int?}")]
        public ActionResult MenuItem(int? id, MenuItemViewModel model)
        {
            SessionState.RequireAdminPermission();

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

            if (ConfigurationRepository.GetAllMenuItems().Where(o => o.Name.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase) && o.Id != model.Id).Any())
            {
                ModelState.AddModelError("Name", $"菜单名 '{model.Name}' 已被使用");
                return View(model);
            }

            if (id.DefaultWhenNull(0) == 0)
            {
                model.Id = ConfigurationRepository.InsertMenuItem(model.ToDataModel());
                ModelState.Clear();

                return NotifyOfSuccess("创建成功", $"/Admin/MenuItem/{model.Id}");
            }
            else
            {
                ConfigurationRepository.UpdateMenuItemById(model.ToDataModel());
            }

            model.SuccessMessage = "操作成功！";
            return View(model);
        }

        [Authorize]
        [HttpGet("DeleteMenuItem/{id}")]
        public ActionResult DeleteMenuItem(int id)
        {
            SessionState.RequireAdminPermission();

            var model = ConfigurationRepository.GetMenuItemById(id);
            SessionState.Page.Name = $"{model.Name} Delete";

            return View(model.ToViewModel());
        }

        [Authorize]
        [HttpPost("DeleteMenuItem/{id}")]
        public ActionResult DeleteMenuItem(MenuItemViewModel model)
        {
            SessionState.RequireAdminPermission();

            bool confirmAction = bool.Parse(GetFormValue("IsActionConfirmed").EnsureNotNull());
            if (confirmAction == true)
            {
                ConfigurationRepository.DeleteMenuItemById(model.Id);

                return NotifyOfSuccess("删除成功", $"/Admin/MenuItems");
            }

            return Redirect($"{GlobalConfiguration.BasePath}/Admin/MenuItem/{model.Id}");
        }

        #endregion

        #region Roles.

        [Authorize]
        [HttpGet("Role/{navigation}")]
        public ActionResult Role(string navigation)
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Roles";

            navigation = Navigation.Clean(navigation);

            var role = UsersRepository.GetRoleByName(navigation);

            var model = new RoleViewModel()
            {
                Id = role.Id,
                Name = role.Name,
                Users = UsersRepository.GetProfilesByRoleIdPaged(role.Id, GetQueryValue("page", 1))
            };

            model.PaginationPageCount = (model.Users.FirstOrDefault()?.PaginationPageCount ?? 0);

            return View(model);
        }

        [Authorize]
        [HttpGet("Roles")]
        public ActionResult Roles()
        {
            SessionState.RequireAdminPermission();

            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new RolesViewModel()
            {
                Roles = UsersRepository.GetAllRoles(orderBy, orderByDirection)
            };

            return View(model);
        }

        #endregion

        #region Accounts

        [Authorize]
        [HttpGet("Account/{navigation}")]
        public ActionResult Account(string navigation)
        {
            SessionState.RequireAdminPermission();

            var model = new ZelWiki.Models.ViewModels.Admin.AccountProfileViewModel()
            {
                AccountProfile = ZelWiki.Models.ViewModels.Admin.AccountProfileAccountViewModel.FromDataModel(
                    UsersRepository.GetAccountProfileByNavigation(Navigation.Clean(navigation))),
                Credential = new CredentialViewModel(),
                Themes = ConfigurationRepository.GetAllThemes(),
                TimeZones = TimeZoneItem.GetAll(),
                Countries = CountryItem.GetAll(),
                Languages = LanguageItem.GetAll(),
                Roles = UsersRepository.GetAllRoles()
            };

            model.AccountProfile.CreatedDate = SessionState.LocalizeDateTime(model.AccountProfile.CreatedDate);
            model.AccountProfile.ModifiedDate = SessionState.LocalizeDateTime(model.AccountProfile.ModifiedDate);

            return View(model);
        }

        /// <summary>
        /// Save user profile.
        /// </summary>
        /// <param name="profile"></param>
        /// <returns></returns>
        [Authorize]
        [HttpPost("Account/{navigation}")]
        public ActionResult Account(string navigation, Models.ViewModels.Admin.AccountProfileViewModel model)
        {
            SessionState.RequireAdminPermission();

            model.Themes = ConfigurationRepository.GetAllThemes();
            model.TimeZones = TimeZoneItem.GetAll();
            model.Countries = CountryItem.GetAll();
            model.Languages = LanguageItem.GetAll();
            model.Roles = UsersRepository.GetAllRoles();
            model.AccountProfile.Navigation = NamespaceNavigation.CleanAndValidate(model.AccountProfile.AccountName.ToLower());

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

            var user = UserManager.FindByIdAsync(model.AccountProfile.UserId.ToString()).Result.EnsureNotNull();

            if (model.Credential.Password != CredentialViewModel.NOTSET && model.Credential.Password == model.Credential.ComparePassword)
            {
                try
                {
                    var token = UserManager.GeneratePasswordResetTokenAsync(user).Result.EnsureNotNull();
                    var result = UserManager.ResetPasswordAsync(user, token, model.Credential.Password).Result.EnsureNotNull();
                    if (!result.Succeeded)
                    {
                        throw new Exception(string.Join("<br />\r\n", result.Errors.Select(o => o.Description)));
                    }

                    if (model.AccountProfile.AccountName.Equals(Constants.DEFAULTACCOUNT, StringComparison.CurrentCultureIgnoreCase))
                    {
                        UsersRepository.SetAdminPasswordIsChanged();
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError("Credential.Password", ex.Message);
                    return View(model);
                }
            }

            var profile = UsersRepository.GetAccountProfileByUserId(model.AccountProfile.UserId);
            if (!profile.Navigation.Equals(model.AccountProfile.Navigation, StringComparison.CurrentCultureIgnoreCase))
            {
                if (UsersRepository.DoesProfileAccountExist(model.AccountProfile.AccountName))
                {
                    ModelState.AddModelError("AccountProfile.AccountName", "用户名已经被使用");
                    return View(model);
                }
            }

            if (!profile.EmailAddress.Equals(model.AccountProfile.EmailAddress, StringComparison.CurrentCultureIgnoreCase))
            {
                if (UsersRepository.DoesEmailAddressExist(model.AccountProfile.EmailAddress))
                {
                    ModelState.AddModelError("AccountProfile.EmailAddress", "该邮箱已经存在");
                    return View(model);
                }
            }

            var file = Request.Form.Files["Avatar"];
            if (file != null && file.Length > 0)
            {
                if (GlobalConfiguration.AllowableImageTypes.Contains(file.ContentType.ToLower()) == false)
                {
                    model.ErrorMessage += "无法保存附件图片, 不允许的格式.\r\n";
                }
                else if (file.Length > GlobalConfiguration.MaxAvatarFileSize)
                {
                    model.ErrorMessage += "无法保存附件图片, 图片过大.\r\n";
                }
                else
                {
                    try
                    {
                        var imageBytes = Utility.ConvertHttpFileToBytes(file);
                        var image = SixLabors.ImageSharp.Image.Load(new MemoryStream(imageBytes));
                        UsersRepository.UpdateProfileAvatar(profile.UserId, imageBytes, file.ContentType.ToLower());
                    }
                    catch
                    {
                        model.ErrorMessage += "无法保存附件图片.\r\n";
                    }
                }
            }

            profile.AccountName = model.AccountProfile.AccountName;
            profile.Navigation = NamespaceNavigation.CleanAndValidate(model.AccountProfile.AccountName);
            profile.Biography = model.AccountProfile.Biography;
            profile.ModifiedDate = DateTime.UtcNow;
            UsersRepository.UpdateProfile(profile);

            var claims = new List<Claim>
                    {
                        new (ClaimTypes.Role, model.AccountProfile.Role),
                        new ("timezone", model.AccountProfile.TimeZone),
                        new (ClaimTypes.Country, model.AccountProfile.Country),
                        new ("language", model.AccountProfile.Language),
                        new ("firstname", model.AccountProfile.FirstName ?? ""),
                        new ("lastname", model.AccountProfile.LastName ?? ""),
                        new ("theme", model.AccountProfile.Theme ?? ""),
                    };
            SecurityRepository.UpsertUserClaims(UserManager, user, claims);

            //If we are changing the currently logged in user, then make sure we take some extra actions so we can see the changes immediately.
            if (SessionState.Profile?.UserId == model.AccountProfile.UserId)
            {
                SignInManager.RefreshSignInAsync(user);

                WikiCache.ClearCategory(WikiCacheKey.Build(WikiCache.Category.User, [profile.Navigation]));
                WikiCache.ClearCategory(WikiCacheKey.Build(WikiCache.Category.User, [profile.UserId]));

                //This is not 100% necessary, I just want to prevent the user from needing to refresh to view the new theme.
                SessionState.UserTheme = ConfigurationRepository.GetAllThemes().SingleOrDefault(o => o.Name == model.AccountProfile.Theme) ?? GlobalConfiguration.SystemTheme;
            }

            //Allow the administrator to confirm/unconfirm the email address.
            bool emailConfirmChanged = profile.EmailConfirmed != model.AccountProfile.EmailConfirmed;
            if (emailConfirmChanged)
            {
                user.EmailConfirmed = model.AccountProfile.EmailConfirmed;
                var updateResult = UserManager.UpdateAsync(user).Result;
                if (!updateResult.Succeeded)
                {
                    throw new Exception(string.Join("<br />\r\n", updateResult.Errors.Select(o => o.Description)));
                }
            }

            if (!profile.EmailAddress.Equals(model.AccountProfile.EmailAddress, StringComparison.CurrentCultureIgnoreCase))
            {
                bool wasEmailAlreadyConfirmed = user.EmailConfirmed;

                var setEmailResult = UserManager.SetEmailAsync(user, model.AccountProfile.EmailAddress).Result;
                if (!setEmailResult.Succeeded)
                {
                    throw new Exception(string.Join("<br />\r\n", setEmailResult.Errors.Select(o => o.Description)));
                }

                var setUserNameResult = UserManager.SetUserNameAsync(user, model.AccountProfile.EmailAddress).Result;
                if (!setUserNameResult.Succeeded)
                {
                    throw new Exception(string.Join("<br />\r\n", setUserNameResult.Errors.Select(o => o.Description)));
                }

                //If the email address was already confirmed, just keep the status. Afterall, this is an admin making the change.
                if (wasEmailAlreadyConfirmed && emailConfirmChanged == false)
                {
                    user.EmailConfirmed = true;
                    var updateResult = UserManager.UpdateAsync(user).Result;
                    if (!updateResult.Succeeded)
                    {
                        throw new Exception(string.Join("<br />\r\n", updateResult.Errors.Select(o => o.Description)));
                    }
                }
            }

            model.SuccessMessage = "操作成功！";

            return View(model);
        }

        [Authorize]
        [HttpGet("AddAccount")]
        public ActionResult AddAccount()
        {
            SessionState.RequireAdminPermission();

            var membershipConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("Membership");
            var defaultSignupRole = membershipConfig.Value<string>("Default Signup Role").EnsureNotNull();
            var customizationConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("Customization");

            var model = new ZelWiki.Models.ViewModels.Admin.AccountProfileViewModel()
            {
                AccountProfile = new ZelWiki.Models.ViewModels.Admin.AccountProfileAccountViewModel
                {
                    AccountName = string.Empty,
                    Country = customizationConfig.Value<string>("Default Country", string.Empty),
                    TimeZone = customizationConfig.Value<string>("Default TimeZone", string.Empty),
                    Language = customizationConfig.Value<string>("Default Language", string.Empty),
                    Role = defaultSignupRole
                },
                Themes = ConfigurationRepository.GetAllThemes(),
                Credential = new CredentialViewModel(),
                TimeZones = TimeZoneItem.GetAll(),
                Countries = CountryItem.GetAll(),
                Languages = LanguageItem.GetAll(),
                Roles = UsersRepository.GetAllRoles()
            };

            return View(model);
        }

        /// <summary>
        /// Create a new user profile.
        /// </summary>
        /// <param name="profile"></param>
        /// <returns></returns>
        [Authorize]
        [HttpPost("AddAccount")]
        public ActionResult AddAccount(ZelWiki.Models.ViewModels.Admin.AccountProfileViewModel model)
        {
            SessionState.RequireAdminPermission();

            model.Themes = ConfigurationRepository.GetAllThemes();
            model.TimeZones = TimeZoneItem.GetAll();
            model.Countries = CountryItem.GetAll();
            model.Languages = LanguageItem.GetAll();
            model.Roles = UsersRepository.GetAllRoles();
            model.AccountProfile.Navigation = NamespaceNavigation.CleanAndValidate(model.AccountProfile.AccountName?.ToLower());

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

            if (string.IsNullOrWhiteSpace(model.AccountProfile.AccountName))
            {
                ModelState.AddModelError("AccountProfile.AccountName", "用户名为必填项");
                return View(model);
            }

            if (UsersRepository.DoesProfileAccountExist(model.AccountProfile.AccountName))
            {
                ModelState.AddModelError("AccountProfile.AccountName", "用户名已经在使用");
                return View(model);
            }

            if (UsersRepository.DoesEmailAddressExist(model.AccountProfile.EmailAddress))
            {
                ModelState.AddModelError("AccountProfile.EmailAddress", "邮箱已经被使用");
                return View(model);
            }

            Guid? userId;

            try
            {
                //Define the new user:
                var identityUser = new IdentityUser(model.AccountProfile.EmailAddress)
                {
                    Email = model.AccountProfile.EmailAddress,
                    EmailConfirmed = true
                };

                //Create the new user:
                var creationResult = UserManager.CreateAsync(identityUser, model.Credential.Password).Result;
                if (!creationResult.Succeeded)
                {
                    model.ErrorMessage = string.Join("<br />\r\n", creationResult.Errors.Select(o => o.Description));
                    return View(model);
                }
                identityUser = UserManager.FindByEmailAsync(model.AccountProfile.EmailAddress).Result.EnsureNotNull();

                userId = Guid.Parse(identityUser.Id);

                //Insert the claims.
                var claims = new List<Claim>
                    {
                        new (ClaimTypes.Role, model.AccountProfile.Role),
                        new ("timezone", model.AccountProfile.TimeZone),
                        new (ClaimTypes.Country, model.AccountProfile.Country),
                        new ("language", model.AccountProfile.Language),
                        new ("firstname", model.AccountProfile.FirstName ?? ""),
                        new ("lastname", model.AccountProfile.LastName ?? ""),
                        new ("theme", model.AccountProfile.Theme ?? ""),
                    };
                SecurityRepository.UpsertUserClaims(UserManager, identityUser, claims);
            }
            catch (Exception ex)
            {
                return NotifyOfError(ex.Message);
            }

            UsersRepository.CreateProfile((Guid)userId, model.AccountProfile.AccountName);
            var profile = UsersRepository.GetAccountProfileByUserId((Guid)userId);

            profile.AccountName = model.AccountProfile.AccountName;
            profile.Navigation = NamespaceNavigation.CleanAndValidate(model.AccountProfile.AccountName);
            profile.Biography = model.AccountProfile.Biography;
            profile.ModifiedDate = DateTime.UtcNow;
            UsersRepository.UpdateProfile(profile);

            var file = Request.Form.Files["Avatar"];
            if (file != null && file.Length > 0)
            {
                if (GlobalConfiguration.AllowableImageTypes.Contains(file.ContentType.ToLower()) == false)
                {
                    model.ErrorMessage += "无法保存附件图片, 不支持的格式.\r\n";
                }
                else if (file.Length > GlobalConfiguration.MaxAvatarFileSize)
                {
                    model.ErrorMessage += "无法保存附件图片, 图片过大.\r\n";
                }
                else
                {
                    try
                    {
                        var imageBytes = Utility.ConvertHttpFileToBytes(file);
                        var image = SixLabors.ImageSharp.Image.Load(new MemoryStream(imageBytes));
                        UsersRepository.UpdateProfileAvatar(profile.UserId, imageBytes, file.ContentType.ToLower());
                    }
                    catch
                    {
                        model.ErrorMessage += "无法保存附件图片.";
                    }
                }
            }

            return NotifyOf("创建成功", model.ErrorMessage, $"/Admin/Account/{profile.Navigation}");
        }

        [Authorize]
        [HttpGet("Accounts")]
        public ActionResult Accounts()
        {
            SessionState.RequireAdminPermission();

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");
            var searchString = GetQueryValue("SearchString") ?? string.Empty;

            var model = new AccountsViewModel()
            {
                Users = UsersRepository.GetAllUsersPaged(pageNumber, orderBy, orderByDirection, searchString),
                SearchString = searchString
            };

            model.PaginationPageCount = (model.Users.FirstOrDefault()?.PaginationPageCount ?? 0);

            if (model.Users != null && model.Users.Count > 0)
            {
                model.Users.ForEach(o =>
                {
                    o.CreatedDate = SessionState.LocalizeDateTime(o.CreatedDate);
                    o.ModifiedDate = SessionState.LocalizeDateTime(o.ModifiedDate);
                });
            }

            return View(model);
        }

        [Authorize]
        [HttpPost("DeleteAccount/{navigation}")]
        public ActionResult DeleteAccount(string navigation, DeleteAccountViewModel model)
        {
            SessionState.RequireAdminPermission();

            var profile = UsersRepository.GetAccountProfileByNavigation(navigation);

            bool confirmAction = bool.Parse(GetFormValue("IsActionConfirmed").EnsureNotNull());
            if (confirmAction == true && profile != null)
            {
                var user = UserManager.FindByIdAsync(profile.UserId.ToString()).Result;
                if (user == null)
                {
                    return NotFound("404 404 404");
                }

                var result = UserManager.DeleteAsync(user).Result;
                if (!result.Succeeded)
                {
                    throw new Exception(string.Join("<br />\r\n", result.Errors.Select(o => o.Description)));
                }

                UsersRepository.AnonymizeProfile(profile.UserId);
                WikiCache.ClearCategory(WikiCacheKey.Build(WikiCache.Category.User, [profile.Navigation]));

                if (profile.UserId == SessionState.Profile?.UserId)
                {
                    //We're deleting our own account. Oh boy...
                    SignInManager.SignOutAsync();

                    return NotifyOfSuccess("操作成功", $"/Profile/Deleted");
                }

                return NotifyOfSuccess("操作成功", $"/Admin/Accounts");
            }

            return Redirect($"{GlobalConfiguration.BasePath}/Admin/Account/{navigation}");
        }

        [Authorize]
        [HttpGet("DeleteAccount/{navigation}")]
        public ActionResult DeleteAccount(string navigation)
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Delete Profile";

            var profile = UsersRepository.GetAccountProfileByNavigation(navigation);

            var model = new DeleteAccountViewModel()
            {
                AccountName = profile.AccountName
            };

            if (profile != null)
            {
                SessionState.Page.Name = $"Delete {profile.AccountName}";
            }

            return View(model);
        }

        #endregion

        #region Config.

        [Authorize]
        [HttpGet("Config")]
        public ActionResult Config()
        {
            SessionState.RequireAdminPermission();

            var model = new ConfigurationViewModel()
            {
                Themes = ConfigurationRepository.GetAllThemes(),
                Roles = UsersRepository.GetAllRoles(),
                TimeZones = TimeZoneItem.GetAll(),
                Countries = CountryItem.GetAll(),
                Languages = LanguageItem.GetAll(),
                Nest = ConfigurationRepository.GetConfigurationNest()
            };
            return View(model);
        }

        [Authorize]
        [HttpPost("Config")]
        public ActionResult Config(ConfigurationViewModel model)
        {
            SessionState.RequireAdminPermission();

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

            try
            {
                model = new ConfigurationViewModel()
                {
                    Themes = ConfigurationRepository.GetAllThemes(),
                    Roles = UsersRepository.GetAllRoles(),
                    TimeZones = TimeZoneItem.GetAll(),
                    Countries = CountryItem.GetAll(),
                    Languages = LanguageItem.GetAll(),
                    Nest = ConfigurationRepository.GetConfigurationNest(),
                };

                var flatConfig = ConfigurationRepository.GetFlatConfiguration();

                foreach (var fc in flatConfig)
                {
                    var parent = model.Nest.Single(o => o.Name == fc.GroupName);
                    var child = parent.Entries.Single(o => o.Name == fc.EntryName);

                    var value = GetFormValue($"{fc.GroupId}:{fc.EntryId}", string.Empty);

                    //We keep the value in model.Nest.Entries.Value so that the page will reflect the new settings after post.
                    child.Value = value;

                    if (fc.IsRequired && string.IsNullOrEmpty(value))
                    {
                        model.ErrorMessage = $"{fc.GroupName} : {fc.EntryName} is required.";
                        return View(model);
                    }

                    if ($"{fc.GroupName}:{fc.EntryName}" == "Customization:Theme")
                    {
                        //This is not 100% necessary, I just want to prevent the user from needing to refresh to view the new theme.
                        GlobalConfiguration.SystemTheme = ConfigurationRepository.GetAllThemes().Single(o => o.Name == value);
                        if (string.IsNullOrEmpty(SessionState.Profile?.Theme))
                        {
                            SessionState.UserTheme = GlobalConfiguration.SystemTheme;
                        }
                    }

                    if (fc.IsEncrypted)
                    {
                        value = Security.Helpers.EncryptString(Security.Helpers.MachineKey, value);
                    }

                    ConfigurationRepository.SaveConfigurationEntryValueByGroupAndEntry(fc.GroupName, fc.EntryName, value);
                }

                WikiCache.ClearCategory(WikiCache.Category.Configuration);

                model.SuccessMessage = "操作成功！";
            }
            catch (Exception ex)
            {
                return NotifyOfError(ex.Message);
            }

            return View(model);
        }

        #endregion

        #region Emojis.

        [Authorize]
        [HttpGet("Emojis")]
        public ActionResult Emojis()
        {
            SessionState.RequireModeratePermission();
            SessionState.Page.Name = $"Emojis";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");
            var searchString = GetQueryValue("SearchString") ?? string.Empty;

            var model = new EmojisViewModel()
            {
                Emojis = EmojiRepository.GetAllEmojisPaged(pageNumber, orderBy, orderByDirection, Utility.SplitToTokens(searchString)),
                SearchString = searchString
            };

            model.PaginationPageCount = (model.Emojis.FirstOrDefault()?.PaginationPageCount ?? 0);

            return View(model);
        }

        [Authorize]
        [HttpGet("Emoji/{name}")]
        public ActionResult Emoji(string name)
        {
            SessionState.RequireModeratePermission();

            var emoji = EmojiRepository.GetEmojiByName(name);

            var model = new EmojiViewModel
            {
                Emoji = emoji ?? new Emoji(),
                Categories = string.Join(",", EmojiRepository.GetEmojiCategoriesByName(name).Select(o => o.Category).ToList()),
                OriginalName = emoji?.Name ?? string.Empty
            };

            return View(model);
        }

        /// <summary>
        /// Update an existing emoji.
        /// </summary>
        /// <param name="profile"></param>
        /// <returns></returns>
        [Authorize]
        [HttpPost("Emoji/{name}")]
        public ActionResult Emoji(EmojiViewModel model)
        {
            SessionState.RequireAdminPermission();

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

            bool nameChanged = false;

            if (!model.OriginalName.Equals(model.Emoji.Name, StringComparison.InvariantCultureIgnoreCase))
            {
                nameChanged = true;
                var checkName = EmojiRepository.GetEmojiByName(model.Emoji.Name.ToLowerInvariant());
                if (checkName != null)
                {
                    ModelState.AddModelError("Emoji.Name", "Emoji名称已经被使用");
                    return View(model);
                }
            }

            var emoji = new UpsertEmoji
            {
                Id = model.Emoji.Id,
                Name = model.Emoji.Name.ToLowerInvariant(),
                Categories = Utility.SplitToTokens($"{model.Categories} {model.Emoji.Name} {Text.SeperateCamelCase(model.Emoji.Name)}")
            };

            var file = Request.Form.Files["ImageData"];
            if (file != null && file.Length > 0)
            {
                if (file.Length > GlobalConfiguration.MaxEmojiFileSize)
                {
                    model.ErrorMessage += "无法保存附件图片, 图片过大.";
                }
                else
                {
                    try
                    {
                        emoji.ImageData = Utility.ConvertHttpFileToBytes(file);
                        _ = SixLabors.ImageSharp.Image.Load(new MemoryStream(emoji.ImageData));
                        emoji.MimeType = file.ContentType;
                    }
                    catch
                    {
                        model.ErrorMessage += "无法保存附件图片.";
                    }
                }
            }

            emoji.Id = EmojiRepository.UpsertEmoji(emoji);
            model.OriginalName = model.Emoji.Name;
            model.SuccessMessage = "操作成功！";
            model.Emoji.Id = (int)emoji.Id;
            ModelState.Clear();

            ConfigurationRepository.ReloadEmojis();

            if (nameChanged)
            {
                return NotifyOfSuccess("操作成功", $"/Admin/Emoji/{Navigation.Clean(emoji.Name)}");
            }

            return View(model);
        }

        [Authorize]
        [HttpGet("AddEmoji")]
        public ActionResult AddEmoji()
        {
            SessionState.RequireAdminPermission();

            var model = new AddEmojiViewModel()
            {
                Name = string.Empty,
                OriginalName = string.Empty,
                Categories = string.Empty
            };

            return View(model);
        }

        /// <summary>
        /// Save user profile.
        /// </summary>
        /// <param name="profile"></param>
        /// <returns></returns>
        [Authorize]
        [HttpPost("AddEmoji")]
        public ActionResult AddEmoji(AddEmojiViewModel model)
        {
            SessionState.RequireAdminPermission();

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

            if (string.IsNullOrEmpty(model.OriginalName) == true || !model.OriginalName.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase))
            {
                var checkName = EmojiRepository.GetEmojiByName(model.Name.ToLower());
                if (checkName != null)
                {
                    ModelState.AddModelError("Name", "Emoji名已经存在");
                    return View(model);
                }
            }

            var emoji = new UpsertEmoji
            {
                Id = model.Id,
                Name = model.Name.ToLowerInvariant(),
                Categories = Utility.SplitToTokens($"{model.Categories} {model.Name} {Text.SeperateCamelCase(model.Name)}")
            };

            var file = Request.Form.Files["ImageData"];
            if (file != null && file.Length > 0)
            {
                if (file.Length > GlobalConfiguration.MaxEmojiFileSize)
                {
                    ModelState.AddModelError("Name", "无法保存附件图片, 图片过大.");
                }
                else
                {
                    try
                    {
                        emoji.ImageData = Utility.ConvertHttpFileToBytes(file);
                        var image = SixLabors.ImageSharp.Image.Load(new MemoryStream(emoji.ImageData));
                        emoji.MimeType = file.ContentType;
                    }
                    catch
                    {
                        ModelState.AddModelError("Name", "无法保存附件图片.");
                    }
                }
            }

            EmojiRepository.UpsertEmoji(emoji);

            return NotifyOfSuccess("操作成功", $"/Admin/Emoji/{Navigation.Clean(emoji.Name)}");
        }

        [Authorize]
        [HttpPost("DeleteEmoji/{name}")]
        public ActionResult DeleteEmoji(string name, EmojiViewModel model)
        {
            SessionState.RequireAdminPermission();

            var emoji = EmojiRepository.GetEmojiByName(name);

            bool confirmAction = bool.Parse(GetFormValue("IsActionConfirmed").EnsureNotNull());
            if (confirmAction == true && emoji != null)
            {
                EmojiRepository.DeleteById(emoji.Id);

                return NotifyOfSuccess("操作成功", $"/Admin/Emojis");
            }

            return Redirect($"{GlobalConfiguration.BasePath}/Admin/Emoji/{name}");
        }

        [Authorize]
        [HttpGet("DeleteEmoji/{name}")]
        public ActionResult DeleteEmoji(string name)
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Delete Emoji";

            var emoji = EmojiRepository.GetEmojiByName(name);

            var model = new EmojiViewModel()
            {
                OriginalName = emoji?.Name ?? string.Empty
            };

            if (emoji != null)
            {
                SessionState.Page.Name = $"Delete {emoji.Name}";
            }

            return View(model);
        }

        #endregion

        #region Exceptions.

        [Authorize]
        [HttpGet("Exceptions")]
        public ActionResult Exceptions()
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Exceptions";

            var pageNumber = GetQueryValue("page", 1);
            var orderBy = GetQueryValue("OrderBy");
            var orderByDirection = GetQueryValue("OrderByDirection");

            var model = new ExceptionsViewModel()
            {
                Exceptions = ExceptionRepository.GetAllExceptionsPaged(pageNumber, orderBy, orderByDirection)
            };

            model.PaginationPageCount = (model.Exceptions.FirstOrDefault()?.PaginationPageCount ?? 0);

            return View(model);
        }

        [Authorize]
        [HttpGet("Exception/{id}")]
        public ActionResult Exception(int id)
        {
            SessionState.RequireAdminPermission();
            SessionState.Page.Name = $"Exception";

            var model = new ExceptionViewModel()
            {
                Exception = ExceptionRepository.GetExceptionById(id)
            };

            return View(model);
        }

        [Authorize]
        [HttpPost("PurgeExceptions")]
        public ActionResult PurgeExceptions(ConfirmActionViewModel model)
        {
            SessionState.RequireAdminPermission();

            if (model.UserSelection == true)
            {
                ExceptionRepository.PurgeExceptions();
                return NotifyOfSuccess("操作成功", model.YesRedirectURL);
            }

            return Redirect($"{GlobalConfiguration.BasePath}{model.NoRedirectURL}");
        }

        #endregion
    }
}
