using System;
using System.Threading;
using System.Threading.Tasks;

namespace PCL.Core.Utils.Threading;

/// <summary>
/// 可重置的异步延时器。在指定延时后执行异步任务并等待下一次重置后重复该逻辑，指定延时未到达时重置将会重新开始计时。
/// <p>实例创建后并不会立即开始计时，而是等待第一次 <see cref="ResetAsync"/>
/// 调用。因此，若有特殊需求，请不要忘了创建实例后调用一次 <see cref="ResetAsync"/>。</p>
/// </summary>
public class AsyncDebounce(CancellationToken cancelToken = default) : IDisposable
{
    /// <summary>
    /// 执行延迟。
    /// </summary>
    public required TimeSpan Delay { get; init; }

    /// <summary>
    /// 异步任务实例。
    /// </summary>
    public required Func<Task> ScheduledTask { get; init; }

    /// <summary>
    /// 指示本次延迟任务是否已经完成。
    /// </summary>
    public bool IsCurrentTaskCompleted { get; private set; }

    /// <summary>
    /// 指示本次延迟任务是否正在运行。
    /// </summary>
    public bool IsCurrentTaskRunning => _currentTask is not null;

    private Task? _currentTask;
    private Task? _worker; // 跟踪最近一次 worker
    private CancellationTokenSource? _currentDelayCts;
    private readonly CancellationTokenSource _cts = CancellationTokenSource.CreateLinkedTokenSource(cancelToken);
    private readonly object _resetLock = new();

    /// <summary>
    /// 重置延时。
    /// </summary>
    public async Task ResetAsync()
    {
        IsCurrentTaskCompleted = false;

        CancellationTokenSource? capturedCts;
        Task? runningToAwait;

#pragma warning disable VSTHRD103 // 禁用检查 避免智障警告
        lock (_resetLock)
        {
            // 只取消，不在这里 Dispose
            try { _currentDelayCts?.Cancel(); }
            catch(ObjectDisposedException) { /* ignored */ }

            _currentDelayCts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
            capturedCts = _currentDelayCts;

            // 记录当前运行中的 ScheduledTask，稍后在锁外等待，避免重叠
            runningToAwait = _currentTask;

            _worker = Task.Run(async () =>
            {
                try { await Task.Delay(Delay, capturedCts.Token).ConfigureAwait(false); }
                catch (OperationCanceledException) { return; }
                finally
                {
                    // 仅由使用者自己释放，避免跨线程 Dispose 竞态
                    // 注意：不要在这里释放 _cancelToken
                    capturedCts.Dispose();
                }

                // 身份校验，确保自己仍是“当前”那一次
                if (
                    !ReferenceEquals(_currentDelayCts, capturedCts) ||
                    capturedCts.IsCancellationRequested ||
                    _cts.IsCancellationRequested
                ) return;

                if (_currentTask is not null) await _currentTask.ConfigureAwait(false);

                var task = ScheduledTask();
                lock (_resetLock) _currentTask = task;

                try
                {
                    await task.ConfigureAwait(false);
                }
                finally
                {
                    lock (_resetLock)
                    {
                        _currentTask = null;
                        IsCurrentTaskCompleted = true;
                    }
                }
            }, _cts.Token);
        }
#pragma warning restore VSTHRD103

        // 避免 ScheduledTask 并发
        if (runningToAwait is not null) await runningToAwait.ConfigureAwait(false);
    }

    public void Dispose()
    {
        _cts.Cancel();
        try { _worker?.Wait(); } catch { /* ignored */ }
        _currentDelayCts?.Dispose();
        _cts.Dispose();
        GC.SuppressFinalize(this);
    }
}
