using System;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data.Entity;
using atakanozbancom.Models.classes;

namespace atakanozbancom.Controllers
{
    public class adminController : Controller
    {
        private readonly Context db = new Context();

        [Authorize]
        public ActionResult Index() // admin dashboard landing page
        {
            ViewBag.MyProjectsCount = db.MyProjects.Count();
            ViewBag.AffiliateLinksCount = db.AffiliateLinks.Count();
            ViewBag.SocialIconsCount = db.SocialIcons.Count();
            return View();
        }

        [Authorize]
        public ActionResult MyProjects() // shows what we have
        {
            var value = db.MyProjects.ToList();
            return View(value);
        }

        [Authorize]
        [HttpGet]
        public ActionResult myprojectsget(int id)
        {
            var p = db.MyProjects
                      .Include(x => x.Translations)
                      .FirstOrDefault(x => x.id == id);

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

            var tr = p.Translations?
                       .FirstOrDefault(t => t.culture == "tr" || t.culture == "tr-TR");

            var vm = new AdminMyProjectsEditVM
            {
                id = p.id,

                TitleEN = p.title,
                DescEN = p.description,

                TitleTR = tr != null ? tr.title : "",
                DescTR = tr != null ? tr.description : ""
            };

            // NEW:
            vm.medias = db.projectmedia
                .Where(m => m.project_id == p.id)
                .OrderBy(m => m.sort_order)
                .ThenBy(m => m.id)
                .ToList();

            return View("myprojectsget", vm);
        }


        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult myprojectsget(AdminMyProjectsEditVM vm)
        {
            if (!ModelState.IsValid)
            {
                vm.medias = db.projectmedia
                    .Where(m => m.project_id == vm.id)
                    .OrderBy(m => m.sort_order)
                    .ThenBy(m => m.id)
                    .ToList();

                return View("myprojectsget", vm);
            }

            var p = db.MyProjects.FirstOrDefault(x => x.id == vm.id);
            if (p == null) return HttpNotFound();

            // EN -> base
            p.title = vm.TitleEN;
            p.description = vm.DescEN;

            // TR -> translations
            var tr = db.myprojectstranslations
                       .FirstOrDefault(x => x.project_id == p.id && (x.culture == "tr" || x.culture == "tr-TR"));

            if (tr == null)
            {
                tr = new myprojectstranslation { project_id = p.id, culture = "tr" };
                db.myprojectstranslations.Add(tr);
            }

            tr.title = vm.TitleTR;
            tr.description = vm.DescTR;

            db.SaveChanges();
            TempData["ok"] = "Project updated successfully!";
            return RedirectToAction("myprojectsget", new { id = p.id });
        }


