using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

var builder = DistributedApplication.CreateBuilder(args);

// WEB-2: lokales Verzeichnis für die veröffentlichte öffentliche Seite. Die API schreibt
// nach <root>/live (POST /api/publish), der publicsite-nginx serviert genau dieses live/
// — dieselbe Form wie in Produktion (deploy/truenas), nur lokal zum Testen.
var publicSiteRoot = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "deploy", ".local-publicsite"));
Directory.CreateDirectory(Path.Combine(publicSiteRoot, "live"));   // leeres live/, damit nginx sofort startet
var publicSiteConf = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "deploy", "truenas", "nginx", "publicsite.conf"));

var postgres = builder.AddPostgres("postgres")
    .WithDataVolume();

var gerbilDb = postgres.AddDatabase("gerbilmanager");

// Stable, LAN-facing HTTP port for the API (matches the SPA's default base URL).
const int ApiHttpPort = 5179;

var webapi = builder.AddProject<Projects.GerbilManagerWebAPI>("webapi")
    .WithReference(gerbilDb)
    .WaitFor(gerbilDb)
    // Pin the port so the phone-facing URL doesn't change between launches.
    // Proxyless so the app binds the port directly — Program.cs then widens the
    // host to 0.0.0.0 for LAN access (the DCP proxy binds localhost only).
    .WithEndpoint("http", e => { e.Port = ApiHttpPort; e.IsProxied = false; }, createIfNotExists: false)
    // WEB-2: Publish-Ziel der öffentlichen Seite (sonst gibt POST /api/publish 503).
    .WithEnvironment("PublicSite__RootPath", publicSiteRoot)
    .WithExternalHttpEndpoints();

// The machine's LAN IP — the SPA runs in the phone's browser, so it must reach
// the API at this address, not "localhost" (which would be the phone itself).
var lanIp = GetLanIpAddress();

builder.AddViteApp("frontend", "../gerbil-manager-web")
    .WithReference(webapi)
    .WaitFor(webapi)
    // Pin a stable port for the phone bookmark (Vite default 5173). Proxyless so
    // the Vite dev server binds the port directly; vite.config host:true makes it
    // bind 0.0.0.0 for LAN access.
    .WithEndpoint("http", e => { e.Port = 5173; e.IsProxied = false; }, createIfNotExists: false)
    .WithExternalHttpEndpoints()
    .WithEnvironment(context =>
    {
        // VITE_API_BASE_URL is read by the Vite dev server at launch (see
        // src/api/client.ts). Point it at the API's LAN-reachable URL.
        context.EnvironmentVariables["VITE_API_BASE_URL"] = $"http://{lanIp}:{ApiHttpPort}";
    });

// WEB-2: publicsite-nginx — serviert die veröffentlichte öffentliche Seite lokal (gleiche
// nginx-Config wie in Produktion). Liest das live/-Verzeichnis aus dem Publish-Output der API.
// Test: in der App veröffentlichen (oder POST /api/publish), dann http://localhost:8081 öffnen.
builder.AddContainer("publicsite", "nginx", "alpine")
    .WithBindMount(publicSiteRoot, "/usr/share/nginx/html", isReadOnly: true)
    .WithBindMount(publicSiteConf, "/etc/nginx/conf.d/default.conf", isReadOnly: true)
    .WithHttpEndpoint(port: 8081, targetPort: 80, name: "http")
    .WithExternalHttpEndpoints();

builder.Build().Run();

// Picks the IPv4 address of the active physical LAN adapter (Wi-Fi/Ethernet),
// skipping loopback and virtual adapters (WSL/Hyper-V). Recomputed each launch
// so it follows DHCP changes.
static string GetLanIpAddress()
{
    var candidates = NetworkInterface.GetAllNetworkInterfaces()
        .Where(ni => ni.OperationalStatus == OperationalStatus.Up)
        .Where(ni => ni.NetworkInterfaceType is NetworkInterfaceType.Wireless80211
                                              or NetworkInterfaceType.Ethernet)
        .Where(ni => !ni.Description.Contains("Hyper-V", StringComparison.OrdinalIgnoreCase)
                  && !ni.Description.Contains("Virtual", StringComparison.OrdinalIgnoreCase)
                  && !ni.Name.Contains("WSL", StringComparison.OrdinalIgnoreCase)
                  && !ni.Name.Contains("vEthernet", StringComparison.OrdinalIgnoreCase))
        // Prefer adapters that have a default gateway (i.e. a real network path).
        .OrderByDescending(ni => ni.GetIPProperties().GatewayAddresses.Count > 0);

    foreach (var ni in candidates)
    {
        var ip = ni.GetIPProperties().UnicastAddresses
            .FirstOrDefault(a => a.Address.AddressFamily == AddressFamily.InterNetwork
                              && !IPAddress.IsLoopback(a.Address));
        if (ip is not null)
        {
            return ip.Address.ToString();
        }
    }

    return "localhost";
}
