using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Net;

namespace BankSampahApp.Filters;

/// <summary>
/// Global exception filter untuk menangani error secara terpusat
/// </summary>
public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;
    private readonly IWebHostEnvironment _environment;

    /// <summary>
    /// Constructor dengan dependency injection
    /// </summary>
    /// <param name="logger">Logger untuk logging</param>
    /// <param name="environment">Environment information</param>
    public GlobalExceptionFilter(
        ILogger<GlobalExceptionFilter> logger,
        IWebHostEnvironment environment)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _environment = environment ?? throw new ArgumentNullException(nameof(environment));
    }

    /// <summary>
    /// Handle exception yang terjadi di aplikasi
    /// </summary>
    /// <param name="context">Exception context</param>
    public void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        var requestId = context.HttpContext.TraceIdentifier;

        // Log the exception with details
        _logger.LogError(exception,
            "Unhandled exception occurred. RequestId: {RequestId}, Path: {Path}, Method: {Method}, User: {User}",
            requestId,
            context.HttpContext.Request.Path,
            context.HttpContext.Request.Method,
            context.HttpContext.User?.Identity?.Name ?? "Anonymous");

        // Determine response based on request type
        if (IsApiRequest(context.HttpContext.Request))
        {
            HandleApiException(context, exception, requestId);
        }
        else
        {
            HandleWebException(context, exception, requestId);
        }
    }

    /// <summary>
    /// Handle exception untuk API requests
    /// </summary>
    /// <param name="context">Exception context</param>
    /// <param name="exception">Exception yang terjadi</param>
    /// <param name="requestId">Request ID</param>
    private void HandleApiException(ExceptionContext context, Exception exception, string requestId)
    {
        var statusCode = GetStatusCodeFromException(exception);
        var includeDetails = _environment.IsDevelopment();

        var response = new
        {
            Error = new
            {
                Message = includeDetails ? exception.Message : "An error occurred while processing your request.",
                RequestId = requestId,
                Details = includeDetails ? exception.ToString() : null,
                Type = exception.GetType().Name,
                Timestamp = DateTime.UtcNow
            }
        };

        context.Result = new JsonResult(response)
        {
            StatusCode = (int)statusCode
        };

        context.ExceptionHandled = true;
    }

    /// <summary>
    /// Handle exception untuk Web requests
    /// </summary>
    /// <param name="context">Exception context</param>
    /// <param name="exception">Exception yang terjadi</param>
    /// <param name="requestId">Request ID</param>
    private void HandleWebException(ExceptionContext context, Exception exception, string requestId)
    {
        var statusCode = GetStatusCodeFromException(exception);

        // Set status code
        context.HttpContext.Response.StatusCode = (int)statusCode;

        // Store exception details untuk error page
        context.HttpContext.Items["Exception"] = exception;
        context.HttpContext.Items["RequestId"] = requestId;

        // Redirect to error page
        var routeData = new RouteData();
        routeData.Values["controller"] = "Home";
        routeData.Values["action"] = "Error";

        context.Result = new RedirectToRouteResult(routeData);
        context.ExceptionHandled = true;
    }

    /// <summary>
    /// Determine HTTP status code based on exception type
    /// </summary>
    /// <param name="exception">Exception</param>
    /// <returns>HTTP status code</returns>
    private static HttpStatusCode GetStatusCodeFromException(Exception exception)
    {
        return exception switch
        {
            ArgumentNullException => HttpStatusCode.BadRequest,
            ArgumentException => HttpStatusCode.BadRequest,
            UnauthorizedAccessException => HttpStatusCode.Unauthorized,
            NotImplementedException => HttpStatusCode.NotImplemented,
            TimeoutException => HttpStatusCode.RequestTimeout,
            _ => HttpStatusCode.InternalServerError
        };
    }

    /// <summary>
    /// Check if request is API request
    /// </summary>
    /// <param name="request">HTTP request</param>
    /// <returns>True if API request</returns>
    private static bool IsApiRequest(HttpRequest request)
    {
        // Check if request accepts JSON or has API path
        return request.Headers["Accept"].ToString().Contains("application/json") ||
               request.Path.StartsWithSegments("/api") ||
               request.Path.StartsWithSegments("/health");
    }
}