        [Authorize]
        [HttpGet]
        public ActionResult newmppost() // newmppost = New My Projects Post
        {
            return View(new AdminMyProjectsEditVM());
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult newmppost(AdminMyProjectsEditVM vm)
        {
            if (!ModelState.IsValid)
                return View(vm);

            // EN -> base table
            var newProject = new myprojects
            {
                title = vm.TitleEN,
                description = vm.DescEN
                // image/iframe artık yok
            };

            db.MyProjects.Add(newProject);
            db.SaveChanges();

            // TR -> translations
            var tr = new myprojectstranslation
            {
                project_id = newProject.id,
                culture = "tr",
                title = vm.TitleTR ?? "",
                description = vm.DescTR ?? ""
            };

            db.myprojectstranslations.Add(tr);
            db.SaveChanges();

            TempData["ok"] = "Project added successfully!";
            return RedirectToAction("myprojectsget", new { id = newProject.id }); // direkt edit sayfasına at, media eklesin
        }


        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult myprojectsdelete(int id)
        {
            var mpd = db.MyProjects.Find(id); // mpd = my projects delete
            if (mpd != null)
            {
                db.MyProjects.Remove(mpd);
                db.SaveChanges();
            }
            return RedirectToAction("MyProjects");
        }

        [HttpPost]
        [Authorize]
        [ValidateAntiForgeryToken]
        public ActionResult addmedia(
    int projectId,
    int mediaType,
    int sortOrder = 0,
    string iframeUrl = null,
    HttpPostedFileBase mediaFile = null)
        {
            var type = (MediaType)mediaType;

            // 1) IFRAME -> URL
            if (type == MediaType.Iframe)
            {
                if (string.IsNullOrWhiteSpace(iframeUrl))
                {
                    TempData["ok"] = "Iframe URL boş olamaz.";
                    return RedirectToAction("myprojectsget", new { id = projectId });
                }

                var trimmed = iframeUrl.Trim();
                if (!Uri.TryCreate(trimmed, UriKind.Absolute, out var embedUri)
                    || (embedUri.Scheme != Uri.UriSchemeHttps && embedUri.Scheme != Uri.UriSchemeHttp))
                {
                    TempData["ok"] = "Iframe URL must be a valid http(s) address.";
                    return RedirectToAction("myprojectsget", new { id = projectId });
                }

                var row = new projectmedia
                {
                    project_id = projectId,
                    media_type = MediaType.Iframe,
                    url = trimmed,
                    sort_order = sortOrder
                };

                db.projectmedia.Add(row);
                db.SaveChanges();

                TempData["ok"] = "Media eklendi!";
                return RedirectToAction("myprojectsget", new { id = projectId });
            }

            // 2) IMAGE/VIDEO/DOCUMENT -> FILE
            if (mediaFile == null || mediaFile.ContentLength <= 0)
            {
                TempData["ok"] = "Dosya seçmelisin.";
                return RedirectToAction("myprojectsget", new { id = projectId });
            }

            var ext = Path.GetExtension(mediaFile.FileName)?.ToLowerInvariant() ?? "";

            // Tip bazlı uzantı whitelist
            string[] allowed;
            switch (type)
            {
                case MediaType.Image:
                    allowed = new[] { ".jpg", ".jpeg", ".png", ".webp", ".gif" };
                    break;
                //case MediaType.Video:
                //    allowed = new[] { ".mp4", ".webm", ".mov" };
                //    break;
                //case MediaType.Document:
                //    allowed = new[] { ".pdf" };
                //    break;
                default:
                    TempData["ok"] = "Geçersiz media type.";
                    return RedirectToAction("myprojectsget", new { id = projectId });
            }

            if (!allowed.Contains(ext))
            {
                TempData["ok"] = "Bu dosya tipi kabul edilmiyor: " + ext;
                return RedirectToAction("myprojectsget", new { id = projectId });
            }

            // Upload path
            var uploadsRoot = Server.MapPath("~/Content/uploads/projects-media");
            if (!Directory.Exists(uploadsRoot))
                Directory.CreateDirectory(uploadsRoot);

            var fileName = Guid.NewGuid().ToString("N") + ext;
            var filePath = Path.Combine(uploadsRoot, fileName);
            mediaFile.SaveAs(filePath);

            var publicUrl = "/Content/uploads/projects-media/" + fileName;

            var m = new projectmedia
            {
                project_id = projectId,
                media_type = type,
                url = publicUrl,
                sort_order = sortOrder
            };

            db.projectmedia.Add(m);
            db.SaveChanges();

            TempData["ok"] = "Media eklendi!";
            return RedirectToAction("myprojectsget", new { id = projectId });
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult deletemedia(int id, int projectId)
        {
            var m = db.projectmedia.FirstOrDefault(x => x.id == id && x.project_id == projectId);
            if (m == null)
                return RedirectToAction("myprojectsget", new { id = projectId });

            // Dosya silme: sadece Iframe dışındakiler + URL bizim uploads klasörümüzse
            if (m.media_type != MediaType.Iframe && !string.IsNullOrWhiteSpace(m.url))
            {
                try
                {
                    // Güvenlik: sadece bizim klasördeki dosyaları sil
                    var prefix = "/Content/uploads/projects-media/";
                    if (m.url.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                    {
                        var relative = "~" + m.url; // ~/Content/uploads/...
                        var physicalPath = Server.MapPath(relative);

                        if (System.IO.File.Exists(physicalPath))
                            System.IO.File.Delete(physicalPath);
                    }
                }
                catch
                {
                    // istersen log yazdırırız, şimdilik sessiz geçiyoruz
                }
            }

            db.projectmedia.Remove(m);
            db.SaveChanges();

            TempData["ok"] = "Media silindi.";
            return RedirectToAction("myprojectsget", new { id = projectId });
        }

        // ================= AFFILIATE LINKS =================

        // .svg intentionally excluded: SVGs can embed <script> and execute when opened directly.
        private static readonly string[] AllowedLogoExtensions = { ".jpg", ".jpeg", ".png", ".webp", ".gif" };
        private const string AffiliateLogosPrefix = "/Content/uploads/affiliate-logos/";

        [Authorize]
        public ActionResult AffiliateLinks()
        {
            var value = db.AffiliateLinks
                          .OrderBy(x => x.sort_order)
                          .ThenBy(x => x.id)
                          .ToList();
            return View(value);
        }

        [Authorize]
        [HttpGet]
        public ActionResult newaffiliatepost()
        {
            return View(new AdminAffiliateLinkVM());
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult newaffiliatepost(AdminAffiliateLinkVM vm)
        {
            if (vm.logoFile == null || vm.logoFile.ContentLength <= 0)
            {
                ModelState.AddModelError("logoFile", "Logo image is required.");
            }

            if (!ModelState.IsValid)
                return View(vm);

            if (!IsSafeHttpUrl(vm.url))
            {
                ModelState.AddModelError("url", "URL must be a valid http(s) address.");
                return View(vm);
            }

            var logoUrl = SaveAffiliateLogo(vm.logoFile);
            if (logoUrl == null)
            {
                ModelState.AddModelError("logoFile", "This file type is not accepted.");
                return View(vm);
            }

            var link = new affiliatelink
            {
                title = vm.TitleEN,
                description = vm.DescEN,
                url = vm.url,
                sort_order = vm.sort_order,
                image = logoUrl
            };

            db.AffiliateLinks.Add(link);
            db.SaveChanges();

            var tr = new affiliatelinktranslation
            {
                affiliatelink_id = link.id,
                culture = "tr",
                title = vm.TitleTR ?? "",
                description = vm.DescTR ?? ""
            };

            db.affiliatelinktranslations.Add(tr);
            db.SaveChanges();

            TempData["ok"] = "Affiliate link added successfully!";
            return RedirectToAction("AffiliateLinks");
        }

        [Authorize]
        [HttpGet]
        public ActionResult affiliatelinkget(int id)
        {
            var a = db.AffiliateLinks
                      .Include(x => x.Translations)
                      .FirstOrDefault(x => x.id == id);
            if (a == null) return HttpNotFound();

            var tr = a.Translations?.FirstOrDefault(t => t.culture == "tr" || t.culture == "tr-TR");

            var vm = new AdminAffiliateLinkVM
            {
                id = a.id,
                TitleEN = a.title,
                DescEN = a.description,
                TitleTR = tr != null ? tr.title : "",
                DescTR = tr != null ? tr.description : "",
                url = a.url,
                sort_order = a.sort_order,
                existingImage = a.image
            };

            return View(vm);
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult affiliatelinkget(AdminAffiliateLinkVM vm)
        {
            var a = db.AffiliateLinks.Find(vm.id);
            if (a == null) return HttpNotFound();

            if (!ModelState.IsValid)
            {
                vm.existingImage = a.image;
                return View(vm);
            }

            if (!IsSafeHttpUrl(vm.url))
            {
                ModelState.AddModelError("url", "URL must be a valid http(s) address.");
                vm.existingImage = a.image;
                return View(vm);
            }

            if (vm.logoFile != null && vm.logoFile.ContentLength > 0)
            {
                var logoUrl = SaveAffiliateLogo(vm.logoFile);
                if (logoUrl == null)
                {
                    ModelState.AddModelError("logoFile", "This file type is not accepted.");
                    vm.existingImage = a.image;
                    return View(vm);
                }

                DeleteAffiliateLogo(a.image);
                a.image = logoUrl;
            }

            a.title = vm.TitleEN;
            a.description = vm.DescEN;
            a.url = vm.url;
            a.sort_order = vm.sort_order;

            var tr = db.affiliatelinktranslations
                       .FirstOrDefault(x => x.affiliatelink_id == a.id && (x.culture == "tr" || x.culture == "tr-TR"));

            if (tr == null)
            {
                tr = new affiliatelinktranslation { affiliatelink_id = a.id, culture = "tr" };
                db.affiliatelinktranslations.Add(tr);
            }

            tr.title = vm.TitleTR;
            tr.description = vm.DescTR;

            db.SaveChanges();
            TempData["ok"] = "Affiliate link updated successfully!";
            return RedirectToAction("affiliatelinkget", new { id = a.id });
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult affiliatelinkdelete(int id)
        {
            var a = db.AffiliateLinks.Find(id);
            if (a != null)
            {
                var translations = db.affiliatelinktranslations.Where(x => x.affiliatelink_id == id);
                db.affiliatelinktranslations.RemoveRange(translations);

                DeleteAffiliateLogo(a.image);
                db.AffiliateLinks.Remove(a);
                db.SaveChanges();
            }
            return RedirectToAction("AffiliateLinks");
        }

        private string SaveAffiliateLogo(HttpPostedFileBase file)
        {
            var ext = Path.GetExtension(file.FileName)?.ToLowerInvariant() ?? "";
            if (!AllowedLogoExtensions.Contains(ext))
                return null;

            var uploadsRoot = Server.MapPath("~/Content/uploads/affiliate-logos");
            if (!Directory.Exists(uploadsRoot))
                Directory.CreateDirectory(uploadsRoot);

            var fileName = Guid.NewGuid().ToString("N") + ext;
            var filePath = Path.Combine(uploadsRoot, fileName);
            file.SaveAs(filePath);

            return AffiliateLogosPrefix + fileName;
        }

        private void DeleteAffiliateLogo(string publicUrl)
        {
            if (string.IsNullOrWhiteSpace(publicUrl)) return;
            if (!publicUrl.StartsWith(AffiliateLogosPrefix, StringComparison.OrdinalIgnoreCase)) return;

            try
            {
                var physicalPath = Server.MapPath("~" + publicUrl);
                if (System.IO.File.Exists(physicalPath))
                    System.IO.File.Delete(physicalPath);
            }
            catch
            {
                // silently ignore, matches existing media-delete behavior
            }
        }

        // ================= SOCIAL ICONS (footer) =================

        [Authorize]
        public ActionResult SocialIcons()
        {
            var value = db.SocialIcons
                          .OrderBy(x => x.sort_order)
                          .ThenBy(x => x.id)
                          .ToList();
            return View(value);
        }

        [Authorize]
        [HttpGet]
        public ActionResult newsocialicon()
        {
            return View(new AdminSocialIconVM());
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult newsocialicon(AdminSocialIconVM vm)
        {
            if (!ModelState.IsValid)
                return View(vm);

            if (!IsSafeHttpUrl(vm.url))
            {
                ModelState.AddModelError("url", "URL must be a valid http(s) address.");
                return View(vm);
            }

            var icon = new socialicon
            {
                icon = vm.icon,
                url = vm.url,
                sort_order = vm.sort_order
            };

            db.SocialIcons.Add(icon);
            db.SaveChanges();

            TempData["ok"] = "Social icon added successfully!";
            return RedirectToAction("SocialIcons");
        }

        [Authorize]
        [HttpGet]
        public ActionResult socialiconget(int id)
        {
            var s = db.SocialIcons.Find(id);
            if (s == null) return HttpNotFound();

            var vm = new AdminSocialIconVM
            {
                id = s.id,
                icon = s.icon,
                url = s.url,
                sort_order = s.sort_order
            };

            return View(vm);
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult socialiconget(AdminSocialIconVM vm)
        {
            var s = db.SocialIcons.Find(vm.id);
            if (s == null) return HttpNotFound();

            if (!ModelState.IsValid)
                return View(vm);

            if (!IsSafeHttpUrl(vm.url))
            {
                ModelState.AddModelError("url", "URL must be a valid http(s) address.");
                return View(vm);
            }

            s.icon = vm.icon;
            s.url = vm.url;
            s.sort_order = vm.sort_order;

            db.SaveChanges();
            TempData["ok"] = "Social icon updated successfully!";
            return RedirectToAction("socialiconget", new { id = s.id });
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult socialicondelete(int id)
        {
            var s = db.SocialIcons.Find(id);
            if (s != null)
            {
                db.SocialIcons.Remove(s);
                db.SaveChanges();
            }
            return RedirectToAction("SocialIcons");
        }

        // ================= WALLPAPER (site background) =================

        private static readonly string[] AllowedWallpaperExtensions = { ".jpg", ".jpeg", ".png", ".webp", ".gif" };
        private const string WallpaperPrefix = "/Content/uploads/wallpaper/";
        private const string DefaultWallpaperUrl = "/web_atakanozbancom/assets/img/wp.jpg";

        [Authorize]
        [HttpGet]
        public ActionResult Wallpaper()
        {
            var setting = db.SiteSettings.FirstOrDefault();
            ViewBag.CurrentWallpaper = !string.IsNullOrWhiteSpace(setting?.wallpaper) ? setting.wallpaper : DefaultWallpaperUrl;
            return View();
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Wallpaper(HttpPostedFileBase wallpaperFile)
        {
            if (wallpaperFile == null || wallpaperFile.ContentLength <= 0)
            {
                TempData["ok"] = "Please choose an image.";
                return RedirectToAction("Wallpaper");
            }

            var ext = Path.GetExtension(wallpaperFile.FileName)?.ToLowerInvariant() ?? "";
            if (!AllowedWallpaperExtensions.Contains(ext))
            {
                TempData["ok"] = "This file type is not accepted.";
                return RedirectToAction("Wallpaper");
            }

            var uploadsRoot = Server.MapPath("~/Content/uploads/wallpaper");
            if (!Directory.Exists(uploadsRoot))
                Directory.CreateDirectory(uploadsRoot);

            var fileName = Guid.NewGuid().ToString("N") + ext;
            var filePath = Path.Combine(uploadsRoot, fileName);
            wallpaperFile.SaveAs(filePath);

            var publicUrl = WallpaperPrefix + fileName;

            var setting = db.SiteSettings.FirstOrDefault();
            if (setting == null)
            {
                setting = new sitesetting();
                db.SiteSettings.Add(setting);
            }
            else
            {
                DeleteWallpaperFile(setting.wallpaper);
            }

            setting.wallpaper = publicUrl;
            db.SaveChanges();

            TempData["ok"] = "Wallpaper updated successfully!";
            return RedirectToAction("Wallpaper");
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult WallpaperReset()
        {
            var setting = db.SiteSettings.FirstOrDefault();
            if (setting != null && !string.IsNullOrWhiteSpace(setting.wallpaper))
            {
                DeleteWallpaperFile(setting.wallpaper);
                setting.wallpaper = null;
                db.SaveChanges();
            }

            TempData["ok"] = "Wallpaper reset to default.";
            return RedirectToAction("Wallpaper");
        }

        private void DeleteWallpaperFile(string publicUrl)
        {
            if (string.IsNullOrWhiteSpace(publicUrl)) return;
            if (!publicUrl.StartsWith(WallpaperPrefix, StringComparison.OrdinalIgnoreCase)) return;

            try
            {
                var physicalPath = Server.MapPath("~" + publicUrl);
                if (System.IO.File.Exists(physicalPath))
                    System.IO.File.Delete(physicalPath);
            }
            catch
            {
            }
        }

        [Authorize]
        public ActionResult UserSettings()
        {
            var user = GetCurrentAdmin();
            if (user == null)
                return RedirectToAction("Index", "login");

            ViewBag.TwoFactorEnabled = user.two_factor_enabled;
            ViewBag.Username = user.username;

            if (!user.two_factor_enabled)
            {
                var secret = Session["Pending2FaSetupSecret"] as string;
                if (string.IsNullOrWhiteSpace(secret))
                {
                    secret = TotpHelper.GenerateSecret();
                    Session["Pending2FaSetupSecret"] = secret;
                }

                ViewBag.SetupSecret = secret;
                ViewBag.OtpAuthUri = TotpHelper.BuildOtpAuthUri("atakanozban.com", user.username, secret);
            }

            return View();
        }

        [Authorize]
        public ActionResult Security()
        {
            return RedirectToAction("UserSettings");
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
        {
            var user = GetCurrentAdmin();
            if (user == null)
                return RedirectToAction("Index", "login");

            if (string.IsNullOrWhiteSpace(currentPassword)
                || string.IsNullOrWhiteSpace(newPassword)
                || string.IsNullOrWhiteSpace(confirmPassword))
            {
                TempData["pwdError"] = "All password fields are required.";
                return RedirectToAction("UserSettings");
            }

            if (!PasswordHasher.Verify(currentPassword, user.password))
            {
                TempData["pwdError"] = "Current password is incorrect.";
                return RedirectToAction("UserSettings");
            }

            if (newPassword.Length < 8)
            {
                TempData["pwdError"] = "New password must be at least 8 characters.";
                return RedirectToAction("UserSettings");
            }

            if (!string.Equals(newPassword, confirmPassword, StringComparison.Ordinal))
            {
                TempData["pwdError"] = "New password and confirmation do not match.";
                return RedirectToAction("UserSettings");
            }

            user.password = PasswordHasher.Hash(newPassword);
            db.SaveChanges();

            TempData["pwdOk"] = "Password updated successfully.";
            return RedirectToAction("UserSettings");
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult EnableTwoFactor(string code)
        {
            var user = GetCurrentAdmin();
            if (user == null)
                return RedirectToAction("Index", "login");

            var secret = Session["Pending2FaSetupSecret"] as string;
            if (string.IsNullOrWhiteSpace(secret))
            {
                TempData["tfaError"] = "Setup expired. Please try again.";
                return RedirectToAction("UserSettings");
            }

            if (!TotpHelper.VerifyCode(secret, code))
            {
                TempData["tfaError"] = "Invalid code. Scan the QR again and enter a fresh code.";
                return RedirectToAction("UserSettings");
            }

            user.two_factor_secret = SecretProtector.Protect(secret);
            user.two_factor_enabled = true;
            db.SaveChanges();
            Session.Remove("Pending2FaSetupSecret");

            TempData["tfaOk"] = "Two-factor authentication is now enabled.";
            return RedirectToAction("UserSettings");
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult DisableTwoFactor(string code)
        {
            var user = GetCurrentAdmin();
            if (user == null)
                return RedirectToAction("Index", "login");

            if (!user.two_factor_enabled || string.IsNullOrWhiteSpace(user.two_factor_secret))
            {
                TempData["tfaError"] = "Two-factor authentication is not enabled.";
                return RedirectToAction("UserSettings");
            }

            var secret = SecretProtector.Unprotect(user.two_factor_secret) ?? user.two_factor_secret;
            if (!TotpHelper.VerifyCode(secret, code))
            {
                TempData["tfaError"] = "Invalid code. 2FA was not disabled.";
                return RedirectToAction("UserSettings");
            }

            user.two_factor_enabled = false;
            user.two_factor_secret = null;
            db.SaveChanges();
            Session.Remove("Pending2FaSetupSecret");

            TempData["tfaOk"] = "Two-factor authentication has been disabled.";
            return RedirectToAction("UserSettings");
        }

        private admin GetCurrentAdmin()
        {
            var username = User?.Identity?.Name;
            if (string.IsNullOrWhiteSpace(username))
                return null;

            return db.admins.FirstOrDefault(x => x.username == username);
        }

        private static bool IsSafeHttpUrl(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
                return false;

            return Uri.TryCreate(url.Trim(), UriKind.Absolute, out var uri)
                && (uri.Scheme == Uri.UriSchemeHttps || uri.Scheme == Uri.UriSchemeHttp);
        }

    }
}