using System.Web;
using foodmarket.Infrastructure.Identity;
using foodmarket.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace foodmarket.Api.Controllers;

/// <summary>TOTP-2FA для текущего пользователя. Использует встроенный
/// <c>AuthenticatorTokenProvider</c> ASP.NET Identity — RFC 6238 (Google
/// Authenticator, Authy, 1Password OTP).
///
/// Flow:
/// 1. POST <c>/api/me/2fa/enroll</c> — генерирует секрет (если ещё нет) и
///    возвращает QR-URI (otpauth://) + сам ключ. Клиент рисует QR.
/// 2. POST <c>/api/me/2fa/verify</c> с code из приложения — проверяет и
///    включает <c>TwoFactorEnabled</c>. После этого password-flow требует
///    otp_code.
/// 3. POST <c>/api/me/2fa/disable</c> с code — выключает.
///
/// Дополнительной фичи (backup-коды) пока нет — добавим если попросят.</summary>
[ApiController]
[Authorize]
[Route("api/me/2fa")]
public class TwoFactorController : ControllerBase
{
    private readonly UserManager<User> _users;
    private readonly AppDbContext _db;
    private readonly IConfiguration _cfg;

    public TwoFactorController(UserManager<User> users, AppDbContext db, IConfiguration cfg)
    {
        _users = users;
        _db = db;
        _cfg = cfg;
    }

    public record EnrollResult(string SharedKey, string AuthenticatorUri, bool AlreadyEnabled);
    public record CodeInput(string Code);
    public record StatusDto(bool Enabled);

    [HttpGet("status")]
    public async Task<ActionResult<StatusDto>> Status()
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        return new StatusDto(await _users.GetTwoFactorEnabledAsync(user));
    }

    [HttpPost("enroll")]
    public async Task<ActionResult<EnrollResult>> Enroll()
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();

        var already = await _users.GetTwoFactorEnabledAsync(user);
        // Если уже включено — не отдаём секрет; сначала disable.
        if (already)
            return Ok(new EnrollResult("", "", true));

        var key = await _users.GetAuthenticatorKeyAsync(user);
        if (string.IsNullOrEmpty(key))
        {
            await _users.ResetAuthenticatorKeyAsync(user);
            key = await _users.GetAuthenticatorKeyAsync(user);
        }

        var issuer = _cfg["App:Name"] ?? "food-market";
        var label = $"{issuer}:{user.Email ?? user.UserName}";
        // otpauth-URI согласно RFC: otpauth://totp/{label}?secret={key}&issuer={issuer}.
        var uri = $"otpauth://totp/{HttpUtility.UrlEncode(label)}?secret={key}&issuer={HttpUtility.UrlEncode(issuer)}";

        return Ok(new EnrollResult(key!, uri, false));
    }

    [HttpPost("verify")]
    public async Task<IActionResult> Verify([FromBody] CodeInput input)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        if (string.IsNullOrWhiteSpace(input.Code))
            return BadRequest(new { error = "Не указан код подтверждения.", field = "code" });

        var provider = _users.Options.Tokens.AuthenticatorTokenProvider;
        var ok = await _users.VerifyTwoFactorTokenAsync(user, provider, input.Code.Trim());
        if (!ok)
            return BadRequest(new { error = "Неверный код. Попробуйте ещё раз.", field = "code" });

        await _users.SetTwoFactorEnabledAsync(user, true);
        return Ok(new { enabled = true });
    }

    [HttpPost("disable")]
    public async Task<IActionResult> Disable([FromBody] CodeInput input)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        if (!await _users.GetTwoFactorEnabledAsync(user))
            return Ok(new { enabled = false });

        // Защита от внезапного отключения 2FA: требуем текущий code из приложения.
        if (string.IsNullOrWhiteSpace(input.Code))
            return BadRequest(new { error = "Подтверди отключение текущим кодом из приложения.", field = "code" });

        var provider = _users.Options.Tokens.AuthenticatorTokenProvider;
        var ok = await _users.VerifyTwoFactorTokenAsync(user, provider, input.Code.Trim());
        if (!ok)
            return BadRequest(new { error = "Неверный код.", field = "code" });

        await _users.SetTwoFactorEnabledAsync(user, false);
        // Заодно сбрасываем authenticator-key, чтобы при повторном enroll выдался новый.
        await _users.ResetAuthenticatorKeyAsync(user);
        return Ok(new { enabled = false });
    }
}
