using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using GxMcp.Worker.Models;

namespace GxMcp.Worker.Services
{
    public class BatchService
    {
        private readonly KbService _kbService;
        private readonly WriteService _writeService;
        private readonly PatchService _patchService;
        private readonly ObjectService _objectService;
        private readonly List<BatchItem> _buffer = new List<BatchItem>();

        public BatchService(KbService kbService, WriteService writeService, PatchService patchService, ObjectService objectService)
        {
            _kbService = kbService;
            _writeService = writeService;
            _patchService = patchService;
            _objectService = objectService;
        }

        public string BatchEdit(string target, JArray changes)
        {
            try
            {
                var sw = System.Diagnostics.Stopwatch.StartNew();
                int count = 0;
                var results = new JArray();

                foreach (var change in changes)
                {
                    string part = change["part"]?.ToString() ?? "Source";
                    string mode = change["mode"]?.ToString() ?? "patch";
                    string content = change["content"]?.ToString();
                    string context = change["context"]?.ToString();
                    string operation = change["operation"]?.ToString() ?? "Replace";
                    int expectedCount = change["expectedCount"]?.ToObject<int?>() ?? 1;
                    bool dryRun = change["dryRun"]?.ToObject<bool?>() ?? false;
                    // Item 9 follow-up: forward replaceAll into the batch path so
                    // genexus_edit {targets:[...]} honours the same semantics as the
                    // single-target call. Without this, a per-change replaceAll is
                    // silently dropped and the call returns Ambiguous on N>1 matches.
                    bool replaceAll = change["replaceAll"]?.ToObject<bool?>() ?? false;

                    string result;
                    if (mode == "patch")
                    {
                        result = _patchService.ApplyPatch(target, part, operation, content, context, expectedCount, null, dryRun, verifyRollback: false, returnPostState: true, verbose: false, replaceAll: replaceAll);
                    }
                    else
                    {
                        result = _writeService.WriteObject(target, part, content);
                    }
                    
                    try {
                        results.Add(JObject.Parse(result));
                    } catch {
                        results.Add(new JObject { ["error"] = result });
                    }
                    count++;
                }

                return new JObject { 
                    ["status"] = "Success", 
                    ["count"] = count, 
                    ["results"] = results,
                    ["duration"] = sw.ElapsedMilliseconds 
                }.ToString();
            }
            catch (Exception ex)
            {
                return "{\"status\":\"Error\",\"message\":\"BatchEdit failed: " + CommandDispatcher.EscapeJsonString(ex.Message) + "\"}";
            }
        }

        public string ProcessBatch(string action, string name, string code)
        {
            if (action == "Add")
            {
                _buffer.Add(new BatchItem { Name = name, Code = code });
                return "{\"status\":\"Buffered\"}";
            }
            else if (action == "Commit")
            {
                int count = 0;
                foreach (var item in _buffer)
                {
                    _writeService.WriteObject(item.Name, "Source", item.Code);
                    count++;
                }
                _buffer.Clear();
                return "{\"status\":\"Committed\", \"count\":" + count + "}";
            }
            return McpResponse.Error("Unknown batch action", name, action, "Supported batch actions are Add and Commit.");
        }

        private class BatchItem { public string Name; public string Code; }

