using SatelliteProvider.TestSupport;

namespace SatelliteProvider.IntegrationTests;

class Program
{
    static async Task<int> Main(string[] args)
    {
        // AZ-492: perf-harness bootstrap subcommands short-circuit before any
        // HTTP / DB setup so they can be invoked from scripts/run-performance-tests.sh
        // on a host that only has the .NET SDK installed.
        if (args.Length > 0)
        {
            if (args[0].Equals("--mint-only", StringComparison.OrdinalIgnoreCase))
            {
                return PerfBootstrap.MintToken();
            }
            if (args[0].Equals("--gen-uav-fixture", StringComparison.OrdinalIgnoreCase))
            {
                return PerfBootstrap.GenerateUavFixture(args);
            }
        }

        var apiUrl = Environment.GetEnvironmentVariable("API_URL") ?? "https://api:8080";
        var modeEnv = Environment.GetEnvironmentVariable("INTEGRATION_TESTS_MODE")?.Trim().ToLowerInvariant();
        var modeArg = args.FirstOrDefault(a => a.Equals("--smoke", StringComparison.OrdinalIgnoreCase) || a.Equals("--full", StringComparison.OrdinalIgnoreCase));

        if (modeArg != null)
        {
            TestRunMode.Smoke = modeArg.Equals("--smoke", StringComparison.OrdinalIgnoreCase);
        }
        else if (!string.IsNullOrEmpty(modeEnv))
        {
            TestRunMode.Smoke = modeEnv == "smoke";
        }

        // AZ-493: opt-out of the startup DB reset so a developer can inspect leftover state.
        var keepStateEnv = Environment.GetEnvironmentVariable("INTEGRATION_KEEP_STATE")?.Trim();
        var keepState = args.Any(a => a.Equals("--keep-state", StringComparison.OrdinalIgnoreCase))
            || string.Equals(keepStateEnv, "1", StringComparison.Ordinal)
            || string.Equals(keepStateEnv, "true", StringComparison.OrdinalIgnoreCase);

        var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING")
            ?? "Host=postgres;Port=5432;Database=satelliteprovider;Username=postgres;Password=postgres";

        string jwtSecret;
        string jwtIssuer;
        string jwtAudience;
        try
        {
            jwtSecret = JwtTestHelpers.ResolveSecretOrThrow();
            jwtIssuer = JwtTestHelpers.ResolveIssuerOrThrow();
            jwtAudience = JwtTestHelpers.ResolveAudienceOrThrow();
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine("❌ Integration tests cannot start: JWT configuration is incomplete.");
            Console.WriteLine($"   {ex.Message}");
            return 1;
        }

        Console.WriteLine("Starting Integration Tests");
        Console.WriteLine("=========================");
        Console.WriteLine($"API URL : {apiUrl}");
        Console.WriteLine($"Mode    : {(TestRunMode.Smoke ? "smoke (fast subset, tightened timeouts)" : "full")}");
        Console.WriteLine($"State   : {(keepState ? "keep (DB reset skipped)" : "reset (clean DB at startup, AZ-493)")}");
        Console.WriteLine($"Auth    : JWT_SECRET resolved ({System.Text.Encoding.UTF8.GetByteCount(jwtSecret)} bytes); iss={jwtIssuer}; aud={jwtAudience}");
        Console.WriteLine();

        using var httpClient = new HttpClient
        {
            BaseAddress = new Uri(apiUrl),
            Timeout = TimeSpan.FromMinutes(15)
        };

        var defaultToken = JwtTestHelpers.MintAuthenticated(jwtSecret);
        JwtTestHelpers.AttachDefaultAuthorization(httpClient, defaultToken);

        try
        {
            Console.WriteLine("Waiting for API to be ready...");
            await WaitForApiReady(httpClient);
            Console.WriteLine("✓ API is ready");

            if (keepState)
            {
                Console.WriteLine("⚠ --keep-state / INTEGRATION_KEEP_STATE set; skipping DB reset (AZ-493)");
            }
            else
            {
                try
                {
                    await new IntegrationTestDatabaseReset(connectionString).EnsureCleanStateAsync();
                }
                catch (InvalidOperationException ex)
                {
                    Console.WriteLine($"❌ DB reset refused: {ex.Message}");
                    return 1;
                }
            }

            Console.WriteLine();

            await JwtIntegrationTests.RunAll(apiUrl, jwtSecret);
            await UavUploadTests.RunAll(apiUrl, jwtSecret);
            await UavUploadValidationTests.RunAll(apiUrl, jwtSecret);
            await Http2MultiplexingTests.RunAll(apiUrl, jwtSecret);

            if (TestRunMode.Smoke)
            {
                await RunSmokeSuite(httpClient, connectionString, apiUrl, jwtSecret);
            }
            else
            {
                await RunFullSuite(httpClient, connectionString, apiUrl, jwtSecret);
            }

            Console.WriteLine();
            Console.WriteLine("=========================");
            Console.WriteLine("All tests completed successfully!");
            return 0;
        }
        catch (Exception ex)
        {
            Console.WriteLine();
            Console.WriteLine("❌ Integration tests failed");
            Console.WriteLine($"Error: {ex.Message}");
            Console.WriteLine($"Stack trace: {ex.StackTrace}");
            return 1;
        }
    }

