using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Meezi.Admin.API.Models;
using Meezi.Admin.API.Services;
using Meezi.Shared;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace Meezi.Admin.API.Controllers;

[ApiController]
[Route("api/admin/auth")]
public class AdminAuthController : ControllerBase
{
    private readonly IAdminAuthService _auth;
    private readonly IValidator<SendOtpRequest> _sendOtpValidator;
    private readonly IValidator<VerifyOtpRequest> _verifyOtpValidator;
    private readonly IValidator<RefreshTokenRequest> _refreshValidator;

    public AdminAuthController(
        IAdminAuthService auth,
        IValidator<SendOtpRequest> sendOtpValidator,
        IValidator<VerifyOtpRequest> verifyOtpValidator,
        IValidator<RefreshTokenRequest> refreshValidator)
    {
        _auth = auth;
        _sendOtpValidator = sendOtpValidator;
        _verifyOtpValidator = verifyOtpValidator;
        _refreshValidator = refreshValidator;
    }

    [HttpPost("send-otp")]
    public async Task<IActionResult> SendOtp([FromBody] SendOtpRequest request, CancellationToken cancellationToken)
    {
        var validation = await _sendOtpValidator.ValidateAsync(request, cancellationToken);
        if (!validation.IsValid)
            return BadRequest(ValidationError(validation));

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

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

    [HttpPost("verify-otp")]
    public async Task<IActionResult> VerifyOtp([FromBody] VerifyOtpRequest request, CancellationToken cancellationToken)
    {
        var validation = await _verifyOtpValidator.ValidateAsync(request, cancellationToken);
        if (!validation.IsValid)
            return BadRequest(ValidationError(validation));

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

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

    [HttpPost("login")]
    public async Task<IActionResult> LoginWithPassword(
        [FromBody] LoginWithPasswordRequest request,
        CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(request.Username) || string.IsNullOrWhiteSpace(request.Password))
            return BadRequest(ValidationError("Username and password are required."));

        var (success, data, code, message) = await _auth.LoginWithPasswordAsync(request, cancellationToken);
        if (!success)
            return ErrorResult(code!, message!);

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

    [HttpPut("password")]
    [Authorize]
    public async Task<IActionResult> ChangePassword(
        [FromBody] ChangePasswordRequest request,
        CancellationToken cancellationToken)
    {
        var adminId = User.FindFirstValue(JwtRegisteredClaimNames.Sub)
                   ?? User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrEmpty(adminId))
            return Unauthorized();

        var (success, code, message) = await _auth.ChangePasswordAsync(adminId, request, cancellationToken);
        if (!success)
            return ErrorResult(code!, message!);

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

    [HttpPost("refresh")]
    public async Task<IActionResult> Refresh([FromBody] RefreshTokenRequest request, CancellationToken cancellationToken)
    {
        var validation = await _refreshValidator.ValidateAsync(request, cancellationToken);
        if (!validation.IsValid)
            return BadRequest(ValidationError(validation));

        var (success, data, code, message) = await _auth.RefreshAsync(request, cancellationToken);
        if (!success)
            return ErrorResult(code!, message!);

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

    private static ApiResponse<object> ValidationError(FluentValidation.Results.ValidationResult validation)
    {
        var first = validation.Errors.First();
        return new ApiResponse<object>(false, null, new ApiError("VALIDATION_ERROR", first.ErrorMessage, first.PropertyName));
    }

    private static ApiResponse<object> ValidationError(string message) =>
        new(false, null, new ApiError("VALIDATION_ERROR", message));

    private IActionResult ErrorResult(string code, string message) =>
        code switch
        {
            "NOT_FOUND" => NotFound(new ApiResponse<object>(false, null, new ApiError(code, message))),
            "RATE_LIMITED" => StatusCode(429, new ApiResponse<object>(false, null, new ApiError(code, message))),
            _ => BadRequest(new ApiResponse<object>(false, null, new ApiError(code, message)))
        };
}
