using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using QRRapidoApp.Models.ViewModels;
using QRRapidoApp.Services;
using System.Security.Claims;
using System.Text.Json;
using System.Text;
using Microsoft.AspNetCore.DataProtection;

namespace QRRapidoApp.Controllers
{
    public class AccountController : Controller
    {
        private readonly IUserService _userService;
        private readonly AdDisplayService _adDisplayService;
        private readonly ILogger<AccountController> _logger;
        private readonly IConfiguration _configuration;
        private readonly IDataProtector _protector;

        public AccountController(IUserService userService, AdDisplayService adDisplayService,
                                ILogger<AccountController> logger, IConfiguration configuration,
                                IDataProtectionProvider dataProtection)
        {
            _userService = userService;
            _adDisplayService = adDisplayService;
            _logger = logger;
            _configuration = configuration;
            _protector = dataProtection.CreateProtector("OAuth.StateProtection");
        }

        [HttpGet]
        public IActionResult Login(string returnUrl = "/")
        {
            _adDisplayService.SetViewBagAds(ViewBag);
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        [HttpGet]
        public IActionResult LoginGoogle(string returnUrl = "/")
        {
            var baseUrl = _configuration.GetSection("App:BaseUrl").Value;

            // Criar state com dados criptografados em vez de sessão
            var stateData = new OAuthStateData
            {
                ReturnUrl = returnUrl,
                Nonce = Guid.NewGuid().ToString(),
                Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
            };

            var stateJson = JsonSerializer.Serialize(stateData);
            var protectedState = _protector.Protect(stateJson);
            var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState));

            var properties = new AuthenticationProperties
            {
                RedirectUri = $"{baseUrl}{Url.Action("GoogleCallback")}",
                Items = { { "state", encodedState } }
            };

            return Challenge(properties, GoogleDefaults.AuthenticationScheme);
        }

