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

namespace ClaudeDo.Worker.Lifecycle;

/// <summary>
/// Startup-only sweep: deletes attachment directories whose task no longer exists in the DB.
/// </summary>
public sealed class AttachmentOrphanRecovery : IHostedService
{
    public const string Phase = "attachments";

    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly AttachmentStore _store;
    private readonly HubBroadcaster _broadcaster;
    private readonly ILogger<AttachmentOrphanRecovery> _logger;

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

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        var taskIds = _store.EnumerateTaskIds();
        if (taskIds.Count == 0)
        {
            _logger.LogInformation("Attachment orphan recovery: no attachment directories found");
            await _broadcaster.OperationProgress(OperationProgressOpKeys.StartupRecovery, Phase, 0, 0);
            return;
        }

        await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken);
        var existingIds = (await ctx.Tasks
            .Where(t => taskIds.Contains(t.Id))
            .Select(t => t.Id)
            .ToListAsync(cancellationToken)).ToHashSet();

        var orphans = taskIds.Where(id => !existingIds.Contains(id)).ToList();
        foreach (var id in orphans)
            _store.DeleteTaskDir(id);

        if (orphans.Count > 0)
            _logger.LogWarning("Attachment orphan recovery: removed {Count} orphaned attachment director(ies)", orphans.Count);
        else
            _logger.LogInformation("Attachment orphan recovery: no orphaned attachment directories found");

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

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