        public string MultiEdit(JArray items)
        {
            try
            {
                if (items == null || items.Count == 0)
                    return "{\"status\":\"Error\",\"message\":\"No items provided\"}";

                var sw = System.Diagnostics.Stopwatch.StartNew();
                var allResults = new JArray();
                int totalChanges = 0;

                foreach (var item in items)
                {
                    string name = item["name"]?.ToString();
                    var changes = item["changes"] as JArray;
                    if (string.IsNullOrEmpty(name) || changes == null) continue;

                    string result = BatchEdit(name, changes);
                    try {
                        var parsed = JObject.Parse(result);
                        parsed["object"] = name;
                        allResults.Add(parsed);
                        totalChanges += parsed["count"]?.ToObject<int>() ?? 0;
                    } catch {
                        allResults.Add(new JObject { ["object"] = name, ["error"] = result });
                    }
                }

                return new JObject {
                    ["status"] = "Success",
                    ["objectCount"] = items.Count,
                    ["totalChanges"] = totalChanges,
                    ["results"] = allResults,
                    ["duration"] = sw.ElapsedMilliseconds
                }.ToString();
            }
            catch (Exception ex)
            {
                return "{\"status\":\"Error\",\"message\":\"MultiEdit failed: " + CommandDispatcher.EscapeJsonString(ex.Message) + "\"}";
            }
        }
        /// <summary>
        /// Builds a paginated payload for lifecycle result items (errors list).
        /// Compatible with net48 (no Math.Clamp).
        /// </summary>
        public static JObject BuildResultPayload(IList<string> items, int page, int pageSize)
        {
            // Clamp inputs
            page = Math.Max(page, 1);
            pageSize = Math.Min(Math.Max(pageSize, 1), 200);

            int total = items == null ? 0 : items.Count;
            int skip = (page - 1) * pageSize;
            bool hasMore = skip + pageSize < total;

            var sliced = new JArray();
            if (items != null)
            {
                int end = Math.Min(skip + pageSize, total);
                for (int i = skip; i < end; i++)
                    sliced.Add(items[i]);
            }

            return new JObject
            {
                ["items"] = sliced,
                ["_meta"] = new JObject
                {
                    ["pagination"] = new JObject
                    {
                        ["total"] = total,
                        ["page"] = page,
                        ["page_size"] = pageSize,
                        ["has_more"] = hasMore
                    }
                }
            };
        }

        /// <summary>
        /// Builds a paginated payload for lifecycle status warnings.
        /// Compatible with net48 (no Math.Clamp).
        /// </summary>
        public static JObject BuildStatusPayload(IList<string> warnings, int page, int pageSize)
        {
            // Clamp inputs
            page = Math.Max(page, 1);
            pageSize = Math.Min(Math.Max(pageSize, 1), 200);

            int total = warnings == null ? 0 : warnings.Count;
            int skip = (page - 1) * pageSize;
            bool hasMore = skip + pageSize < total;

            var sliced = new JArray();
            if (warnings != null)
            {
                int end = Math.Min(skip + pageSize, total);
                for (int i = skip; i < end; i++)
                    sliced.Add(warnings[i]);
            }

            return new JObject
            {
                ["warnings"] = sliced,
                ["_meta"] = new JObject
                {
                    ["pagination"] = new JObject
                    {
                        ["total"] = total,
                        ["page"] = page,
                        ["page_size"] = pageSize,
                        ["has_more"] = hasMore
                    }
                }
            };
        }

        public string BatchRead(JArray items)
        {
            try
            {
                if (items == null || items.Count == 0) return "{\"status\":\"Error\",\"message\":\"No items provided\"}";

                var sw = System.Diagnostics.Stopwatch.StartNew();
                var results = new JArray();

                foreach (var item in items)
                {
                    string name = item["name"]?.ToString();
                    string part = item["part"]?.ToString() ?? "Source";
                    if (string.IsNullOrEmpty(name)) continue;

                    string readResult = _objectService.ReadObjectSource(name, part, null, null, "mcp");
                    try {
                        var parsed = JObject.Parse(readResult);
                        parsed["object"] = name;
                        parsed["part"] = part;
                        results.Add(parsed);
                    } catch {
                        results.Add(new JObject { ["object"] = name, ["part"] = part, ["error"] = readResult });
                    }
                }

                return new JObject {
                    ["status"] = "Success",
                    ["count"] = results.Count,
                    ["results"] = results,
                    ["duration"] = sw.ElapsedMilliseconds
                }.ToString();
            }
            catch (Exception ex)
            {
                return "{\"status\":\"Error\",\"message\":\"BatchRead failed: " + CommandDispatcher.EscapeJsonString(ex.Message) + "\"}";
            }
        }
    }
}
