using System.Diagnostics;

namespace RobotNet.ScriptManager.Services;

public abstract class LoopService(int interval) : IHostedService
{
    private readonly TimeSpan Interval = TimeSpan.FromMilliseconds(interval);
    private readonly CancellationTokenSource cancellationTokenSource = new();
    private readonly Stopwatch stopwatch = new();
    private Timer? timer;

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await BeforExecuteAsync(cancellationToken);
        stopwatch.Start();
        timer = new Timer(Callback, cancellationTokenSource, 0, Timeout.Infinite);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        cancellationTokenSource.Cancel();
        timer?.Dispose();
        await AfterExecuteAsync(cancellationToken);
    }
    protected virtual Task BeforExecuteAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    protected abstract void Execute(CancellationToken stoppingToken);
    protected virtual Task AfterExecuteAsync(CancellationToken cancellationToken) => Task.CompletedTask;

    private void Callback(object? state)
    {
        if (state is not CancellationTokenSource cts || cts.IsCancellationRequested) return;

        Execute(cts.Token);

        if (!cts.IsCancellationRequested)
        {
            long nextTrigger = Interval.Ticks - (stopwatch.ElapsedTicks % Interval.Ticks);
            if (nextTrigger <= 0) nextTrigger = Interval.Ticks;

            int nextIntervalMs = (int)(nextTrigger / TimeSpan.TicksPerMillisecond);

            timer?.Change(nextIntervalMs, Timeout.Infinite);
        }
    }
}
