using System.Runtime.InteropServices;
using Flo.Installer.Provisioning;

namespace Flo.Installer.Commands;

internal static class RunCommand
{
    public static async Task<int> ExecuteAsync(string installPath)
    {
        var paths = new Paths(installPath);
        if (!File.Exists(paths.ConfigFile) || !File.Exists(paths.SecretsFile))
        {
            Console.Error.WriteLine("flo.json or secrets.json missing. Run `provision` first.");
            return 1;
        }

        var config = FloConfig.Load(paths.ConfigFile);
        var secrets = new SecretsStore(paths.SecretsFile).Load();

        using var cts = new CancellationTokenSource();
        Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); };
        using var _sigterm = PosixSignalRegistration.Create(PosixSignal.SIGTERM, ctx =>
        {
            ctx.Cancel = true;
            cts.Cancel();
        });

        var postgres = new PostgresProvisioner(paths);
        var ferret = new FerretDbProvisioner(paths);

        Console.WriteLine($"Starting Postgres on 127.0.0.1:{config.PostgresPort}...");
        await using var pg = postgres.Start(config.PostgresPort);
        await HealthCheck.WaitForPortAsync(
            "127.0.0.1", config.PostgresPort, TimeSpan.FromSeconds(30), "Postgres", cts.Token);
        Console.WriteLine("Postgres healthy.");

        Console.WriteLine($"Starting FerretDB on 127.0.0.1:{config.FerretDbPort}...");
        await using var fd = ferret.Start(
            listenPort:   config.FerretDbPort,
            postgresPort: config.PostgresPort,
            pgUser:       config.ApplicationDbRole,
            pgPassword:   secrets[$"pg.{config.ApplicationDbRole}"],
            pgDatabase:   config.ApplicationDbName);
        await HealthCheck.WaitForPortAsync(
            "127.0.0.1", config.FerretDbPort, TimeSpan.FromSeconds(30), "FerretDB", cts.Token);
        Console.WriteLine("FerretDB healthy.");

        Console.WriteLine();
        Console.WriteLine($"Postgres:  host=127.0.0.1 port={config.PostgresPort} user={config.ApplicationDbRole} db={config.ApplicationDbName}");
        Console.WriteLine($"Mongo URL: mongodb://{config.ApplicationDbRole}:<password>@127.0.0.1:{config.FerretDbPort}/{config.ApplicationDbName}");
        Console.WriteLine("(Password is in secrets.json.)");
        Console.WriteLine();
        Console.WriteLine("Press Ctrl-C to stop.");

        try { await Task.Delay(Timeout.Infinite, cts.Token); }
        catch (OperationCanceledException) { }

        Console.WriteLine();
        Console.WriteLine("Shutdown requested. Stopping FerretDB then Postgres...");

        try { await fd.StopAsync(TimeSpan.FromSeconds(10)); }
        catch (Exception ex) { Console.Error.WriteLine($"FerretDB stop error: {ex.Message}"); }

        try { postgres.StopGracefully(); }
        catch (Exception ex) { Console.Error.WriteLine($"Postgres graceful stop error: {ex.Message}"); }

        return 0;
    }
}
