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

namespace SatelliteProvider.IntegrationTests;

public static class RouteTestHelpers
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true
    };

    private static readonly JsonSerializerOptions JsonWriteOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

    public static void PrintTestHeader(string title)
    {
        Console.WriteLine();
        Console.WriteLine(title);
        Console.WriteLine(new string('=', title.Length));
        Console.WriteLine();
    }

    public static void PrintSectionHeader(string title)
    {
        Console.WriteLine(title);
        Console.WriteLine(new string('-', title.Length));
    }

    public static void PrintRouteCreationDetails(CreateRouteRequest request, bool includeGeofences = false)
    {
        Console.WriteLine($"  Action Points: {request.Points.Count}");
        if (includeGeofences && request.Geofences?.Polygons != null)
        {
            Console.WriteLine($"  Geofence Regions: {request.Geofences.Polygons.Count}");
        }
        Console.WriteLine($"  Region Size: {request.RegionSizeMeters}m");
        Console.WriteLine($"  Zoom Level: {request.ZoomLevel}");
        Console.WriteLine($"  Request Maps: {request.RequestMaps}");
        Console.WriteLine();
    }

    public static void PrintRoutePoints(List<RoutePointInput> points, int maxPoints = 0)
    {
        Console.WriteLine("Route Path:");
        if (maxPoints > 0 && points.Count > maxPoints * 2)
        {
            for (int i = 0; i < maxPoints; i++)
            {
                Console.WriteLine($"  {i + 1}. ({points[i].Lat}, {points[i].Lon})");
            }
            Console.WriteLine("  ...");
            for (int i = points.Count - maxPoints; i < points.Count; i++)
            {
                Console.WriteLine($"  {i + 1}. ({points[i].Lat}, {points[i].Lon})");
            }
        }
        else
        {
            for (int i = 0; i < points.Count; i++)
            {
                Console.WriteLine($"  {i + 1}. ({points[i].Lat}, {points[i].Lon})");
            }
        }
        Console.WriteLine();
    }

    public static void PrintGeofences(List<GeofencePolygonInput> polygons)
    {
        Console.WriteLine("Geofence Regions:");
        for (int i = 0; i < polygons.Count; i++)
        {
            var polygon = polygons[i];
            Console.WriteLine($"  {i + 1}. NW: ({polygon.NorthWest?.Lat}, {polygon.NorthWest?.Lon}) -> SE: ({polygon.SouthEast?.Lat}, {polygon.SouthEast?.Lon})");
        }
        Console.WriteLine();
    }

    public static void PrintRouteResponse(RouteResponseModel route)
    {
        Console.WriteLine($"✓ Route created with {route.TotalPoints} total points");
        Console.WriteLine($"  Distance: {route.TotalDistanceMeters:F2}m");
        Console.WriteLine($"  Request Maps: {route.RequestMaps}");
        Console.WriteLine($"  Maps Ready: {route.MapsReady}");
        Console.WriteLine();
    }

    public static void PrintPointTypeDistribution(RouteResponseModel route)
    {
        var startPoints = route.Points.Count(p => p.PointType == "start");
        var endPoints = route.Points.Count(p => p.PointType == "end");
        var actionPoints = route.Points.Count(p => p.PointType == "action");
        var intermediatePoints = route.Points.Count(p => p.PointType == "intermediate");

        Console.WriteLine("Point Type Distribution:");
        Console.WriteLine($"  Start: {startPoints}");
        Console.WriteLine($"  Action: {actionPoints}");
        Console.WriteLine($"  Intermediate: {intermediatePoints}");
        Console.WriteLine($"  End: {endPoints}");
        Console.WriteLine();
    }

    public static void PrintGeneratedFiles(RouteResponseModel route, int uniqueTileCount, bool includeZip = false)
    {
        var stitchedInfo = new FileInfo(route.StitchedImagePath!);

        Console.WriteLine("Files Generated:");
        Console.WriteLine($"  ✓ CSV: {Path.GetFileName(route.CsvFilePath)} ({uniqueTileCount} tiles)");
        Console.WriteLine($"  ✓ Summary: {Path.GetFileName(route.SummaryFilePath)}");
        Console.WriteLine($"  ✓ Stitched Map: {Path.GetFileName(route.StitchedImagePath)} ({stitchedInfo.Length / 1024:F2} KB)");

        if (includeZip && !string.IsNullOrEmpty(route.TilesZipPath))
        {
            var zipInfo = new FileInfo(route.TilesZipPath);
            Console.WriteLine($"  ✓ Tiles ZIP: {Path.GetFileName(route.TilesZipPath)} ({zipInfo.Length / 1024:F2} KB)");
        }
        Console.WriteLine();
    }

    public static void PrintRouteSummary(RouteResponseModel route, int uniqueTileCount, int? geofenceCount = null, bool includeZip = false)
    {
        Console.WriteLine("Route Summary:");
        Console.WriteLine($"  Route ID: {route.Id}");
        Console.WriteLine($"  Total Points: {route.TotalPoints}");
        Console.WriteLine($"  Distance: {route.TotalDistanceMeters:F2}m");

        if (geofenceCount.HasValue)
        {
            Console.WriteLine($"  Geofence Regions: {geofenceCount.Value}");
        }

        Console.WriteLine($"  Unique Tiles: {uniqueTileCount}");

        if (includeZip && !string.IsNullOrEmpty(route.TilesZipPath))
        {
            var zipInfo = new FileInfo(route.TilesZipPath);
            Console.WriteLine($"  ZIP File Size: {zipInfo.Length / 1024:F2} KB");
        }

        Console.WriteLine($"  Maps Ready: {route.MapsReady}");
        Console.WriteLine();
    }

    public static async Task<RouteResponseModel> CreateRoute(HttpClient httpClient, CreateRouteRequest request)
    {
        var response = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions);

        if (!response.IsSuccessStatusCode)
        {
            var errorContent = await response.Content.ReadAsStringAsync();
            throw new Exception($"Route creation failed: {response.StatusCode} - {errorContent}");
        }

        var route = await response.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);

        if (route == null)
        {
            throw new Exception("No route data returned from API");
        }

        return route;
    }

    public static async Task<RouteResponseModel> WaitForRouteReady(
        HttpClient httpClient,
        Guid routeId,
        int maxAttempts = 180,
        int pollInterval = 3000)
    {
        for (int attempt = 0; attempt < maxAttempts; attempt++)
        {
            await Task.Delay(pollInterval);

            var getResponse = await httpClient.GetAsync($"/api/satellite/route/{routeId}");

            if (!getResponse.IsSuccessStatusCode)
            {
                throw new Exception($"Failed to get route status: {getResponse.StatusCode}");
            }

            var currentRoute = await getResponse.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);

            if (currentRoute == null)
            {
                throw new Exception("No route returned");
            }

            if (currentRoute.MapsReady)
            {
                Console.WriteLine($"✓ Route maps ready in approximately {attempt * pollInterval / 1000}s");
                return currentRoute;
            }

            if (attempt % 5 == 0)
            {
                Console.WriteLine($"  Waiting... (attempt {attempt + 1}/{maxAttempts})");
            }

            if (attempt == maxAttempts - 1)
            {
                throw new Exception($"Timeout: Route maps did not become ready in {maxAttempts * pollInterval / 1000}s");
            }
        }

        throw new Exception("Failed to wait for route ready");
    }

    public static void ValidateFiles(RouteResponseModel route, bool includeZip = false)
    {
        if (string.IsNullOrEmpty(route.CsvFilePath))
        {
            throw new Exception("CSV file path is null or empty");
        }

        if (string.IsNullOrEmpty(route.SummaryFilePath))
        {
            throw new Exception("Summary file path is null or empty");
        }

        if (string.IsNullOrEmpty(route.StitchedImagePath))
        {
            throw new Exception("Stitched image path is null or empty");
        }

        if (!File.Exists(route.CsvFilePath))
        {
            throw new Exception($"CSV file not found: {route.CsvFilePath}");
        }

        if (!File.Exists(route.SummaryFilePath))
        {
            throw new Exception($"Summary file not found: {route.SummaryFilePath}");
        }

        if (!File.Exists(route.StitchedImagePath))
        {
            throw new Exception($"Stitched image not found: {route.StitchedImagePath}");
        }

        if (includeZip)
        {
            if (string.IsNullOrEmpty(route.TilesZipPath))
            {
                throw new Exception("Tiles ZIP file path is null or empty");
            }

            if (!File.Exists(route.TilesZipPath))
            {
                throw new Exception($"Tiles ZIP file not found: {route.TilesZipPath}");
            }
        }
    }

    public static void ValidatePointTypes(RouteResponseModel route, int expectedStart, int expectedEnd, int expectedAction)
    {
        var startPoints = route.Points.Count(p => p.PointType == "start");
        var endPoints = route.Points.Count(p => p.PointType == "end");
        var actionPoints = route.Points.Count(p => p.PointType == "action");

        if (startPoints != expectedStart)
        {
            throw new Exception($"Expected {expectedStart} start point(s), got {startPoints}");
        }

        if (endPoints != expectedEnd)
        {
            throw new Exception($"Expected {expectedEnd} end point(s), got {endPoints}");
        }

        if (actionPoints != expectedAction)
        {
            throw new Exception($"Expected {expectedAction} action point(s), got {actionPoints}");
        }
    }

    public static void ValidateRequestMaps(RouteResponseModel route, bool shouldBeReady)
    {
        if (!route.RequestMaps)
        {
            throw new Exception("Expected RequestMaps to be true");
        }

        if (route.MapsReady != shouldBeReady)
        {
            throw new Exception($"Expected MapsReady to be {shouldBeReady}, got {route.MapsReady}");
        }
    }

    public static async Task<int> GetUniqueTileCount(string csvFilePath)
    {
        var csvLines = await File.ReadAllLinesAsync(csvFilePath);
        return csvLines.Length - 1;
    }
}

