using System.Text.Json;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using PlotLine.Data;
using PlotLine.Models;
using PlotLine.Services;
using PlotLine.ViewModels;

namespace PlotLine.Controllers;

[Authorize]
public sealed class ManageAccountController(
    ICurrentUserService currentUser,
    IUserRepository users,
    IAuthService authService,
    IAccountDeletionService accountDeletion,
    ITwoFactorService twoFactorService,
    ISubscriptionService subscriptions,
    IStripeBillingService stripeBilling,
    ILogger<ManageAccountController> logger) : Controller
{
    [HttpGet]
    public async Task<IActionResult> Index()
    {
        var user = await GetCurrentUserAsync();
        if (user is null)
        {
            return RedirectToAction("Login", "Account");
        }

        var subscription = await subscriptions.GetCurrentSubscriptionAsync(user.UserID);
        return View(new ManageAccountViewModel
        {
            User = user,
            Subscription = subscription,
            Trial = TrialVisibilityViewModel.FromSubscription(subscription, DateTime.UtcNow),
            HasPaidBillingRisk = await subscriptions.HasPaidBillingRiskAsync(user.UserID)
        });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> HardDeleteAccount(string? confirmationEmail)
    {
        var userId = currentUser.UserId;
        if (!userId.HasValue)
        {
            return RedirectToAction("Login", "Account");
        }

        var user = await users.GetByIdAsync(userId.Value);
        if (user is null)
        {
            TempData["AuthMessage"] = "Your account could not be found.";
            return RedirectToAction("Login", "Account");
        }

        if (!string.Equals(confirmationEmail?.Trim(), user.Email, StringComparison.OrdinalIgnoreCase))
        {
            TempData["AuthError"] = "The email address entered did not match your account email.";
            return RedirectToAction(nameof(Index));
        }

        AccountDeletionResult result;
        try
        {
            result = await accountDeletion.HardDeleteAccountAsync(user.UserID);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Account hard delete failed unexpectedly for user {UserID}.", user.UserID);
            TempData["AuthError"] = "Your account could not be deleted. Please try again or contact support.";
            return RedirectToAction(nameof(Index));
        }

        if (result.BlockedByPaidSubscription)
        {
            TempData["AuthError"] = "You currently have an active paid subscription. Please cancel it before deleting your account.";
            return RedirectToAction(nameof(Index));
        }

        if (!result.Succeeded)
        {
            TempData["AuthError"] = "Your account could not be deleted. Please try again or contact support.";
            return RedirectToAction(nameof(Index));
        }

        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        TempData["AuthMessage"] = "Your account has been permanently deleted.";
        return RedirectToAction("Login", "Account");
    }

    [HttpGet]
    public async Task<IActionResult> Subscription()
    {
        var user = await GetCurrentUserAsync();
        if (user is null)
        {
            return RedirectToAction("Login", "Account");
        }

        var subscription = await subscriptions.GetCurrentSubscriptionAsync(user.UserID);
        return View(new ManageSubscriptionViewModel
        {
            User = user,
            CurrentSubscription = subscription,
            Trial = TrialVisibilityViewModel.FromSubscription(subscription, DateTime.UtcNow),
            Usage = subscription is null ? null : await subscriptions.GetUsageSummaryAsync(user.UserID),
            AvailableLevels = await subscriptions.GetActiveSubscriptionLevelsAsync()
        });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult SubscriptionPlaceholder()
    {
        TempData["SubscriptionMessage"] = "Online subscription changes will be available soon.";
        return RedirectToAction(nameof(Subscription));
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> SubscriptionCheckout(StripeCheckoutRequest request)
    {
        var user = await GetCurrentUserAsync();
        if (user is null)
        {
            return RedirectToAction("Login", "Account");
        }

        var successUrl = Url.Action(nameof(SubscriptionSuccess), "ManageAccount", null, Request.Scheme) ?? string.Empty;
        var cancelUrl = Url.Action(nameof(SubscriptionCancelled), "ManageAccount", null, Request.Scheme) ?? string.Empty;
        var result = await stripeBilling.StartCheckoutOrChangePlanAsync(user, request, successUrl, cancelUrl);
        if (!result.Succeeded)
        {
            TempData["SubscriptionMessage"] = result.Message;
            return RedirectToAction(nameof(Subscription));
        }

        if (!string.IsNullOrWhiteSpace(result.RedirectUrl))
        {
            return Redirect(result.RedirectUrl);
        }

        TempData["SubscriptionMessage"] = result.Message;
        return RedirectToAction(nameof(Subscription));
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> CancelSubscription()
    {
        var user = await GetCurrentUserAsync();
        if (user is null)
        {
            return RedirectToAction("Login", "Account");
        }

        var result = await stripeBilling.CancelAtPeriodEndAsync(user);
        TempData["SubscriptionMessage"] = result.Message;
        return RedirectToAction(nameof(Subscription));
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ManageBilling()
    {
        var user = await GetCurrentUserAsync();
        if (user is null)
        {
            return RedirectToAction("Login", "Account");
        }

        var returnUrl = Url.Action(nameof(Subscription), "ManageAccount", null, Request.Scheme) ?? string.Empty;
        var result = await stripeBilling.CreateBillingPortalSessionAsync(user, returnUrl);
        if (result.Succeeded && !string.IsNullOrWhiteSpace(result.RedirectUrl))
        {
            return Redirect(result.RedirectUrl);
        }

        TempData["SubscriptionMessage"] = result.Message;
        return RedirectToAction(nameof(Subscription));
    }

    [HttpGet]
    public IActionResult SubscriptionSuccess()
    {
        TempData["SubscriptionMessage"] = "Thank you. Your subscription is being confirmed. This usually only takes a few moments.";
        return RedirectToAction(nameof(Subscription));
    }

    [HttpGet]
    public IActionResult SubscriptionCancelled()
    {
        TempData["SubscriptionMessage"] = "Stripe Checkout was cancelled. No subscription changes were made.";
        return RedirectToAction(nameof(Subscription));
    }

    [HttpGet]
    public IActionResult ChangePassword()
    {
        return View(new ChangePasswordViewModel());
    }

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

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

        TempData["AuthMessage"] = "Your password has been changed. Please sign in again.";
        return RedirectToAction("Login", "Account");
    }

    [HttpGet]
    public async Task<IActionResult> EnableTwoFactor()
    {
        var user = await GetCurrentUserAsync();
        if (user is null)
        {
            return RedirectToAction("Login", "Account");
        }

        var secret = twoFactorService.GenerateSecretKey();
        var uri = twoFactorService.GenerateQrCodeUri(user.Email, secret);
        return View(new EnableTwoFactorViewModel
        {
            SecretKey = secret,
            QrCodeUri = uri,
            QrCodeImageDataUri = twoFactorService.GenerateQrCodeDataUri(uri)
        });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> EnableTwoFactor(EnableTwoFactorViewModel model)
    {
        if (!ModelState.IsValid)
        {
            model.QrCodeUri = twoFactorService.GenerateQrCodeUri(currentUser.Email ?? string.Empty, model.SecretKey);
            model.QrCodeImageDataUri = twoFactorService.GenerateQrCodeDataUri(model.QrCodeUri);
            return View(model);
        }

        var result = await authService.EnableTwoFactorAsync(currentUser.UserId!.Value, model.SecretKey, model.Code);
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Two-factor authentication could not be enabled.");
            model.QrCodeUri = twoFactorService.GenerateQrCodeUri(currentUser.Email ?? string.Empty, model.SecretKey);
            model.QrCodeImageDataUri = twoFactorService.GenerateQrCodeDataUri(model.QrCodeUri);
            return View(model);
        }

        TempData["RecoveryCodes"] = JsonSerializer.Serialize(result.RecoveryCodes);
        TempData["AuthMessage"] = "Two-factor authentication has been enabled.";
        return RedirectToAction(nameof(RecoveryCodes));
    }

    [HttpGet]
    public IActionResult DisableTwoFactor()
    {
        return View(new DisableTwoFactorViewModel());
    }

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

        var result = await authService.DisableTwoFactorAsync(currentUser.UserId!.Value, model.CurrentPassword);
        if (!result.Success)
        {
            ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Two-factor authentication could not be disabled.");
            return View(model);
        }

        TempData["AuthMessage"] = "Two-factor authentication has been disabled.";
        return RedirectToAction(nameof(Index));
    }

    [HttpGet]
    public IActionResult RecoveryCodes()
    {
        var codes = TempData["RecoveryCodes"] is string json
            ? JsonSerializer.Deserialize<IReadOnlyList<string>>(json) ?? Array.Empty<string>()
            : Array.Empty<string>();

        return View(new RecoveryCodesViewModel { RecoveryCodes = codes });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RegenerateRecoveryCodes()
    {
        var codes = await authService.GenerateRecoveryCodesAsync(currentUser.UserId!.Value);
        TempData["RecoveryCodes"] = JsonSerializer.Serialize(codes);
        TempData["AuthMessage"] = "New recovery codes have been generated.";
        return RedirectToAction(nameof(RecoveryCodes));
    }

    private async Task<PlotLine.Models.AppUser?> GetCurrentUserAsync()
    {
        return currentUser.UserId is int userId ? await users.GetByIdAsync(userId) : null;
    }
}
