using System.Security.Claims;
using Baya.Application.Models.ApiResult;
using Baya.Application.Models.Common;
using Baya.SharedKernel.Extensions;
using Baya.WebFramework.Filters;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Baya.WebFramework.BaseController;

public class BaseController : ControllerBase
{
    protected string UserName => User.Identity?.Name;
    protected int UserId => int.Parse(User.Identity.GetUserId());
    protected string UserEmail => User.Identity.FindFirstValue(ClaimTypes.Email);
    protected string UserRole => User.Identity.FindFirstValue(ClaimTypes.Role);

    protected string UserKey => User.FindFirstValue(ClaimTypes.UserData);

    protected IActionResult OperationResult<TModel>(OperationResult<TModel> result)
    {
        if (result is null)
            return new ServerErrorResult("Server Error");


        if (result.IsSuccess)
            return result.Result is bool ? Ok() : Ok(result.Result);

        if (result.IsNotFound)
        {

            AddErrors(result);

            var notFoundErrors = new ValidationProblemDetails(ModelState);

            return NotFound(notFoundErrors.Errors);
        }

        // 401/403 are written as the final envelope directly (mirroring the JWT-event responses) —
        // the result filters only translate Ok/NotFound/BadRequest.
        if (result.IsUnauthorized)
            return new JsonResult(new ApiResult(false, ApiResultStatusCode.UnAuthorized, FirstErrorMessage(result)))
                { StatusCode = StatusCodes.Status401Unauthorized };

        if (result.IsForbidden)
            return new JsonResult(new ApiResult(false, ApiResultStatusCode.Forbidden, FirstErrorMessage(result)))
                { StatusCode = StatusCodes.Status403Forbidden };

        if (result.IsConflict)
            return new JsonResult(new ApiResult(false, ApiResultStatusCode.Conflict, FirstErrorMessage(result)))
                { StatusCode = StatusCodes.Status409Conflict };

        // A coded failure (e.g. otp_locked) is written as the full envelope so the client sees the stable
        // `code` (+ optional `data`), rather than the bare ModelState errors of an ordinary 400.
        if (result.ErrorCode is not null)
            return new JsonResult(
                    new ApiResult<object>(false, ApiResultStatusCode.BadRequest, result.ErrorData, FirstErrorMessage(result))
                        { Code = result.ErrorCode })
                { StatusCode = StatusCodes.Status400BadRequest };

        AddErrors(result);
        
        var badRequestErrors = new ValidationProblemDetails(ModelState);

        return BadRequest(badRequestErrors.Errors);

    }
    
    private void AddErrors<TModel>(OperationResult<TModel> result)
    {
        foreach (var error in result.ErrorMessages)
        {
            ModelState.AddModelError(error.Key,error.Value);
        }
    }

    private static string FirstErrorMessage<TModel>(OperationResult<TModel> result)
        => result.ErrorMessages.Count > 0 ? result.ErrorMessages[0].Value : null;
}