using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Runner;
using ClaudeDo.Worker.State;
using Microsoft.EntityFrameworkCore;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Lifecycle;

public sealed class TaskResetService
{
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly WorktreeManager _wtManager;
    private readonly HubBroadcaster _broadcaster;
    private readonly ITaskStateService _state;
    private readonly ILogger<TaskResetService> _logger;

    public TaskResetService(
        IDbContextFactory<ClaudeDoDbContext> dbFactory,
        WorktreeManager wtManager,
        HubBroadcaster broadcaster,
        ITaskStateService state,
        ILogger<TaskResetService> logger)
    {
        _dbFactory = dbFactory;
        _wtManager = wtManager;
        _broadcaster = broadcaster;
        _state = state;
        _logger = logger;
    }

    public async Task ResetAsync(string taskId, CancellationToken ct)
    {
        TaskEntity task;
        ListEntity list;
        WorktreeEntity? wt;

        using (var ctx = _dbFactory.CreateDbContext())
        {
            task = await new TaskRepository(ctx).GetByIdAsync(taskId, ct)
                ?? throw new KeyNotFoundException($"Task '{taskId}' not found.");

            if (task.Status == TaskStatus.Running)
                throw new InvalidOperationException("Cannot reset a running task. Cancel it first.");

            list = await new ListRepository(ctx).GetByIdAsync(task.ListId, ct)
                ?? throw new InvalidOperationException("List not found.");

            wt = await new WorktreeRepository(ctx).GetByTaskIdAsync(taskId, ct);
        }

        bool worktreeChanged = false;
        if (wt is not null && wt.State == WorktreeState.Active && list.WorkingDir is not null)
        {
            await _wtManager.DiscardAsync(wt, list.WorkingDir, ct);
            await _broadcaster.WorkerLog($"Discarded worktree for #{task.Number} \"{task.Title}\"", WorkerLogLevel.Warn, DateTime.UtcNow);
            worktreeChanged = true;
        }

        await _state.ResetToIdleAsync(taskId, ct);

        await _broadcaster.TaskUpdated(taskId);
        if (worktreeChanged)
            await _broadcaster.WorktreeUpdated(taskId);

        _logger.LogInformation("Reset task {TaskId} to Idle (worktree discarded: {Discarded})", taskId, worktreeChanged);
        await _broadcaster.WorkerLog($"Reset #{task.Number} \"{task.Title}\"", WorkerLogLevel.Warn, DateTime.UtcNow);
    }
}
