using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using PlotLine.Models;
using PlotLine.Services;
using PlotLine.ViewModels;

namespace PlotLine.Controllers;

public sealed class AccountController(IAuthService authService) : Controller
{
    [HttpGet]
    [AllowAnonymous]
    public IActionResult Register(string? returnUrl = null)
    {
        return View(new RegisterViewModel());
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Register(RegisterViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        RegistrationResult result;
        try
        {
            result = await authService.RegisterAsync(model);
        }
        catch
        {
            ModelState.AddModelError(string.Empty, "Your account could not be created because the verification email could not be queued. Please try again later.");
            return View(model);
        }
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Registration could not be completed.");
            return View(model);
        }

        TempData["AuthMessage"] = "Your account has been created. Please check your email to verify your account. If it does not arrive, use the resend verification email link below.";
        return RedirectToAction(nameof(Login));
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult Login(string? returnUrl = null, bool companion = false)
    {
        return View(new LoginViewModel { ReturnUrl = returnUrl, IsCompanionLogin = IsCompanionLogin(returnUrl, companion) });
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        if (!ModelState.IsValid)
        {
            model.IsCompanionLogin = IsCompanionLogin(model.ReturnUrl, model.IsCompanionLogin);
            return View(model);
        }

        var result = await authService.LoginAsync(model);
        if (result.RequiresTwoFactor)
        {
            return RedirectToAction(nameof(TwoFactor), new { returnUrl = model.ReturnUrl });
        }

        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Login could not be completed.");
            model.IsCompanionLogin = IsCompanionLogin(model.ReturnUrl, model.IsCompanionLogin);
            return View(model);
        }

        if (!string.IsNullOrWhiteSpace(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
        {
            return LocalRedirect(model.ReturnUrl);
        }

        return RedirectToAction("Index", "Projects");
    }

    private static bool IsCompanionLogin(string? returnUrl, bool explicitCompanion)
    {
        if (explicitCompanion)
        {
            return true;
        }

        return !string.IsNullOrWhiteSpace(returnUrl)
            && returnUrl.StartsWith("/word-companion", StringComparison.OrdinalIgnoreCase);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout()
    {
        await authService.LogoutAsync();
        return RedirectToAction("Index", "Home");
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult AccessDenied()
    {
        return View();
    }

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> VerifyEmail(Guid token)
    {
        if (token == Guid.Empty)
        {
            return View(new AccountStatusViewModel
            {
                Success = false,
                Title = "Verification link problem",
                Message = "This verification link is not valid."
            });
        }

        var result = await authService.VerifyEmailAsync(token);
        if (result.Success)
        {
            return RedirectToAction(nameof(VerifyEmailConfirmation));
        }

        return View(new AccountStatusViewModel
        {
            Success = false,
            Title = "Verification link problem",
            Message = result.ErrorMessage ?? "This verification link could not be used."
        });
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult VerifyEmailConfirmation()
    {
        return View();
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ForgotPassword()
    {
        return View(new ForgotPasswordViewModel());
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        try
        {
            var result = await authService.ForgotPasswordAsync(model);
            if (!result.Success)
            {
                ModelState.AddModelError(string.Empty, "We could not queue a password reset email right now. Please try again later.");
                return View(model);
            }
        }
        catch
        {
            ModelState.AddModelError(string.Empty, "We could not queue a password reset email right now. Please try again later.");
            return View(model);
        }

        TempData["AuthMessage"] = "If an account exists for that email address, a password reset email will be sent shortly.";
        return RedirectToAction(nameof(Login));
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ResetPassword(Guid token)
    {
        if (token == Guid.Empty)
        {
            return View(new ResetPasswordViewModel());
        }

        return View(new ResetPasswordViewModel { Token = token });
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var result = await authService.ResetPasswordAsync(model);
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Your password could not be reset.");
            return View(model);
        }

        TempData["AuthMessage"] = "Your password has been reset. You can now log in.";
        return RedirectToAction(nameof(Login));
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ResendVerificationEmail()
    {
        return View(new ResendVerificationEmailViewModel());
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ResendVerificationEmail(ResendVerificationEmailViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var result = await authService.ResendVerificationEmailAsync(model.Email);
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "We could not send the verification email right now. Please try again in a few minutes.");
            return View(model);
        }

        TempData["AuthMessage"] = "If an unverified account exists for that email address, a verification email will be sent shortly.";
        return View(new ResendVerificationEmailViewModel());
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult TwoFactor(string? returnUrl = null)
    {
        if (!authService.HasPendingTwoFactorLogin())
        {
            return RedirectToAction(nameof(Login));
        }

        return View(new TwoFactorCodeViewModel
        {
            ReturnUrl = returnUrl ?? authService.GetPendingTwoFactorReturnUrl()
        });
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> TwoFactor(TwoFactorCodeViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        var result = await authService.CompleteTwoFactorLoginAsync(model);
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Two-factor login could not be completed.");
            return View(model);
        }

        if (!string.IsNullOrWhiteSpace(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
        {
            return LocalRedirect(model.ReturnUrl);
        }

        return RedirectToAction("Index", "Projects");
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult TwoFactorRecovery(string? returnUrl = null)
    {
        if (!authService.HasPendingTwoFactorLogin())
        {
            return RedirectToAction(nameof(Login));
        }

        return View(new TwoFactorRecoveryViewModel
        {
            ReturnUrl = returnUrl ?? authService.GetPendingTwoFactorReturnUrl()
        });
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> TwoFactorRecovery(TwoFactorRecoveryViewModel model)
    {
        if (!authService.HasPendingTwoFactorLogin())
        {
            return RedirectToAction(nameof(Login));
        }

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

        var result = await authService.CompleteTwoFactorRecoveryLoginAsync(model);
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Recovery code login could not be completed.");
            return View(model);
        }

        if (!string.IsNullOrWhiteSpace(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
        {
            return LocalRedirect(model.ReturnUrl);
        }

        return RedirectToAction("Index", "Projects");
    }
}
