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

namespace ClaudeDo.Worker.Lifecycle;

/// <summary>
/// Startup-only sweep that tries to re-attach blocked-by chains to their original
/// planning parent when the lineage was lost (parent_task_id cleared, parent
/// reverted to <c>PlanningPhase.None</c>) but the planning-sessions directory
/// still has the matching folder. Only restores lineage when the chain in the
/// parent's list is unambiguously the parent's — refuses to guess otherwise.
/// </summary>
public sealed class PlanningLineageRecovery : IHostedService
{
    public const string Phase = "planning-lineage";

    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly string _sessionsRoot;
    private readonly HubBroadcaster _broadcaster;
    private readonly ILogger<PlanningLineageRecovery> _logger;

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

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        if (!Directory.Exists(_sessionsRoot))
        {
            _logger.LogInformation("Planning lineage recovery: sessions directory missing, nothing to scan");
            await _broadcaster.OperationProgress(OperationProgressOpKeys.StartupRecovery, Phase, 0, 0);
            return;
        }

        var folders = Directory.GetDirectories(_sessionsRoot);
        if (folders.Length == 0)
        {
            _logger.LogInformation("Planning lineage recovery: no session folders");
            await _broadcaster.OperationProgress(OperationProgressOpKeys.StartupRecovery, Phase, 0, 0);
            return;
        }

        await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken);
        var repo = new TaskRepository(ctx);

        var restored = 0;
        foreach (var folder in folders)
        {
            var taskId = Path.GetFileName(folder);
            if (string.IsNullOrEmpty(taskId)) continue;

            var count = await repo.RestorePlanningLineageAsync(taskId, cancellationToken);
            if (count > 0)
            {
                _logger.LogWarning(
                    "Planning lineage recovery: re-attached {Count} child(ren) to parent {ParentId}",
                    count, taskId);
                restored++;
            }
        }

        if (restored == 0)
            _logger.LogInformation("Planning lineage recovery: no candidates");

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

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