using System.ComponentModel;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Lifecycle;
using ModelContextProtocol.Server;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.External;

public sealed record ResetFailedTaskResult(bool Reset, string TaskId, int Number);

[McpServerToolType]
public sealed class LifecycleMcpTools
{
    private readonly TaskRepository _tasks;
    private readonly TaskResetService _reset;
    private readonly ExternalMcpService _svc;
    private readonly InteractiveReviewSubmissionService _reviewSubmission;

    public LifecycleMcpTools(
        TaskRepository tasks, TaskResetService reset, ExternalMcpService svc,
        InteractiveReviewSubmissionService reviewSubmission)
    {
        _tasks = tasks;
        _reset = reset;
        _svc = svc;
        _reviewSubmission = reviewSubmission;
    }

    [McpServerTool, Description(
        "Discards a failed task's worktree (and all uncommitted work in it) and resets it to Idle for a fresh " +
        "run. Only tasks with Status=Failed are accepted; other statuses throw. Check failureReason from " +
        "get_task/batch_get_tasks first: for failureReason=\"max_turns\" the worktree's work is usually still " +
        "good and continue_task (resume the session, keep the worktree) is almost always the right call instead " +
        "— reach for this tool only for a real error, not a task that just ran out of turns.")]
    public async Task<ResetFailedTaskResult> ResetFailedTask(string taskId, CancellationToken cancellationToken)
    {
        taskId = await TaskIdResolver.ResolveAsync(_tasks, taskId, cancellationToken);
        var task = await _tasks.GetByIdAsync(taskId, cancellationToken)
            ?? throw new InvalidOperationException($"Task {taskId} not found.");
        if (task.Status != TaskStatus.Failed)
            throw new InvalidOperationException($"Task {taskId} is {task.Status}, not Failed. Only failed tasks can be reset via this tool.");

        await _reset.ResetAsync(taskId, cancellationToken);
        return new ResetFailedTaskResult(true, taskId, task.Number);
    }

    [McpServerTool, Description(
        "Submit a task into the review pipeline directly, without a headless agent run — the way a list " +
        "handler ('Let Claude handle it') submits its OWN handler task once its run is done, since " +
        "update_task_status doesn't reach WaitingForReview. Only Idle or Failed tasks are accepted; a " +
        "Running/Queued/WaitingForReview/WaitingForChildren task throws. For a worktree-less handler task this " +
        "stamps the review range's HandlerHeadCommit from the list's working dir HEAD (nothing to commit — the " +
        "handler already committed straight to the list's working dir); for a task with an active worktree it " +
        "commits any uncommitted changes there first. Either way the task then transitions to WaitingForReview " +
        "and its diff becomes reviewable via review_task/get_task_diff." + McpToolDocs.LeanTaskRef + McpToolDocs.TaskNumberHint)]
    public async Task<TaskRefDto> SubmitTaskForReview(string taskId, CancellationToken cancellationToken)
    {
        taskId = await TaskIdResolver.ResolveAsync(_tasks, taskId, cancellationToken);
        try
        {
            await _reviewSubmission.SubmitAsync(taskId, cancellationToken);
        }
        catch (KeyNotFoundException)
        {
            throw new InvalidOperationException($"Task {taskId} not found.");
        }

        return await _svc.GetTaskRefAsync(taskId, cancellationToken);
    }
}
