using System;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using UnityEditor;

namespace AIRuntime.Editor
{
    public sealed partial class AIRuntimeKanbanSession
    {
        private string _cliTaskId;
        private string _cliAttemptId;

        public static void RecoverCompletionKickbackForCommandLine()
        {
            string title = Environment.GetEnvironmentVariable("AIRUNTIME_KANBAN_TASK_TITLE");
            if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("A task title is required.");
            string path = GetDefaultBoardPath();
            using (var backup = new FileStream(path + ".before-completion-kickback-" +
                DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fffffff") + ".json", FileMode.CreateNew))
            using (var input = File.OpenRead(path)) input.CopyTo(backup);
            var session = CreateServiceSession(path);
            try
            {
                if (session._board.SchedulerEnabled || session._activeAgentRuns.Count != 0 ||
                    session._board.Tasks.Any(t => HasLiveDispatchLease(t, DateTimeOffset.UtcNow)))
                    throw new InvalidOperationException("Recovery requires a paused board without live dispatches.");
                if (!session.TryAcquireSchedulerOwnership()) throw new IOException(session._statusMessage);
                session.RecoverCompletionKickback(title);
                if (!session.PersistSchedulerState()) throw new IOException("Recovery was not persisted.");
                UnityEngine.Debug.Log("[Kanban Recovery] Preserved the complete graph and queued the completed artifact for fresh Review.");
            }
            finally { UnityEngine.Object.DestroyImmediate(session); }
        }

        private void RecoverCompletionKickback(string title)
        {
            var source = _board.Tasks.Single(t => t.Title == title);
            var root = GetTaskRoot(source);
            if (root == null || root.WorkflowPhase != WorkflowPhasePlanning ||
                source.ReviewStatus != "Planning Required" ||
                !IsCompletionOnlyPlanningKickback(root.PlanningKickbackReason))
                throw new InvalidOperationException("The selected card is not a contradictory completion kickback.");
            if (TryGetUnresolvedSiblingOwnershipFailure(source, out string conflict))
                throw new InvalidOperationException(conflict);

            // Validate detached copies before accepting the operator-requested
            // full graph. Never reconcile an incomplete interrupted snapshot.
            var candidates = GetDirectChildren(root).Select(t =>
                UnityEngine.JsonUtility.FromJson<KanbanTask>(UnityEngine.JsonUtility.ToJson(t))).ToList();
            ResolveSiblingDependencies(candidates, root.Id);
            string error = ValidatePlanningChildren(root, candidates);
            if (!string.IsNullOrWhiteSpace(error)) throw new InvalidOperationException(error);
            foreach (var candidate in candidates)
            {
                var task = FindTask(candidate.Id);
                task.DependsOnTaskIds = candidate.DependsOnTaskIds;
                task.IsDecompositionDraft = false;
                task.ProposedInPlanningAttemptId = string.Empty;
                task.PlannedRevision = root.PlanRevision;
                TouchTask(task);
            }
            string reason = root.PlanningKickbackReason;
            root.PlanningAttemptId = string.Empty;
            root.NextAgentMessage = string.Empty;
            RequestPlanningRevision(source, reason, "Implementation");
            TouchTask(root);
            MarkDirty();
        }

        internal void PrepareCommandLineStep(string taskId)
        {
            // Acquire ownership before changing execution intent. Never take over
            // an enabled scheduler or another process's live attempt.
            _autoRunAgents = false;
            _agentStartsRemaining = 0;
            if (_board.SchedulerEnabled || _activeAgentRuns.Count != 0 ||
                _board.Tasks.Any(t => HasLiveDispatchLease(t, DateTimeOffset.UtcNow)))
                throw new InvalidOperationException("CLI requires a paused board with no live dispatch claims.");
            if (FindTask(taskId) == null)
                throw new ArgumentException("Task ID does not exist in the selected board: " + taskId);
            if (!TryAcquireSchedulerOwnership()) throw new IOException(_statusMessage);
            _cliTaskId = taskId;
            _cliAttemptId = null;
        }

        internal JObject PollCommandLineStep()
        {
            // Pump the existing reconciliation and completion path, with automatic
            // dispatch disabled. Only the explicitly selected card may start below.
            PumpAgentQueue();
            if (_boardWriteConflict || _schedulerPersistenceFailed || _board.SchedulerEnabled)
                return CommandLineSnapshot("Failed", "Board persistence failed or conflicted.");
            var task = FindTask(_cliTaskId);
            if (task == null) return CommandLineSnapshot("Failed", "Selected task disappeared.");
            if (_cliAttemptId != null)
            {
                if (task.Dispatch?.Id != _cliAttemptId)
                    return CommandLineSnapshot("Failed", "Selected dispatch was replaced.");
                if (task.Dispatch.Outcome == "Running") return null;
                // Force the completion through the same revision-checked save used
                // by the scheduler before reporting a terminal outcome.
                if (_isDirty && !PersistSchedulerState())
                    return CommandLineSnapshot("Failed", "Could not persist completion.");
                var saved = JObject.Parse(File.ReadAllText(_boardFilePath));
                var savedTask = saved["Tasks"].FirstOrDefault(t => (string)t["Id"] == _cliTaskId);
                if ((string)savedTask?["Dispatch"]?["Id"] != _cliAttemptId ||
                    (string)savedTask?["Dispatch"]?["Outcome"] != task.Dispatch.Outcome)
                    return CommandLineSnapshot("Failed", "Saved dispatch does not match completion.");
                string outcome = task.Dispatch.Outcome == "Completed" ? "Completed" :
                    task.Dispatch.Outcome == "Interrupted" ? "Interrupted" : "Failed";
                // Transport completion is not acceptance of a rejected review or
                // an implementation that the workflow marked as failed.
                if (!string.IsNullOrWhiteSpace(task.AgentError) || task.Status == TaskStatus.Failed || task.Status == TaskStatus.Crashed)
                    outcome = "Failed";
                return CommandLineSnapshot(outcome, task.BlockedReason);
            }
            if (EditorApplication.isCompiling || EditorApplication.isUpdating) return null;
            if (EditorUtility.scriptCompilationFailed)
                return CommandLineSnapshot("Failed", "Unity has script compilation errors.");
            _autoRunAgents = true;
            _agentStartsRemaining = 1;
            string selected = _cliTaskId;
            _cliTaskId = null;
            string gate;
            try { gate = GetSchedulerGate(); }
            finally { _cliTaskId = selected; _autoRunAgents = false; _agentStartsRemaining = 0; }
            if (gate != "Ready") return CommandLineSnapshot("Blocked", gate);
            string blocker = GetTaskSchedulingBlocker(task);
            if (blocker != "Eligible") return CommandLineSnapshot("Blocked", blocker);
            bool review = task.Status == TaskStatus.Review;
            string agent = ResolveWorkflowAgentId(task, review);
            bool started = review ? StartReviewTask(task, agent) : StartAgentTask(task, agent);
            if (!started) return CommandLineSnapshot("Blocked", _statusMessage);
            _cliAttemptId = task.Dispatch.Id;
            return null;
        }

        internal JObject StopCommandLineStep(string outcome, string reason)
        {
            _autoRunAgents = false;
            _agentStartsRemaining = 0;
            if (_activeAgentRuns.Count > 0) PrepareForAssemblyReload();
            if (_boardWriteConflict || _schedulerPersistenceFailed || _isDirty)
                return CommandLineSnapshot("Failed", reason + " Checkpoint persistence could not be confirmed.");
            return CommandLineSnapshot(outcome, reason);
        }

        private JObject CommandLineSnapshot(string outcome, string reason)
        {
            // Reconciliation can modify workflow state even when no dispatch is
            // eligible. Publish the revision after that state has been saved.
            if (_isDirty && !_boardWriteConflict && !_schedulerPersistenceFailed && !PersistSchedulerState())
            {
                outcome = "Failed";
                reason = (reason ?? string.Empty) + " Could not persist final board state.";
            }
            var task = FindTask(_cliTaskId);
            return new JObject
            {
                ["Outcome"] = outcome, ["Reason"] = reason ?? string.Empty,
                ["BoardPath"] = _boardFilePath, ["BoardRevision"] = _board.Revision,
                ["TaskId"] = _cliTaskId, ["TaskTitle"] = task?.Title,
                ["TaskStatus"] = task?.Status.ToString(), ["ReviewStatus"] = task?.ReviewStatus,
                ["AttemptId"] = _cliAttemptId, ["DispatchOutcome"] = task?.Dispatch?.Outcome,
                ["AgentError"] = task?.AgentError, ["NextAttemptAtUtc"] = task?.NextAttemptAtUtc,
                ["Scope"] = "One dispatch; Completed does not mean the card or game passed review."
            };
        }
    }
}
