using System.Net.Http;
using System.Text;
using System.Text.Json;

namespace AxCopilot.Services.Agent;

/// <summary>
/// 로컬/사내 HTTP API 호출 도구.
/// GET/POST/PUT/DELETE 요청, JSON 파싱, 헤더 설정을 지원합니다.
/// </summary>
public class HttpTool : IAgentTool
{
    public string Name => "http_tool";
    public string Description =>
        "Make HTTP requests to local or internal APIs. " +
        "Supports GET, POST, PUT, DELETE methods with JSON body and custom headers. " +
        "Only allows localhost and internal network addresses (security restriction). " +
        "Use this for testing APIs, fetching data from internal services, or webhooks.";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["method"] = new()
            {
                Type = "string",
                Description = "HTTP method",
                Enum = ["GET", "POST", "PUT", "DELETE", "PATCH"],
            },
            ["url"] = new()
            {
                Type = "string",
                Description = "Request URL (localhost or internal network only)",
            },
            ["body"] = new()
            {
                Type = "string",
                Description = "Request body (JSON string, for POST/PUT/PATCH)",
            },
            ["headers"] = new()
            {
                Type = "string",
                Description = "Custom headers as JSON object, e.g. {\"Authorization\": \"Bearer token\"}",
            },
            ["timeout"] = new()
            {
                Type = "string",
                Description = "Request timeout in seconds (default: 30, max: 120)",
            },
        },
        Required = ["method", "url"],
    };

    private static readonly HttpClient _client = new() { Timeout = TimeSpan.FromSeconds(30) };

    public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default)
    {
        var method  = args.GetProperty("method").GetString()?.ToUpperInvariant() ?? "GET";
        var url     = args.GetProperty("url").GetString() ?? "";
        var body    = args.TryGetProperty("body", out var b) ? b.GetString() ?? "" : "";
        var headers = args.TryGetProperty("headers", out var h) ? h.GetString() ?? "" : "";
        var timeout = args.TryGetProperty("timeout", out var t) ? int.TryParse(t.GetString(), out var ts) ? Math.Min(ts, 120) : 30 : 30;

        // 네트워크 모드별 접속 제어
        var internalMode = context.Settings?.InternalModeEnabled ?? true;
        if (internalMode && !IsAllowedHost(url))
            return ToolResult.Fail("사내 모드에서는 외부 URL에 접속할 수 없습니다. 설정에서 사외 모드를 활성화하거나, localhost·사내 네트워크 주소만 사용하세요.");

        try
        {
            var httpMethod = new HttpMethod(method);
            using var request = new HttpRequestMessage(httpMethod, url);

            // 헤더 설정
            if (!string.IsNullOrEmpty(headers))
            {
                using var headerDoc = JsonDocument.Parse(headers);
                foreach (var prop in headerDoc.RootElement.EnumerateObject())
                    request.Headers.TryAddWithoutValidation(prop.Name, prop.Value.GetString());
            }

            // 본문 설정
            if (!string.IsNullOrEmpty(body) && method is "POST" or "PUT" or "PATCH")
                request.Content = new StringContent(body, Encoding.UTF8, "application/json");

            using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
            cts.CancelAfter(TimeSpan.FromSeconds(timeout));

            using var response = await _client.SendAsync(request, cts.Token);

            var statusCode = (int)response.StatusCode;
            var responseBody = await response.Content.ReadAsStringAsync(cts.Token);

            // 응답 포맷팅
            var sb = new StringBuilder();
            sb.AppendLine($"HTTP {statusCode} {response.ReasonPhrase}");
            sb.AppendLine($"Content-Type: {response.Content.Headers.ContentType}");
            sb.AppendLine();

            // JSON이면 포맷
            if (response.Content.Headers.ContentType?.MediaType?.Contains("json") == true)
            {
                try
                {
                    using var doc = JsonDocument.Parse(responseBody);
                    responseBody = JsonSerializer.Serialize(doc.RootElement, new JsonSerializerOptions { WriteIndented = true });
                }
                catch (Exception) { /* not valid JSON, keep raw */ }
            }

            if (responseBody.Length > 8000)
                responseBody = responseBody[..8000] + $"\n... (truncated, total {responseBody.Length} chars)";

            sb.Append(responseBody);
            return ToolResult.Ok(sb.ToString());
        }
        catch (TaskCanceledException)
        {
            return ToolResult.Fail($"요청 시간 초과 ({timeout}초)");
        }
        catch (Exception ex)
        {
            return ToolResult.Fail($"HTTP 요청 실패: {ex.Message}");
        }
    }

    private static bool IsAllowedHost(string url)
    {
        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false;
        var host = uri.Host;

        if (host is "localhost" or "127.0.0.1" or "::1") return true;

        // 사내 네트워크 대역
        if (System.Net.IPAddress.TryParse(host, out var ip))
        {
            var bytes = ip.GetAddressBytes();
            if (bytes.Length == 4)
            {
                if (bytes[0] == 10) return true;                                    // 10.0.0.0/8
                if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true; // 172.16.0.0/12
                if (bytes[0] == 192 && bytes[1] == 168) return true;                // 192.168.0.0/16
            }
        }

        return false;
    }
}
