using MemberCenter.Api.Contracts;
using MemberCenter.Application.Abstractions;
using MemberCenter.Application.Constants;
using MemberCenter.Infrastructure.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace MemberCenter.Api.Controllers;

[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    private readonly IAccountProvisioningService _accountProvisioningService;
    private readonly IAccountEmailService _accountEmailService;
    private readonly IAuditLogWriter _auditLogWriter;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;

    public AuthController(
        IAccountProvisioningService accountProvisioningService,
        IAccountEmailService accountEmailService,
        IAuditLogWriter auditLogWriter,
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager)
    {
        _accountProvisioningService = accountProvisioningService;
        _accountEmailService = accountEmailService;
        _auditLogWriter = auditLogWriter;
        _userManager = userManager;
        _signInManager = signInManager;
    }

    [HttpPost("register")]
    [EnableRateLimiting(RateLimitPolicyNames.PublicAuthRegister)]
    public async Task<IActionResult> Register([FromBody] RegisterRequest request)
    {
        var result = await _accountProvisioningService.RegisterLocalAsync(request.Email, request.Password);
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors);
        }

        var user = await _userManager.FindByEmailAsync(request.Email);
        if (user is not null)
        {
            await _accountEmailService.SendVerificationEmailAsync(user.Id, GetBaseUrl());
        }

        return Ok(new
        {
            id = result.UserId,
            email = result.Email,
            email_verified = result.EmailConfirmed,
            linked_subscriptions = result.LinkedSubscriptionsCount
        });
    }

    [HttpPost("password/forgot")]
    [EnableRateLimiting(RateLimitPolicyNames.PublicAuthRecovery)]
    public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordRequest request)
    {
        var user = await _userManager.FindByEmailAsync(request.Email);
        if (user is null)
        {
            return NoContent();
        }

        await _accountEmailService.SendPasswordResetEmailAsync(user.Id, GetBaseUrl());
        return NoContent();
    }

    [HttpPost("password/reset")]
    [EnableRateLimiting(RateLimitPolicyNames.PublicAuthTokenConsumption)]
    public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordRequest request)
    {
        var user = await _userManager.FindByEmailAsync(request.Email);
        if (user is null)
        {
            return BadRequest("Invalid user.");
        }

        var result = await _userManager.ResetPasswordAsync(user, request.Token, request.NewPassword);
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors.Select(e => e.Description));
        }

        await _auditLogWriter.WriteAsync("user", user.Id, "account.password_reset_completed", new
        {
            user_id = user.Id,
            email = user.Email
        });
        return NoContent();
    }

    [HttpGet("email/verify")]
    [EnableRateLimiting(RateLimitPolicyNames.PublicAuthTokenConsumption)]
    public async Task<IActionResult> VerifyEmail([FromQuery] string token, [FromQuery] string email)
    {
        var user = await _userManager.FindByEmailAsync(email);
        if (user is null)
        {
            return BadRequest("Invalid user.");
        }

        var result = await _userManager.ConfirmEmailAsync(user, token);
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors.Select(e => e.Description));
        }

        await _auditLogWriter.WriteAsync("user", user.Id, "account.email_verified", new
        {
            user_id = user.Id,
            email = user.Email
        });
        return Ok(new { status = "verified" });
    }

    [Authorize]
    [HttpPost("email/resend")]
    [EnableRateLimiting(RateLimitPolicyNames.PublicAuthRecovery)]
    public async Task<IActionResult> ResendVerification()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user is null)
        {
            return Unauthorized();
        }

        if (!user.EmailConfirmed)
        {
            await _accountEmailService.SendVerificationEmailAsync(user.Id, GetBaseUrl());
        }

        return NoContent();
    }

    [Authorize]
    [HttpPost("logout")]
    public async Task<IActionResult> Logout([FromBody] LogoutRequest request)
    {
        await _signInManager.SignOutAsync();
        return NoContent();
    }

    private string GetBaseUrl() => $"{Request.Scheme}://{Request.Host}{Request.PathBase}";
}