    static async Task RunSmokeSuite(HttpClient httpClient, string connectionString, string apiUrl, string jwtSecret)
    {
        await TileTests.RunGetTileByLatLonTest(httpClient);
        await RegionTests.RunRegionProcessingTest_200m_Zoom18(httpClient);
        await BasicRouteTests.RunSimpleRouteTest(httpClient);
        await ExtendedRouteTests.RunRouteWithTilesZipTest(httpClient);
        await SecurityTests.RunAll(httpClient);
        await StubAndErrorContractTests.RunAll(httpClient);
        await IdempotentPostTests.RunAll(httpClient);
        await TileInventoryTests.RunAll(httpClient);
        await TileInventoryValidationTests.RunAll(httpClient);
        await RegionFieldRenameTests.RunAll(httpClient);
        await RegionRequestValidationTests.RunAll(httpClient);
        await GetTileByLatLonValidationTests.RunAll(httpClient);
        await CreateRouteValidationTests.RunAll(httpClient);
        await RouteTileDeliveryGrpcTests.RunAll(httpClient, apiUrl, jwtSecret);
        await LeafletPathIndexOnlyTests.RunAll(connectionString);
        await MigrationTests.RunAll();
    }

    static async Task RunFullSuite(HttpClient httpClient, string connectionString, string apiUrl, string jwtSecret)
    {
        await TileTests.RunGetTileByLatLonTest(httpClient);

        await RegionTests.RunRegionProcessingTest_200m_Zoom18(httpClient);
        await RegionTests.RunRegionProcessingTest_400m_Zoom17(httpClient);
        await RegionTests.RunRegionProcessingTest_500m_Zoom18(httpClient);

        await BasicRouteTests.RunSimpleRouteTest(httpClient);
        await BasicRouteTests.RunRouteWithRegionProcessingAndStitching(httpClient);
        await ExtendedRouteTests.RunRouteWithTilesZipTest(httpClient);
        await ComplexRouteTests.RunComplexRouteWithStitching(httpClient);
        await ComplexRouteTests.RunComplexRouteWithStitchingAndGeofences(httpClient);
        await ExtendedRouteTests.RunExtendedRouteEast(httpClient);

        await SecurityTests.RunAll(httpClient);
        await StubAndErrorContractTests.RunAll(httpClient);
        await IdempotentPostTests.RunAll(httpClient);
        await TileInventoryTests.RunAll(httpClient);
        await TileInventoryValidationTests.RunAll(httpClient);
        await RegionFieldRenameTests.RunAll(httpClient);
        await RegionRequestValidationTests.RunAll(httpClient);
        await GetTileByLatLonValidationTests.RunAll(httpClient);
        await CreateRouteValidationTests.RunAll(httpClient);
        await RouteTileDeliveryGrpcTests.RunAll(httpClient, apiUrl, jwtSecret);
        await LeafletPathIndexOnlyTests.RunAll(connectionString);
        await MigrationTests.RunAll();
    }

    static async Task WaitForApiReady(HttpClient httpClient, int maxRetries = 30)
    {
        for (int i = 0; i < maxRetries; i++)
        {
            try
            {
                var response = await httpClient.GetAsync("/");
                if (response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    return;
                }
            }
            catch
            {
            }

            Console.WriteLine($"  Attempt {i + 1}/{maxRetries} - waiting 2 seconds...");
            await Task.Delay(2000);
        }

        throw new Exception("API did not become ready in time");
    }
}