        [HttpGet]
        public IActionResult LoginMicrosoft(string returnUrl = "/")
        {
            var baseUrl = _configuration.GetSection("App:BaseUrl").Value;

            // Mesmo processo para Microsoft
            var stateData = new OAuthStateData
            {
                ReturnUrl = returnUrl,
                Nonce = Guid.NewGuid().ToString(),
                Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
            };

            var stateJson = JsonSerializer.Serialize(stateData);
            var protectedState = _protector.Protect(stateJson);
            var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState));

            var redirectUrl = returnUrl == "/"
                ? $"{baseUrl}/Account/MicrosoftCallback"
                : $"{baseUrl}/Account/MicrosoftCallback";

            var properties = new AuthenticationProperties
            {
                RedirectUri = redirectUrl,
                Items = { { "state", encodedState } }
            };

            return Challenge(properties, MicrosoftAccountDefaults.AuthenticationScheme);
        }

        [HttpGet]
        public async Task<IActionResult> GoogleCallback(string state = null)
        {
            var returnUrl = await HandleExternalLoginCallbackAsync(GoogleDefaults.AuthenticationScheme, state);
            return Redirect(returnUrl ?? "/");
        }

        [HttpGet]
        public async Task<IActionResult> MicrosoftCallback(string state = null)
        {
            var returnUrl = await HandleExternalLoginCallbackAsync(MicrosoftAccountDefaults.AuthenticationScheme, state);
            return Redirect(returnUrl ?? "/");
        }

        private async Task<string> HandleExternalLoginCallbackAsync(string scheme, string state = null)
        {
            try
            {
                _adDisplayService.SetViewBagAds(ViewBag);

                // Recuperar returnUrl do state em vez da sessão
                string returnUrl = "/";
                if (!string.IsNullOrEmpty(state))
                {
                    try
                    {
                        var decodedState = Encoding.UTF8.GetString(Convert.FromBase64String(state));
                        var unprotectedState = _protector.Unprotect(decodedState);
                        var stateData = JsonSerializer.Deserialize<OAuthStateData>(unprotectedState);

                        // Validar timestamp (não mais que 10 minutos)
                        var maxAge = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - (10 * 60);
                        if (stateData.Timestamp > maxAge)
                        {
                            returnUrl = stateData.ReturnUrl ?? "/";
                        }
                        else
                        {
                            _logger.LogWarning($"OAuth state expired for scheme {scheme}");
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, $"Failed to decode OAuth state for scheme {scheme}");
                    }
                }

                var result = await HttpContext.AuthenticateAsync(scheme);
                if (!result.Succeeded)
                {
                    _logger.LogWarning($"External authentication failed for scheme {scheme}");
                    return "/Account/Login";
                }

                var email = result.Principal?.FindFirst(ClaimTypes.Email)?.Value;
                var name = result.Principal?.FindFirst(ClaimTypes.Name)?.Value;
                var providerId = result.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;

                if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(providerId))
                {
                    _logger.LogWarning($"Missing required claims from {scheme} authentication");
                    return "/Account/Login";
                }

                // Find or create user
                var user = await _userService.GetUserByProviderAsync(scheme, providerId);
                if (user == null)
                {
                    user = await _userService.CreateUserAsync(email, name ?? email, scheme, providerId);
                }
                else
                {
                    await _userService.UpdateLastLoginAsync(user.Id);
                }

                // Create application claims
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, user.Id),
                    new Claim(ClaimTypes.Email, user.Email),
                    new Claim(ClaimTypes.Name, user.Name),
                    new Claim("Provider", user.Provider),
                    new Claim("IsPremium", user.IsPremium.ToString())
                };

                var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                var authProperties = new AuthenticationProperties
                {
                    IsPersistent = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30)
                };

                await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(claimsIdentity), authProperties);

                return returnUrl;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error in external login callback for {scheme}");
                return "/Account/Login";
            }
        }

        [HttpPost]
        [Authorize]
        public async Task<IActionResult> Logout()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return RedirectToAction("Index", "Home");
        }

        [HttpGet]
        [Authorize]
        public async Task<IActionResult> Profile()
        {
            var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (string.IsNullOrEmpty(userId))
            {
                return RedirectToAction("Login");
            }

            var user = await _userService.GetUserAsync(userId);
            if (user == null)
            {
                return RedirectToAction("Login");
            }

            ViewBag.QRHistory = await _userService.GetUserQRHistoryAsync(userId, 10);
            ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId);
            ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId);
            _adDisplayService.SetViewBagAds(ViewBag);

            return View(user);
        }

        [HttpGet]
        public async Task<IActionResult> AdFreeStatus()
        {
            var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            if (string.IsNullOrEmpty(userId))
            {
                return Json(new AdFreeStatusViewModel
                {
                    IsAdFree = false,
                    TimeRemaining = 0,
                    IsPremium = false
                });
            }

            var shouldShowAds = await _adDisplayService.ShouldShowAds(userId);
            var isPremium = await _adDisplayService.HasValidPremiumSubscription(userId);
            var expiryDate = await _adDisplayService.GetAdFreeExpiryDate(userId);
            var status = await _adDisplayService.GetAdFreeStatusAsync(userId);

            return Json(new AdFreeStatusViewModel
            {
                IsAdFree = !shouldShowAds,
                TimeRemaining = isPremium ? int.MaxValue : 0,
                IsPremium = isPremium,
                ExpiryDate = expiryDate,
                SessionType = status
            });
        }

        [HttpPost]
        [Authorize]
        public async Task<IActionResult> ExtendAdFreeTime(int minutes)
        {
            var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            // Método removido - sem extensão de tempo ad-free
            return Json(new { success = false, message = "Feature not available" });
        }

        [HttpGet]
        [Authorize]
        public async Task<IActionResult> History()
        {
            _adDisplayService.SetViewBagAds(ViewBag);
            var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (string.IsNullOrEmpty(userId))
            {
                return RedirectToAction("Login");
            }

            var history = await _userService.GetUserQRHistoryAsync(userId, 50);
            return View(history);
        }

        [HttpPost]
        [Authorize]
        public async Task<IActionResult> UpdatePreferences(string language)
        {
            var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (string.IsNullOrEmpty(userId))
            {
                return Json(new { success = false });
            }

            try
            {
                var user = await _userService.GetUserAsync(userId);
                if (user != null)
                {
                    user.PreferredLanguage = language;
                    await _userService.UpdateUserAsync(user);
                    return Json(new { success = true });
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error updating preferences for user {userId}");
            }

            return Json(new { success = false });
        }
    }

    // Classe para dados do state
    public class OAuthStateData
    {
        public string ReturnUrl { get; set; } = "/";
        public string Nonce { get; set; } = "";
        public long Timestamp { get; set; }
    }
}