using Foundation;

namespace Blahblah.Library.Network;

abstract class ContentTask<T, TResult> : NetworkTask
{
    public StepHandler<T>? Step { get; set; }

    protected virtual bool AllowDefaultResult => false;

    protected TaskCompletionSource<NetworkResult<TResult>>? taskSource;

    protected long expectedContentLength;

    protected ContentTask(string url, TaskCompletionSource<NetworkResult<TResult>> source, CancellationToken token) : base(url, token)
    {
        taskSource = source ?? throw new ArgumentNullException(nameof(source));
    }

    protected override void OnCancelled()
    {
        taskSource?.TrySetCanceled();
    }

    protected override void OnException(Exception ex)
    {
        taskSource?.TrySetResult(ex);
    }

    public override bool OnResponded(NSHttpUrlResponse response)
    {
        expectedContentLength = response.ExpectedContentLength;
        return true;
    }

    protected virtual T? Update(float progress)
    {
        return default;
    }

    public override void OnReceived(int length)
    {
        if (Step != null)
        {
            if (Data == null)
            {
                Step(0f, default);
            }
            else
            {
                float progress = (float)Data.Length / expectedContentLength;
                var update = Update(progress);
                Step(progress, update);
            }
        }
    }

    protected virtual TResult? Completed(NSHttpUrlResponse? response)
    {
        throw new NotImplementedException();
    }

    protected override void OnCompleted(NSHttpUrlResponse? response)
    {
        try
        {
            var result = Completed(response);
            if (result == null && !AllowDefaultResult)
            {
                throw new NullReferenceException("Result is null");
            }
            taskSource?.TrySetResult(result);
        }
        catch (Exception ex)
        {
            OnException(ex);
        }
    }

    protected override void Disposing()
    {
        if (taskSource?.Task.IsCanceled == false)
        {
            taskSource.TrySetCanceled();
            taskSource = null;
        }
    }
}