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

namespace AxCopilot.Services.Agent;

/// <summary>
/// 성공적인 실행 흐름을 재사용 가능한 플레이북으로 저장/관리하는 도구.
/// .ax/playbooks/ 에 JSON 파일로 저장됩니다.
/// </summary>
public class PlaybookTool : IAgentTool
{
    public string Name => "playbook";

    public string Description =>
        "Save, list, describe, or delete execution playbooks. " +
        "A playbook captures a successful task workflow for reuse.\n" +
        "- action=\"save\": Save a new playbook (name, description, steps required)\n" +
        "- action=\"list\": List all saved playbooks\n" +
        "- action=\"describe\": Show full playbook details (id required)\n" +
        "- action=\"delete\": Delete a playbook (id required)";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["action"] = new()
            {
                Type = "string",
                Description = "save | list | describe | delete",
                Enum = ["save", "list", "describe", "delete"],
            },
            ["name"] = new()
            {
                Type = "string",
                Description = "Playbook name (for save)",
            },
            ["description"] = new()
            {
                Type = "string",
                Description = "What this playbook does (for save)",
            },
            ["steps"] = new()
            {
                Type = "array",
                Description = "List of step descriptions (for save)",
                Items = new() { Type = "string", Description = "Step description" },
            },
            ["tools_used"] = new()
            {
                Type = "array",
                Description = "List of tool names used in this workflow (for save)",
                Items = new() { Type = "string", Description = "Tool name" },
            },
            ["id"] = new()
            {
                Type = "integer",
                Description = "Playbook ID (for describe/delete)",
            },
        },
        Required = ["action"],
    };

    public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default)
    {
        if (!args.TryGetProperty("action", out var actionEl))
            return ToolResult.Fail("action이 필요합니다.");
        var action = actionEl.GetString() ?? "";

        if (string.IsNullOrEmpty(context.WorkFolder))
            return ToolResult.Fail("작업 폴더가 설정되지 않았습니다.");

        var playbookDir = Path.Combine(context.WorkFolder, ".ax", "playbooks");

        return action switch
        {
            "save" => await SavePlaybook(args, playbookDir, ct),
            "list" => ListPlaybooks(playbookDir),
            "describe" => await DescribePlaybook(args, playbookDir, ct),
            "delete" => DeletePlaybook(args, playbookDir),
            _ => ToolResult.Fail($"알 수 없는 액션: {action}. save | list | describe | delete 중 선택하세요."),
        };
    }

    private static async Task<ToolResult> SavePlaybook(JsonElement args, string playbookDir, CancellationToken ct)
    {
        var name = args.TryGetProperty("name", out var n) ? n.GetString() ?? "" : "";
        var description = args.TryGetProperty("description", out var d) ? d.GetString() ?? "" : "";

        if (string.IsNullOrWhiteSpace(name))
            return ToolResult.Fail("플레이북 name이 필요합니다.");
        if (string.IsNullOrWhiteSpace(description))
            return ToolResult.Fail("플레이북 description이 필요합니다.");

        // steps 파싱
        var steps = new List<string>();
        if (args.TryGetProperty("steps", out var stepsEl) && stepsEl.ValueKind == JsonValueKind.Array)
        {
            foreach (var step in stepsEl.EnumerateArray())
            {
                var s = step.GetString();
                if (!string.IsNullOrWhiteSpace(s))
                    steps.Add(s);
            }
        }

        if (steps.Count == 0)
            return ToolResult.Fail("최소 1개 이상의 step이 필요합니다.");

        // tools_used 파싱
        var toolsUsed = new List<string>();
        if (args.TryGetProperty("tools_used", out var toolsEl) && toolsEl.ValueKind == JsonValueKind.Array)
        {
            foreach (var tool in toolsEl.EnumerateArray())
            {
                var t = tool.GetString();
                if (!string.IsNullOrWhiteSpace(t))
                    toolsUsed.Add(t);
            }
        }

        try
        {
            Directory.CreateDirectory(playbookDir);

            // 다음 ID 결정
            var nextId = GetNextId(playbookDir);

            // 파일명에서 비안전 문자 제거
            var safeName = string.Join("_", name.Split(Path.GetInvalidFileNameChars()));
            var fileName = $"{nextId}_{safeName}.json";

            var playbook = new PlaybookData
            {
                Id = nextId,
                Name = name,
                Description = description,
                Steps = steps,
                ToolsUsed = toolsUsed,
                CreatedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
            };

            var json = JsonSerializer.Serialize(playbook, new JsonSerializerOptions
            {
                WriteIndented = true,
                Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
            });

            var filePath = Path.Combine(playbookDir, fileName);
            await File.WriteAllTextAsync(filePath, json, ct);

            return ToolResult.Ok($"플레이북 저장 완료: [{nextId}] {name}\n단계 수: {steps.Count}, 사용 도구: {toolsUsed.Count}개");
        }
        catch (Exception ex)
        {
            return ToolResult.Fail($"플레이북 저장 오류: {ex.Message}");
        }
    }

    private static ToolResult ListPlaybooks(string playbookDir)
    {
        if (!Directory.Exists(playbookDir))
            return ToolResult.Ok("저장된 플레이북이 없습니다.");

        var files = Directory.GetFiles(playbookDir, "*.json")
            .OrderBy(f => f)
            .ToList();

        if (files.Count == 0)
            return ToolResult.Ok("저장된 플레이북이 없습니다.");

        var sb = new StringBuilder();
        sb.AppendLine($"플레이북 {files.Count}개:");

        foreach (var file in files)
        {
            try
            {
                var json = File.ReadAllText(file);
                var pb = JsonSerializer.Deserialize<PlaybookData>(json);
                if (pb != null)
                    sb.AppendLine($"  [{pb.Id}] {pb.Name} — {pb.Description} ({pb.Steps.Count}단계, {pb.CreatedAt})");
            }
            catch (Exception)
            {
                sb.AppendLine($"  [?] {Path.GetFileName(file)} — 파싱 오류");
            }
        }

        return ToolResult.Ok(sb.ToString());
    }

    private static async Task<ToolResult> DescribePlaybook(JsonElement args, string playbookDir, CancellationToken ct)
    {
        if (!args.TryGetProperty("id", out var idEl))
            return ToolResult.Fail("플레이북 id가 필요합니다.");

        var id = idEl.ValueKind == JsonValueKind.Number ? idEl.GetInt32() : int.TryParse(idEl.GetString(), out var parsed) ? parsed : -1;
        var playbook = await FindPlaybookById(playbookDir, id, ct);

        if (playbook == null)
            return ToolResult.Fail($"ID {id}의 플레이북을 찾을 수 없습니다.");

        var sb = new StringBuilder();
        sb.AppendLine($"플레이북: {playbook.Name} (ID: {playbook.Id})");
        sb.AppendLine($"설명: {playbook.Description}");
        sb.AppendLine($"생성일: {playbook.CreatedAt}");
        sb.AppendLine();

        sb.AppendLine("단계:");
        for (var i = 0; i < playbook.Steps.Count; i++)
            sb.AppendLine($"  {i + 1}. {playbook.Steps[i]}");

        if (playbook.ToolsUsed.Count > 0)
        {
            sb.AppendLine();
            sb.AppendLine($"사용 도구: {string.Join(", ", playbook.ToolsUsed)}");
        }

        return ToolResult.Ok(sb.ToString());
    }

    private static ToolResult DeletePlaybook(JsonElement args, string playbookDir)
    {
        if (!args.TryGetProperty("id", out var idEl))
            return ToolResult.Fail("삭제할 플레이북 id가 필요합니다.");

        var id = idEl.ValueKind == JsonValueKind.Number ? idEl.GetInt32() : int.TryParse(idEl.GetString(), out var parsed) ? parsed : -1;

        if (!Directory.Exists(playbookDir))
            return ToolResult.Fail("저장된 플레이북이 없습니다.");

        // ID에 해당하는 파일 찾기
        var files = Directory.GetFiles(playbookDir, $"{id}_*.json");
        if (files.Length == 0)
        {
            // 파일 내부 ID로 검색
            foreach (var file in Directory.GetFiles(playbookDir, "*.json"))
            {
                try
                {
                    var json = File.ReadAllText(file);
                    var pb = JsonSerializer.Deserialize<PlaybookData>(json);
                    if (pb?.Id == id)
                    {
                        var name = pb.Name;
                        File.Delete(file);
                        return ToolResult.Ok($"플레이북 삭제됨: [{id}] {name}");
                    }
                }
                catch (Exception) { /* 무시 */ }
            }
            return ToolResult.Fail($"ID {id}의 플레이북을 찾을 수 없습니다.");
        }

        try
        {
            var fileName = Path.GetFileNameWithoutExtension(files[0]);
            File.Delete(files[0]);
            return ToolResult.Ok($"플레이북 삭제됨: {fileName}");
        }
        catch (Exception ex)
        {
            return ToolResult.Fail($"플레이북 삭제 오류: {ex.Message}");
        }
    }

    #region Helpers

    private static int GetNextId(string playbookDir)
    {
        if (!Directory.Exists(playbookDir))
            return 1;

        var maxId = 0;
        foreach (var file in Directory.GetFiles(playbookDir, "*.json"))
        {
            try
            {
                var json = File.ReadAllText(file);
                var pb = JsonSerializer.Deserialize<PlaybookData>(json);
                if (pb != null && pb.Id > maxId)
                    maxId = pb.Id;
            }
            catch (Exception) { /* 무시 */ }
        }
        return maxId + 1;
    }

    private static async Task<PlaybookData?> FindPlaybookById(string playbookDir, int id, CancellationToken ct)
    {
        if (!Directory.Exists(playbookDir))
            return null;

        foreach (var file in Directory.GetFiles(playbookDir, "*.json"))
        {
            try
            {
                var json = await File.ReadAllTextAsync(file, ct);
                var pb = JsonSerializer.Deserialize<PlaybookData>(json);
                if (pb?.Id == id)
                    return pb;
            }
            catch (Exception) { /* 무시 */ }
        }
        return null;
    }

    #endregion

    #region Models

    private class PlaybookData
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public string Description { get; set; } = "";
        public List<string> Steps { get; set; } = [];
        public List<string> ToolsUsed { get; set; } = [];
        public string CreatedAt { get; set; } = "";
    }

    #endregion
}
