using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace AIRuntime.Editor
{
    internal sealed class AIRuntimeAgentActivityExplanation
    {
        public string Action;
        public string Target;
        public string Purpose;
        public string Outcome;

        // Presentation uses only the store's redacted, bounded observations.
        // It never retrieves model reasoning or makes another provider call.
        internal static AIRuntimeAgentActivityExplanation Build(
            AIRuntimeAgentActivityEntry entry, IReadOnlyList<AIRuntimeAgentActivityEntry> history)
        {
            string tool = entry.ToolName ?? string.Empty;
            var result = new AIRuntimeAgentActivityExplanation { Action = entry.Summary };
            if (string.IsNullOrWhiteSpace(tool))
            {
                if (entry.Kind == AIRuntimeAgentActivityKind.RunFailed || entry.Kind == AIRuntimeAgentActivityKind.Error ||
                    entry.Kind == AIRuntimeAgentActivityKind.RunTimedOut || entry.Kind == AIRuntimeAgentActivityKind.RunCancelled)
                {
                    result.Target = entry.Objective;
                    string detail = entry.Detail ?? string.Empty;
                    result.Outcome = detail.IndexOf("429", StringComparison.Ordinal) >= 0 ||
                        detail.IndexOf("RateLimited", StringComparison.OrdinalIgnoreCase) >= 0
                        ? "The provider limited requests. This attempt stopped before the task was finished."
                        : string.IsNullOrWhiteSpace(detail) ? "No cause was recorded in this event." : detail;
                }
                return result;
            }
            var prior = (history ?? Array.Empty<AIRuntimeAgentActivityEntry>())
                .Where(e => e.Sequence < entry.Sequence && e.CorrelationId == entry.CorrelationId &&
                    e.SessionId == entry.SessionId).OrderByDescending(e => e.Sequence).ToArray();
            string arguments = entry.Kind == AIRuntimeAgentActivityKind.ToolStarted ? entry.Detail : null;
            if (arguments == null)
            {
                // AgentEvent has no call ID. Do not assign a result to one of
                // several overlapping calls of the same tool by guessing.
                var calls = prior.Where(e => e.ToolName == tool)
                    .TakeWhile(e => e.Kind != AIRuntimeAgentActivityKind.ToolCompleted &&
                        e.Kind != AIRuntimeAgentActivityKind.ToolFailed)
                    .Where(e => e.Kind == AIRuntimeAgentActivityKind.ToolStarted).ToArray();
                if (calls.Length == 1) arguments = calls[0].Detail;
            }
            string facts = entry.Detail ?? string.Empty;
            result.Target = ResolveTarget(facts, arguments, prior);
            switch (tool)
            {
                case "create_kanban_child_task":
                    result.Target = Value(facts, "ChildTaskTitle") ?? Value(arguments, "title", "Title") ?? "Unnamed proposed card";
                    result.Action = entry.Kind == AIRuntimeAgentActivityKind.ToolCompleted ? "Recorded plan for" : "Planning";
                    result.Purpose = "Define this card's work, acceptance criteria, owned outputs, and dependencies in the proposed plan.";
                    break;
                case "request_capability":
                    result.Action = "Request an unavailable capability";
                    result.Target = Value(arguments, "name") ?? entry.Objective;
                    result.Purpose = Value(arguments, "purpose") ?? "Describe the missing operation so execution can resume after it is supplied.";
                    break;                case "inspect_editor_component":
                case "get_serialized_properties":
                    result.Action = "Inspect component settings";
                    result.Purpose = "Check the component's current values before changing it or judging whether it meets the task requirements.";
                    break;
                case "set_serialized_property":
                    result.Action = "Change a component setting";
                    result.Purpose = "Apply a requested configuration change to the selected component.";
                    break;
                case "create_prefab":
                    result.Action = "Save a reusable prefab";
                    result.Purpose = "Persist the selected object as an asset that can be placed in a scene.";
                    break;
                case "save_editor_scene":
                    result.Action = "Save the scene";
                    result.Purpose = "Keep scene changes on disk so later work and review can use them.";
                    break;
                case "open_editor_scene":
                    result.Action = "Open the scene";
                    result.Purpose = "Load the scene needed for the current task.";
                    break;
                case "inspect_prefab":
                    result.Action = "Check the saved prefab";
                    result.Purpose = "Verify the asset's identity and saved prefab information.";
                    break;
                case "get_compilation_errors":
                    result.Action = "Check script compilation";
                    result.Target = "Unity project";
                    result.Purpose = "Find compiler errors that could prevent the scripts from running.";
                    break;
                case "save_kanban_checkpoint":
                    result.Action = "Save task progress";
                    result.Target = entry.Objective;
                    result.Purpose = "Record completed work and the next step so this card can resume without starting over.";
                    break;
                case "read_kanban_memory":
                    result.Action = "Read earlier task notes";
                    result.Target = entry.Objective;
                    result.Purpose = "Recover context from earlier attempts. These notes do not verify the current project state.";
                    break;
                case "control_play_mode":
                    result.Action = "Change Play Mode: " + (Value(arguments, "action") ?? "requested transition");
                    result.Target = "Unity Editor's loaded scenes";
                    result.Purpose = "Switch between editing and running the game for a runtime check.";
                    break;
                case "wait_for_editor_frames":
                    result.Action = "Wait " + (Value(arguments, "frameCount") ?? "for") + " editor frames";
                    result.Target = "Unity Editor";
                    result.Purpose = "Allow Unity to advance before the next observation.";
                    break;
                case "read_file":
                    result.Action = "Read a file";
                    result.Purpose = "Inspect existing content before implementing or reviewing the task.";
                    break;
                case "write_file":
                    result.Action = "Write a file";
                    result.Purpose = "Save content needed for the task's deliverable.";
                    break;
                case "find_game_objects":
                case "get_hierarchy_tree":
                case "inspect_game_object":
                    result.Action = "Inspect scene objects";
                    result.Purpose = "Find the relevant objects and check their identities, hierarchy and components.";
                    break;
                default:
                    result.Action = char.ToUpperInvariant(tool[0]) + tool.Substring(1).Replace('_', ' ');
                    result.Purpose = "Purpose not recorded for this action. See the task objective and technical details.";
                    break;
            }
            string component = Value(arguments, "componentType") ?? Value(facts, "ObjectType");
            if (!string.IsNullOrWhiteSpace(component)) result.Target += " · " + component.Replace("UnityEngine.", "");
            string property = Value(arguments, "propertyPath");
            if (property != null) result.Target += " · " + property;
            if (entry.Kind == AIRuntimeAgentActivityKind.ToolStarted)
                result.Outcome = "Requested — waiting for a result.";
            else if (entry.Kind == AIRuntimeAgentActivityKind.ToolFailed)
            {
                var owner = Regex.Match(facts, @"Canonical owner:\s*'(?<owner>[^']+)'");
                result.Outcome = facts.Contains("rejected unchanged artifacts")
                    ? "Skipped: these files have not changed in this attempt, so another forced import is not permitted."
                    : facts.Contains("[ArtifactOwnership]") || facts.StartsWith("Task ownership:", StringComparison.Ordinal)
                        ? "Blocked by task ownership. This card is not authorised to make this change." +
                            (owner.Success ? " The owning card is “" + owner.Groups["owner"].Value + "”." : " The task's write contract must be checked.")
                        : "Failed: " + (facts.Length > 360 ? facts.Substring(0, 360) + "… See technical details." : facts);
            }
            else if (entry.Kind == AIRuntimeAgentActivityKind.ToolCompleted)
            {
                bool rejected = string.Equals(Value(facts, "Succeeded"), "false", StringComparison.OrdinalIgnoreCase) ||
                    string.Equals(Value(facts, "ActiveRequestSucceeded"), "false", StringComparison.OrdinalIgnoreCase);
                result.Outcome = rejected ? "Returned a result, but the requested operation was not fully successful. " + facts
                    : tool == "get_compilation_errors" && Value(facts, "ErrorCount") != null
                        ? "Compiler reports " + Value(facts, "ErrorCount") + " error(s)."
                        : "Tool returned successfully. Task acceptance still requires review.";
            }
            if (tool == "create_kanban_child_task" && entry.Kind == AIRuntimeAgentActivityKind.ToolCompleted &&
                !string.Equals(Value(facts, "Succeeded"), "false", StringComparison.OrdinalIgnoreCase))
                result.Outcome = "Proposed card recorded. This is planning progress; implementation and review are still required.";            return result;
        }

        private static string ResolveTarget(string facts, string arguments, AIRuntimeAgentActivityEntry[] prior)
        {
            string path = Value(arguments, "assetPath", "scenePath", "path", "destinationPath") ??
                Value(facts, "AssetPath", "ScenePath", "Path");
            string name = Value(facts, "HierarchyPath", "GameObjectName", "ObjectName") ??
                Value(arguments, "hierarchyPath", "name");
            string id = Value(arguments, "gameObjectInstanceId", "sourceGameObjectInstanceId", "objectInstanceId", "componentInstanceId");
            if (name == null && id != null)
            {
                var known = prior.FirstOrDefault(e => Value(e.Detail, "GameObjectInstanceId", "ObjectInstanceId") == id &&
                    Value(e.Detail, "HierarchyPath", "GameObjectName", "ObjectName") != null);
                if (known != null)
                {
                    name = Value(known.Detail, "HierarchyPath", "GameObjectName", "ObjectName");
                    path = path ?? Value(known.Detail, "ScenePath", "AssetPath");
                }
            }
            if (name != null) return name + (path != null ? " — " + path : "");
            return path ?? (id != null ? "Unity object ID " + id + " (name not recorded yet)" : "Target not recorded in this event");
        }

        private static string Value(string detail, params string[] keys)
        {
            foreach (string key in keys)
            {
                var match = Regex.Match(detail ?? string.Empty, @"(?:^|,\s*)" + Regex.Escape(key) +
                    @"=(?<value>.*?)(?=,\s*[\w.]+=|,\s*…|$)", RegexOptions.IgnoreCase);
                if (match.Success && !string.IsNullOrWhiteSpace(match.Groups["value"].Value) &&
                    match.Groups["value"].Value != "null") return match.Groups["value"].Value.Trim();
            }
            return null;
        }
    }
}
