using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace AxCopilot.Services.Agent;

/// <summary>작업 폴더 내 TODO/태스크 추적 도구.</summary>
public class TaskTrackerTool : IAgentTool
{
    public string Name => "task_tracker";
    public string Description =>
        "Track tasks/TODOs in the working folder. Actions: " +
        "'scan' — scan source files for TODO/FIXME/HACK/BUG comments; " +
        "'add' — add a task to .ax/tasks.json; " +
        "'list' — list tasks from .ax/tasks.json; " +
        "'done' — mark a task as completed.";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["action"] = new()
            {
                Type = "string",
                Description = "Action: scan, add, list, done",
                Enum = ["scan", "add", "list", "done"],
            },
            ["title"] = new()
            {
                Type = "string",
                Description = "Task title (for 'add')",
            },
            ["priority"] = new()
            {
                Type = "string",
                Description = "Priority: high, medium, low (for 'add', default: medium)",
            },
            ["id"] = new()
            {
                Type = "integer",
                Description = "Task ID (for 'done')",
            },
            ["extensions"] = new()
            {
                Type = "string",
                Description = "File extensions to scan, comma-separated (default: .cs,.py,.js,.ts,.java,.cpp,.c)",
            },
        },
        Required = ["action"],
    };

    private const string TaskFileName = ".ax/tasks.json";

    public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default)
    {
        var action = args.GetProperty("action").GetString() ?? "";

        try
        {
            return action switch
            {
                "scan" => Task.FromResult(ScanTodos(args, context)),
                "add" => Task.FromResult(AddTask(args, context)),
                "list" => Task.FromResult(ListTasks(context)),
                "done" => Task.FromResult(MarkDone(args, context)),
                _ => Task.FromResult(ToolResult.Fail($"Unknown action: {action}")),
            };
        }
        catch (Exception ex)
        {
            return Task.FromResult(ToolResult.Fail($"태스크 추적 오류: {ex.Message}"));
        }
    }

    private static ToolResult ScanTodos(JsonElement args, AgentContext context)
    {
        var extStr = args.TryGetProperty("extensions", out var e)
            ? e.GetString() ?? ".cs,.py,.js,.ts,.java,.cpp,.c"
            : ".cs,.py,.js,.ts,.java,.cpp,.c";
        var exts = new HashSet<string>(
            extStr.Split(',').Select(s => s.Trim().StartsWith('.') ? s.Trim() : "." + s.Trim()),
            StringComparer.OrdinalIgnoreCase);

        var patterns = new[] { "TODO", "FIXME", "HACK", "BUG", "XXX" };
        var results = new List<(string File, int Line, string Tag, string Text)>();
        var workDir = context.WorkFolder;

        if (!Directory.Exists(workDir))
            return ToolResult.Fail($"작업 폴더 없음: {workDir}");

        foreach (var file in Directory.EnumerateFiles(workDir, "*", SearchOption.AllDirectories))
        {
            if (!exts.Contains(Path.GetExtension(file))) continue;
            if (file.Contains("bin") || file.Contains("obj") || file.Contains("node_modules")) continue;

            var lineNum = 0;
            foreach (var line in File.ReadLines(file))
            {
                lineNum++;
                foreach (var pat in patterns)
                {
                    var idx = line.IndexOf(pat, StringComparison.OrdinalIgnoreCase);
                    if (idx >= 0)
                    {
                        var text = line[(idx + pat.Length)..].TrimStart(':', ' ');
                        if (text.Length > 100) text = text[..100] + "...";
                        var relPath = Path.GetRelativePath(workDir, file);
                        results.Add((relPath, lineNum, pat, text));
                        break;
                    }
                }
                if (results.Count >= 200) break;
            }
            if (results.Count >= 200) break;
        }

        if (results.Count == 0)
            return ToolResult.Ok("TODO/FIXME 코멘트가 발견되지 않았습니다.");

        var sb = new StringBuilder();
        sb.AppendLine($"발견된 TODO 코멘트: {results.Count}개");
        foreach (var (file, line, tag, text) in results)
            sb.AppendLine($"  [{tag}] {file}:{line} — {text}");
        return ToolResult.Ok(sb.ToString());
    }

    private static ToolResult AddTask(JsonElement args, AgentContext context)
    {
        var title = args.TryGetProperty("title", out var t) ? t.GetString() ?? "" : "";
        if (string.IsNullOrEmpty(title))
            return ToolResult.Fail("'title'이 필요합니다.");

        var priority = args.TryGetProperty("priority", out var p) ? p.GetString() ?? "medium" : "medium";
        var tasks = LoadTasks(context);

        var maxId = tasks.Count > 0 ? tasks.Max(t2 => t2.Id) : 0;
        tasks.Add(new TaskItem
        {
            Id = maxId + 1,
            Title = title,
            Priority = priority,
            Created = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
            Done = false,
        });

        SaveTasks(context, tasks);
        return ToolResult.Ok($"태스크 추가: #{maxId + 1} — {title} (우선순위: {priority})");
    }

    private static ToolResult ListTasks(AgentContext context)
    {
        var tasks = LoadTasks(context);
        if (tasks.Count == 0)
            return ToolResult.Ok("등록된 태스크가 없습니다.");

        var sb = new StringBuilder();
        sb.AppendLine($"태스크 목록 ({tasks.Count}개):");
        foreach (var task in tasks.OrderBy(t => t.Done).ThenByDescending(t => t.Priority == "high" ? 0 : t.Priority == "medium" ? 1 : 2))
        {
            var status = task.Done ? "✓" : "○";
            sb.AppendLine($"  {status} #{task.Id} [{task.Priority}] {task.Title} ({task.Created})");
        }
        return ToolResult.Ok(sb.ToString());
    }

    private static ToolResult MarkDone(JsonElement args, AgentContext context)
    {
        if (!args.TryGetProperty("id", out var idEl))
            return ToolResult.Fail("'id'가 필요합니다.");
        var id = idEl.GetInt32();

        var tasks = LoadTasks(context);
        var task = tasks.FirstOrDefault(t => t.Id == id);
        if (task == null) return ToolResult.Fail($"태스크 #{id}를 찾을 수 없습니다.");

        task.Done = true;
        SaveTasks(context, tasks);
        return ToolResult.Ok($"태스크 #{id} 완료: {task.Title}");
    }

    private static List<TaskItem> LoadTasks(AgentContext context)
    {
        var path = Path.Combine(context.WorkFolder, TaskFileName);
        if (!File.Exists(path)) return new List<TaskItem>();
        var json = File.ReadAllText(path);
        return JsonSerializer.Deserialize<List<TaskItem>>(json) ?? new List<TaskItem>();
    }

    private static void SaveTasks(AgentContext context, List<TaskItem> tasks)
    {
        var path = Path.Combine(context.WorkFolder, TaskFileName);
        var dir = Path.GetDirectoryName(path);
        if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
            Directory.CreateDirectory(dir);
        var json = JsonSerializer.Serialize(tasks, new JsonSerializerOptions { WriteIndented = true });
        File.WriteAllText(path, json);
    }

    private class TaskItem
    {
        [JsonPropertyName("id")] public int Id { get; set; }
        [JsonPropertyName("title")] public string Title { get; set; } = "";
        [JsonPropertyName("priority")] public string Priority { get; set; } = "medium";
        [JsonPropertyName("created")] public string Created { get; set; } = "";
        [JsonPropertyName("done")] public bool Done { get; set; }
    }
}
