using Microsoft.AspNetCore.Mvc; // 引入 ASP.NET Core MVC 的核心命名空间，提供控制器、视图等功能
using System.Threading.Tasks; // 引入用于异步操作的命名空间
using Microsoft.AspNetCore.Authentication; // 引入认证相关的命名空间
using Microsoft.AspNetCore.Authentication.Cookies; // 引入 Cookie 认证方案的命名空间
using iyixiGames.Web.Models; // 引入 Web 项目中定义的视图模型（LoginViewModel, RegisterViewModel）
using iyixiGames.Infrastructure.Services; // 引入基础设施层服务，例如 IAuthService
using iyixiGames.Application.Services; // 引入应用层服务，例如 IUserService
using System.Security.Claims; // 引入用于声明（Claims）的命名空间，用于构建用户身份
using Microsoft.Extensions.Logging; // 引入日志记录的命名空间

namespace iyixiGames.Web.Controllers
{
    /// <summary>
    /// 处理用户认证（登录、注册、注销）的控制器。
    /// </summary>
    public class AccountController : Controller
    {
        private readonly IAuthService _authService; // 认证服务接口，负责用户凭据验证和身份构建
        private readonly IUserService _userService; // 用户服务接口，负责用户注册、查询等业务操作
        private readonly ILogger<AccountController> _logger; // 日志记录器，用于记录控制器中的操作和错误

        /// <summary>
        /// AccountController 的构造函数。
        /// 通过依赖注入获取所需的认证服务、用户服务和日志记录器实例。
        /// </summary>
        /// <param name="authService">认证服务的实现。</param>
        /// <param name="userService">用户服务的实现。</param>
        /// <param name="logger">日志记录器的实例。</param>
        public AccountController(IAuthService authService, IUserService userService, ILogger<AccountController> logger)
        {
            _authService = authService;
            _userService = userService;
            _logger = logger;
        }

        // --- 登录相关操作 ---

        /// <summary>
        /// 显示用户登录表单的 GET 请求处理方法。
        /// </summary>
        /// <param name="returnUrl">可选参数，表示用户登录成功后应重定向到的原始 URL。</param>
        /// <returns>登录视图。</returns>
        [HttpGet]
        public IActionResult Login(string returnUrl = null)
        {
            // 将 returnUrl 传递给视图，以便登录表单提交时可以带回此信息，用于登录后重定向。
            ViewData["ReturnUrl"] = returnUrl;
            return View(); // 返回 Login.cshtml 视图
        }

        /// <summary>
        /// 处理用户登录表单提交的 POST 请求。
        /// </summary>
        /// <param name="model">从登录表单提交的数据，绑定到 LoginViewModel。</param>
        /// <param name="returnUrl">可选参数，登录成功后重定向到的原始 URL。</param>
        /// <returns>成功则重定向，失败则返回带有错误信息的登录视图。</returns>
        [HttpPost]
        [ValidateAntiForgeryToken] // 防止跨站请求伪造 (CSRF) 攻击，确保表单提交的安全性
        public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
        {
            // 在模型验证失败时，重新将 returnUrl 传递给视图。
            ViewData["ReturnUrl"] = returnUrl;

            // 检查提交的模型是否通过了数据注解验证（例如，是否填写了必填项）。
            if (!ModelState.IsValid)
            {
                _logger.LogWarning("登录尝试失败：模型验证不通过，用户名: {Username}", model.Username);
                return View(model); // 返回登录视图，并显示验证错误信息
            }

            // 调用认证服务，尝试验证用户凭据。
            // AuthenticateUserAsync 会返回一个 ClaimsPrincipal 对象，如果验证失败则返回 null。
            var principal = await _authService.AuthenticateUserAsync(model.Username, model.Password);

            // 如果认证服务返回 null，表示用户名或密码无效。
            if (principal == null)
            {
                // 向 ModelState 添加一个泛型错误，在视图中显示为“无效的用户名或密码”。
                ModelState.AddModelError(string.Empty, "无效的用户名或密码。");
                _logger.LogWarning("登录尝试失败：凭据无效，用户名: {Username}", model.Username);
                return View(model); // 返回登录视图，显示错误信息
            }

            // --- 用户认证成功处理 ---

            // 设置认证属性，用于配置认证 Cookie 的行为。
            var authProperties = new AuthenticationProperties
            {
                // IsPersistent 为 true 表示生成一个持久化的 Cookie（“记住我”功能），
                // 用户关闭浏览器后下次访问仍保持登录状态。
                IsPersistent = model.RememberMe,
                // 设置 Cookie 的过期时间，如果“记住我”功能开启，则设置为从现在起 7 天后过期。
                // 否则，默认为会话 Cookie，浏览器关闭即失效。
                ExpiresUtc = model.RememberMe ? DateTimeOffset.UtcNow.AddDays(7) : (DateTimeOffset?)null
            };

            // 使用 HttpContext.SignInAsync 方法将用户登录。
            // 这将创建一个认证 Cookie 并发送到客户端浏览器。
            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme, // 指定使用的认证方案（这里是 Cookie 认证）
                principal, // 包含用户身份和声明的 ClaimsPrincipal 对象
                authProperties // 认证属性
            );

