using ClaudeDo.Data;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Hub;
using Microsoft.EntityFrameworkCore;

namespace ClaudeDo.Worker.Lifecycle;

/// <summary>
/// Startup-only sweep: dequeues queued tasks whose parent is missing or no longer
/// in a planning phase. The child stays attached (<c>ParentTaskId</c> intact) but
/// drops out of the queue so it can't run against a dead chain. The user can
/// re-queue or detach manually.
/// </summary>
public sealed class OrphanRecovery : IHostedService
{
    public const string Phase = "orphaned-children";

    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly HubBroadcaster _broadcaster;
    private readonly ILogger<OrphanRecovery> _logger;

    public OrphanRecovery(
        IDbContextFactory<ClaudeDoDbContext> dbFactory,
        HubBroadcaster broadcaster,
        ILogger<OrphanRecovery> logger)
    {
        _dbFactory = dbFactory;
        _broadcaster = broadcaster;
        _logger = logger;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken);
        var repo = new TaskRepository(ctx);
        var dequeued = await repo.DequeueOrphanedChildrenAsync(cancellationToken);
        if (dequeued > 0)
            _logger.LogWarning("Orphan recovery: dequeued {Count} stuck child task(s)", dequeued);
        else
            _logger.LogInformation("Orphan recovery: no stuck child tasks found");

        await _broadcaster.OperationProgress(OperationProgressOpKeys.StartupRecovery, Phase, 0, 0);
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
