using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
using atakanozbancom.Models.classes;

namespace atakanozbancom.Controllers
{
    public class loginController : Controller
    {
        private readonly Context c = new Context();
        private const int MaxPasswordFailures = 8;
        private const int MaxTotpFailures = 5;
        private const int LockMinutes = 15;

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

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Index(admin ad)
        {
            if (ad == null || string.IsNullOrWhiteSpace(ad.username) || string.IsNullOrWhiteSpace(ad.password))
            {
                ViewBag.Error = "Username and password are required.";
                return View();
            }

            var user = c.admins.FirstOrDefault(x => x.username == ad.username);
            if (user == null)
            {
                ViewBag.Error = "Invalid username or password.";
                return View();
            }

            if (user.login_lock_until.HasValue && user.login_lock_until.Value > DateTime.UtcNow)
            {
                ViewBag.Error = "Account temporarily locked. Try again later.";
                return View();
            }

            if (!PasswordHasher.Verify(ad.password, user.password))
            {
                user.login_failed_count++;
                if (user.login_failed_count >= MaxPasswordFailures)
                {
                    user.login_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
                    user.login_failed_count = 0;
                }
                c.SaveChanges();
                ViewBag.Error = "Invalid username or password.";
                return View();
            }

            user.login_failed_count = 0;
            user.login_lock_until = null;

            if (!PasswordHasher.IsHashed(user.password))
            {
                user.password = PasswordHasher.Hash(ad.password);
            }

            c.SaveChanges();
            ClearPending2Fa();

            if (user.two_factor_enabled && !string.IsNullOrWhiteSpace(user.two_factor_secret))
            {
                if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
                {
                    ViewBag.Error = "Too many failed 2FA attempts. Try again later.";
                    return View();
                }

                Session["Pending2FaUserId"] = user.id;
                Session["Pending2FaUsername"] = user.username;
                Session["Pending2FaExpiresUtc"] = DateTime.UtcNow.AddMinutes(10);
                return RedirectToAction("Verify");
            }

            CompleteLogin(user.username);
            return RedirectToAction("Index", "admin");
        }

        public ActionResult Verify()
        {
            if (!HasValidPending2Fa())
            {
                ClearPending2Fa();
                return RedirectToAction("Index");
            }

            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Verify(string code)
        {
            if (!HasValidPending2Fa())
            {
                ClearPending2Fa();
                return RedirectToAction("Index");
            }

            var pendingId = Convert.ToInt32(Session["Pending2FaUserId"]);
            var pendingUsername = Session["Pending2FaUsername"].ToString();

            var user = c.admins.FirstOrDefault(x => x.id == pendingId && x.username == pendingUsername);
            if (user == null || !user.two_factor_enabled || string.IsNullOrWhiteSpace(user.two_factor_secret))
            {
                ClearPending2Fa();
                return RedirectToAction("Index");
            }

            if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
            {
                ViewBag.Error = "Too many failed attempts. Try again later.";
                return View();
            }

            var secret = ResolveTotpSecret(user);
            if (string.IsNullOrWhiteSpace(secret) || !TotpHelper.VerifyCode(secret, code))
            {
                user.totp_failed_count++;
                if (user.totp_failed_count >= MaxTotpFailures)
                {
                    user.totp_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
                    user.totp_failed_count = 0;
                    ViewBag.Error = "Too many failed attempts. Try again later.";
                }
                else
                {
                    ViewBag.Error = "Invalid authentication code.";
                }
                c.SaveChanges();
                return View();
            }

            user.totp_failed_count = 0;
            user.totp_lock_until = null;
            c.SaveChanges();

            ClearPending2Fa();
            CompleteLogin(user.username);
            return RedirectToAction("Index", "admin");
        }

        public ActionResult logout()
        {
            ClearPending2Fa();
            Session.Remove("username");
            Session.Abandon();
            FormsAuthentication.SignOut();
            return RedirectToAction("index", "login");
        }

        private void CompleteLogin(string username)
        {
            FormsAuthentication.SetAuthCookie(username, false);
            Session["username"] = username;
        }

        private bool HasValidPending2Fa()
        {
            if (Session["Pending2FaUserId"] == null || Session["Pending2FaUsername"] == null)
                return false;

            var expires = Session["Pending2FaExpiresUtc"] as DateTime?;
            if (expires == null || expires.Value < DateTime.UtcNow)
                return false;

            return true;
        }

        private static string ResolveTotpSecret(admin user)
        {
            if (string.IsNullOrWhiteSpace(user.two_factor_secret))
                return null;

            var unprotected = SecretProtector.Unprotect(user.two_factor_secret);
            if (!string.IsNullOrWhiteSpace(unprotected))
                return unprotected;

            return user.two_factor_secret;
        }

        private void ClearPending2Fa()
        {
            Session.Remove("Pending2FaUserId");
            Session.Remove("Pending2FaUsername");
            Session.Remove("Pending2FaSetupSecret");
            Session.Remove("Pending2FaExpiresUtc");
        }
    }
}