            // 登录成功后，更新用户的最后登录时间。
            // 从 ClaimsPrincipal 中查找存储的用户 ID 声明。
            var userIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier);
            if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
            {
                // 调用认证服务更新用户最后登录时间。
                await _authService.UpdateLastLoginTimeAsync(userId);
            }
            else
            {
                // 如果无法获取用户 ID，记录一个错误日志。
                _logger.LogError("无法从认证主体中获取用户ID。登录用户: {Username}", model.Username);
            }

            _logger.LogInformation("用户 {Username} 成功登录。", model.Username);

            // --- 重定向用户 ---
            // 优先重定向到 returnUrl，但要确保它是本地 URL，以防止开放重定向漏洞。
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                // 如果没有 returnUrl 或 returnUrl 不安全，则重定向到默认主页。
                return RedirectToAction("Index", "Home"); // 重定向到 HomeController 的 Index 方法
            }
        }

        // --- 注册相关操作 ---

        /// <summary>
        /// 显示用户注册表单的 GET 请求处理方法。
        /// </summary>
        /// <returns>注册视图。</returns>
        [HttpGet]
        public IActionResult Register()
        {
            return View(); // 返回 Register.cshtml 视图
        }

        /// <summary>
        /// 处理用户注册表单提交的 POST 请求。
        /// </summary>
        /// <param name="model">从注册表单提交的数据，绑定到 RegisterViewModel。</param>
        /// <returns>成功则重定向到登录页，失败则返回带有错误信息的注册视图。</returns>
        [HttpPost]
        [ValidateAntiForgeryToken] // 防止 CSRF 攻击
        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            // 检查提交的模型是否通过了数据注解验证。
            if (!ModelState.IsValid)
            {
                _logger.LogWarning("注册尝试失败：模型验证不通过，用户名: {Username}, 邮箱: {Email}", model.Username, model.Email);
                return View(model); // 返回注册视图，并显示验证错误信息
            }

            // 调用用户服务，尝试注册新用户。
            var newUser = await _userService.RegisterUserAsync(
                model.Username,
                model.Email,
                model.FullName,
                model.Password // 传递明文密码，由 UserService 负责哈希处理
            );

            // 如果用户服务返回 null，表示注册失败（例如，用户名或邮箱已被占用）。
            if (newUser == null)
            {
                // 根据具体失败原因向 ModelState 添加错误，以便前端显示。
                // 检查用户名是否已被占用（GetUserByUsernameAsync 会检查激活状态）。
                if (await _userService.GetUserByUsernameAsync(model.Username) != null)
                {
                    ModelState.AddModelError("Username", "该用户名已被占用。");
                }

                // 检查邮箱是否已被注册（GetUserByEmailAsync 仅检查邮箱本身，不考虑激活状态）。
                if (await _userService.GetUserByEmailAsync(model.Email) != null)
                {
                    ModelState.AddModelError("Email", "该电子邮件地址已被注册。");
                }

                _logger.LogWarning("注册失败：用户名或邮箱已被占用，用户名: {Username}, 邮箱: {Email}", model.Username, model.Email);
                return View(model); // 返回注册视图，显示具体错误信息
            }

            // --- 用户注册成功处理 ---
            _logger.LogInformation("用户 '{Username}' 成功注册。", newUser.Username);

            // 使用 TempData 传递一个成功消息到下一个请求（即登录页面）。
            // TempData 是一种在两个连续请求之间传递数据的方式。
            TempData["SuccessMessage"] = "注册成功！请使用您的新账户登录。";

            // 注册成功后，重定向到登录页面，让用户手动登录。
            return RedirectToAction("Login", "Account");
        }


        // --- 注销操作 ---

        /// <summary>
        /// 处理用户注销请求的 POST 方法。
        /// </summary>
        /// <returns>重定向到登录页面。</returns>
        [HttpPost]
        [ValidateAntiForgeryToken] // 防止 CSRF 攻击
        public async Task<IActionResult> Logout()
        {
            // 使用 HttpContext.SignOutAsync 方法将用户从认证方案中注销，
            // 从而移除客户端浏览器中的认证 Cookie。
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            _logger.LogInformation("用户已注销。");
            return RedirectToAction("Login", "Account"); // 注销后重定向到登录页面
        }
    }
}