using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using ServiceStack.Text;
using ServiceStack.Text.Pools;
using ServiceStack.Web;

namespace ServiceStack;

public static class HttpResultUtils
{
    /// <summary>
    /// Shortcut to get the ResponseDTO whether it's bare or inside a IHttpResult
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    public static object GetDto(this object response)
    {
        if (response == null) return null;
        return response is IHttpResult httpResult ? httpResult.Response : response;
    }

    /// <summary>
    /// Alias of AsDto
    /// </summary>
    public static object GetResponseDto(this object response)
    {
        return GetDto(response);
    }

    /// <summary>
    /// Shortcut to get the ResponseDTO whether it's bare or inside a IHttpResult
    /// </summary>
    /// <param name="response"></param>
    /// <returns>TResponse if found; otherwise null</returns>
    public static TResponse GetDto<TResponse>(this object response) where TResponse : class
    {
        if (response == null) return default(TResponse);
        return (response is IHttpResult httpResult ? httpResult.Response : response) as TResponse;
    }

    /// <summary>
    /// Alias of AsDto
    /// </summary>
    public static TResponse GetResponseDto<TResponse>(this object response) where TResponse : class
    {
        return GetDto<TResponse>(response);
    }

    public static object CreateErrorResponse(this IHttpError httpError)
    {
        if (httpError == null) return null;
        var errorDto = httpError.GetDto();
        if (errorDto != null) return errorDto;

        return new ErrorResponse
        {
            ResponseStatus = new ResponseStatus
            {
                ErrorCode = httpError.ErrorCode,
                Message = httpError.Message,
                StackTrace = httpError.StackTrace,
            }
        };
    }

    /// <summary>
    /// Whether the response is an IHttpError or Exception or ErrorResponse
    /// </summary>
    public static bool IsErrorResponse(this object response)
    {
        return response is IHttpError or Exception or ErrorResponse;
    }

    /// <summary>
    /// rangeHeader should be of the format "bytes=0-" or "bytes=0-12345" or "bytes=123-456"
    /// </summary>
    public static void ExtractHttpRanges(this string rangeHeader, long contentLength, out long rangeStart, out long rangeEnd)
    {
        if (string.IsNullOrEmpty(rangeHeader) || rangeHeader.LeftPart('=') != "bytes" 
                                              || rangeHeader.IndexOf('-') == -1)
            throw new HttpError(HttpStatusCode.RequestedRangeNotSatisfiable, "Unsupported HTTP Range");
            
        if (rangeHeader.IndexOf(',') >= 0)
            throw new HttpError(HttpStatusCode.RequestedRangeNotSatisfiable, "Multiple HTTP Ranges is not supported");

        var bytesRange = rangeHeader.RightPart("=");
        var rangeStartStr = bytesRange.LeftPart("-");
        var rangeEndStr = bytesRange.RightPart("-");
        if (string.IsNullOrEmpty(rangeStartStr))
        {
            var suffixLength = long.Parse(rangeEndStr);
            rangeEnd = contentLength - 1;
            rangeStart = rangeEnd - suffixLength;
        }
        else
        {
            rangeStart = long.Parse(rangeStartStr);
            rangeEnd = !string.IsNullOrEmpty(rangeEndStr)
                ? long.Parse(rangeEndStr) //the client requested a chunk
                : contentLength - 1;
        }

        if (rangeStart < 0)
            rangeStart = 0;
        if (rangeEnd > contentLength - 1)
            rangeEnd = contentLength - 1;
    }

    /// <summary>
    /// Adds 206 PartialContent Status, Content-Range and Content-Length headers
    /// </summary>
    public static void AddHttpRangeResponseHeaders(this IResponse response, long rangeStart, long rangeEnd, long contentLength)
    {
        response.AddHeader(HttpHeaders.ContentRange, $"bytes {rangeStart}-{rangeEnd}/{contentLength}");
        response.StatusCode = (int)HttpStatusCode.PartialContent;
        response.SetContentLength(rangeEnd - rangeStart + 1);
    }

}