using System.Globalization;
using System.Net.Http.Headers;
using Ehwrj.Core.Services;

namespace Ehwrj.Tools.Capture;

internal static class Program
{
    private static readonly Endpoint[] Endpoints =
    {
        new("map_info.json", "map_info.json", Required: true),
        new("map_obj.json", "map_obj.json", Required: true),
        new("map.img", "map.img", Required: true),
        new("state", "state.json", Required: false),
        new("hudmsg", "hudmsg.json", Required: false),
        new("gamechat", "gamechat.json", Required: false)
    };

    private static async Task<int> Main(string[] args)
    {
        var options = CaptureOptions.Parse(args);
        if (options.ShowHelp)
        {
            CaptureOptions.PrintHelp();
            return 0;
        }

        if (!string.IsNullOrWhiteSpace(options.ValidateDirectory))
        {
            return WriteCaptureReport(options.ValidateDirectory);
        }

        var captureDir = options.OutputDirectory ?? Path.Combine(
            "captures",
            DateTimeOffset.Now.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture));

        try
        {
            LoopbackGuard.EnsureLoopbackHttp(options.BaseAddress, "--base-url");
        }
        catch (ArgumentException ex)
        {
            Console.Error.WriteLine($"error: {ex.Message}");
            return 1;
        }

        Directory.CreateDirectory(captureDir);

        using var client = new HttpClient
        {
            BaseAddress = options.BaseAddress,
            Timeout = TimeSpan.FromMilliseconds(options.TimeoutMs)
        };
        client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("EhwrjCapture", "1.0"));

        Console.WriteLine($"Capturing War Thunder local API from {client.BaseAddress}");
        Console.WriteLine($"Output: {Path.GetFullPath(captureDir)}");

        var failures = 0;
        foreach (var endpoint in Endpoints)
        {
            var ok = await CaptureEndpointAsync(client, endpoint, captureDir).ConfigureAwait(false);
            if (!ok && endpoint.Required)
            {
                failures++;
            }
        }

        File.WriteAllText(
            Path.Combine(captureDir, "README.txt"),
            $"""
            Ehwrj local API capture
            Captured at: {DateTimeOffset.Now:O}
            Source: {client.BaseAddress}

            Files:
            - map_info.json
            - map_obj.json
            - map.img
            - state.json, hudmsg.json, gamechat.json when available

            Use with:
              scripts/run-local-api-stub.sh 8111 {Path.GetFullPath(captureDir)}
            """);

        WriteCaptureReport(captureDir);

        if (failures > 0)
        {
            Console.Error.WriteLine($"{failures} required endpoint(s) failed.");
            return 1;
        }

        Console.WriteLine("Capture complete.");
        return 0;
    }

    private static int WriteCaptureReport(string captureDir)
    {
        if (!Directory.Exists(captureDir))
        {
            Console.Error.WriteLine($"error: capture directory not found: {captureDir}");
            return 1;
        }

        var report = CaptureFixtureAnalyzer.AnalyzeDirectory(captureDir);
        var reportPath = Path.Combine(report.Directory, "capture-report.txt");
        File.WriteAllText(reportPath, report.ToText());
        Console.WriteLine($"report: {reportPath}");

        if (report.Warnings.Count > 0)
        {
            Console.WriteLine($"{report.Warnings.Count} capture warning(s); see capture-report.txt.");
        }

        return report.HasRequiredFiles ? 0 : 1;
    }

    private static async Task<bool> CaptureEndpointAsync(HttpClient client, Endpoint endpoint, string captureDir)
    {
        try
        {
            using var response = await client.GetAsync(endpoint.Path).ConfigureAwait(false);
            if (!response.IsSuccessStatusCode)
            {
                var level = endpoint.Required ? "error" : "skip";
                Console.WriteLine($"{level}: {endpoint.Path} returned {(int)response.StatusCode}");
                return false;
            }

            var bytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
            var outputPath = Path.Combine(captureDir, endpoint.FileName);
            await File.WriteAllBytesAsync(outputPath, bytes).ConfigureAwait(false);
            Console.WriteLine($"saved: {endpoint.Path} -> {outputPath} ({bytes.Length} bytes)");
            return true;
        }
        catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
        {
            var level = endpoint.Required ? "error" : "skip";
            Console.WriteLine($"{level}: {endpoint.Path} failed: {ex.Message}");
            return false;
        }
    }
}

internal sealed record Endpoint(string Path, string FileName, bool Required);

internal sealed record CaptureOptions(Uri BaseAddress, string? OutputDirectory, string? ValidateDirectory, int TimeoutMs, bool ShowHelp)
{
    public static CaptureOptions Parse(IReadOnlyList<string> args)
    {
        var baseAddress = new Uri("http://127.0.0.1:8111/");
        string? output = null;
        string? validate = null;
        var timeoutMs = 1200;

        for (var i = 0; i < args.Count; i++)
        {
            switch (args[i])
            {
                case "-h":
                case "--help":
                    return new CaptureOptions(baseAddress, output, validate, timeoutMs, ShowHelp: true);
                case "--base-url" when i + 1 < args.Count && Uri.TryCreate(args[++i], UriKind.Absolute, out var uri):
                    baseAddress = uri;
                    break;
                case "--out" when i + 1 < args.Count:
                    output = args[++i];
                    break;
                case "--validate" when i + 1 < args.Count:
                    validate = args[++i];
                    break;
                case "--timeout-ms" when i + 1 < args.Count && int.TryParse(args[++i], NumberStyles.None, CultureInfo.InvariantCulture, out var parsed):
                    timeoutMs = Math.Clamp(parsed, 200, 10_000);
                    break;
                default:
                    Console.Error.WriteLine($"Ignoring unknown argument: {args[i]}");
                    break;
            }
        }

        return new CaptureOptions(EnsureTrailingSlash(baseAddress), output, validate, timeoutMs, ShowHelp: false);
    }

    public static void PrintHelp()
    {
        Console.WriteLine("""
        Usage:
          dotnet run --project tools/Ehwrj.Tools.Capture -- [--base-url http://127.0.0.1:8111/] [--out captures/name] [--timeout-ms 1200]
          dotnet run --project tools/Ehwrj.Tools.Capture -- --validate captures/name

        Captures War Thunder local map, telemetry, and message endpoints into a fixture directory.
        Writes capture-report.txt with parser coverage and replay readiness.
        """);
    }

    private static Uri EnsureTrailingSlash(Uri uri)
    {
        var text = uri.ToString();
        return text.EndsWith('/') ? uri : new Uri($"{text}/");
    }
}
