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

namespace ClaudeDo.Worker.Lifecycle;

// Submits an interactively-worked task -- one whose worktree/working-dir commits were made by a
// human-in-the-loop ConPTY session rather than an autonomous run -- into the review pipeline.
// Shared by WorkerHub.SubmitTaskForReview (a UI ConPTY session) and the external
// submit_task_for_review MCP tool (a list handler submitting its OWN handler task): both must
// stamp/commit identically before TaskStateService.SubmitInteractiveForReviewAsync flips the status.
public sealed class InteractiveReviewSubmissionService
{
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly GitService _git;
    private readonly ITaskStateService _state;
    private readonly WorktreeManager? _worktreeManager;

    public InteractiveReviewSubmissionService(
        IDbContextFactory<ClaudeDoDbContext> dbFactory,
        GitService git,
        ITaskStateService state,
        WorktreeManager? worktreeManager = null)
    {
        _dbFactory = dbFactory;
        _git = git;
        _state = state;
        _worktreeManager = worktreeManager;
    }

    public async Task SubmitAsync(string taskId, CancellationToken ct)
    {
        await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
        var taskRepo = new TaskRepository(ctx);
        var task = await taskRepo.GetByIdAsync(taskId, ct)
            ?? throw new KeyNotFoundException();
        if (task.Status is TaskStatus.Running or TaskStatus.Queued)
            throw new InvalidOperationException("Can't submit a running or queued task — interrupt it first.");
        if (task.Status is TaskStatus.WaitingForReview or TaskStatus.WaitingForChildren)
            throw new InvalidOperationException("Task is already awaiting review.");
        // Check the status gate up front, before either mutation branch, so a Done or Cancelled
        // task can't get committed / have its HandlerHeadCommit stamped and then be rejected,
        // stranding the work on an orphaned branch.
        if (task.Status is not (TaskStatus.Idle or TaskStatus.Failed))
            throw new InvalidOperationException("Task must be Idle or Failed to submit for review.");

        var worktree = await new WorktreeRepository(ctx).GetByTaskIdAsync(taskId, ct);
        if (worktree is not null)
        {
            if (_worktreeManager is null)
                throw new InvalidOperationException("Worktree manager is not configured.");
            if (worktree.State is not (WorktreeState.Active or WorktreeState.Kept))
                throw new InvalidOperationException("This task has no active worktree to submit.");
            if (!Directory.Exists(worktree.Path))
                throw new InvalidOperationException("The task's worktree directory no longer exists.");

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

            var wtCtx = new WorktreeContext(worktree.Path, worktree.BranchName, worktree.BaseCommit);
            await _worktreeManager.CommitIfChangedAsync(wtCtx, task, list, ct);
        }
        else if (task.HandlerBaseCommit is { Length: > 0 })
        {
            // Worktree-less "list handler" host task (Mission Control's "Let Claude handle it"):
            // the handler commits its own changes straight to the list's working dir, so there
            // is nothing for us to commit here — just stamp the review range's head commit.
            var list = await new ListRepository(ctx).GetByIdAsync(task.ListId, ct)
                ?? throw new InvalidOperationException("Task list not found.");
            if (string.IsNullOrEmpty(list.WorkingDir) || !Directory.Exists(list.WorkingDir))
                throw new InvalidOperationException("The list's working directory no longer exists.");

            var headCommit = await _git.RevParseHeadAsync(list.WorkingDir, ct);
            await taskRepo.SetHandlerHeadCommitAsync(taskId, headCommit, ct);
        }
        else
        {
            throw new InvalidOperationException("This task has no active worktree to submit.");
        }

        var result = await _state.SubmitInteractiveForReviewAsync(taskId, DateTime.UtcNow, ct);
        if (!result.Ok)
            throw new InvalidOperationException(result.Reason ?? "Could not submit for review.");
    }
}
