using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Meezi.API.Models.Auth;
using Meezi.API.Services;
using Meezi.Shared;

namespace Meezi.API.Controllers;

[ApiController]
[AllowAnonymous]
[Route("api/auth/customer")]
public class ConsumerAuthController : ControllerBase
{
    private readonly IConsumerAuthService _auth;
    private readonly IValidator<SendOtpRequest> _sendValidator;
    private readonly IValidator<VerifyOtpRequest> _verifyValidator;

    public ConsumerAuthController(
        IConsumerAuthService auth,
        IValidator<SendOtpRequest> sendValidator,
        IValidator<VerifyOtpRequest> verifyValidator)
    {
        _auth = auth;
        _sendValidator = sendValidator;
        _verifyValidator = verifyValidator;
    }

    [HttpPost("send-otp")]
    [EnableRateLimiting("auth-otp")]
    public async Task<IActionResult> SendOtp([FromBody] SendOtpRequest request, CancellationToken ct)
    {
        var validation = await _sendValidator.ValidateAsync(request, ct);
        if (!validation.IsValid) return ValidationBadRequest(validation);

        var (success, data, code, message) = await _auth.SendOtpAsync(request, ct);
        if (!success)
            return AuthError(code, message);

        return Ok(new ApiResponse<SendOtpResponse>(true, data));
    }

    [HttpPost("verify-otp")]
    [EnableRateLimiting("auth-otp")]
    public async Task<IActionResult> VerifyOtp([FromBody] VerifyOtpRequest request, CancellationToken ct)
    {
        var validation = await _verifyValidator.ValidateAsync(request, ct);
        if (!validation.IsValid) return ValidationBadRequest(validation);

        var (success, data, code, message) = await _auth.VerifyOtpAsync(request, ct);
        if (!success)
            return AuthError(code, message);

        return Ok(new ApiResponse<object>(true, data));
    }

    [HttpPost("refresh")]
    public async Task<IActionResult> Refresh([FromBody] RefreshTokenRequest request, CancellationToken ct)
    {
        var (success, data, code, message) = await _auth.RefreshAsync(request, ct);
        if (!success)
            return AuthError(code, message);

        return Ok(new ApiResponse<object>(true, data));
    }

    private IActionResult ValidationBadRequest(FluentValidation.Results.ValidationResult validation)
    {
        var first = validation.Errors[0];
        return BadRequest(new ApiResponse<object>(false, null,
            new ApiError("VALIDATION_ERROR", first.ErrorMessage, first.PropertyName)));
    }

    private IActionResult AuthError(string? code, string? message) =>
        code switch
        {
            "RATE_LIMITED" => StatusCode(429, new ApiResponse<object>(false, null, new ApiError(code, message ?? "Rate limited."))),
            "NOT_FOUND" => NotFound(new ApiResponse<object>(false, null, new ApiError(code, message ?? "Not found."))),
            _ => BadRequest(new ApiResponse<object>(false, null, new ApiError(code ?? "AUTH_FAILED", message ?? "Authentication failed.")))
        };
}
