using Microsoft.AspNetCore.Hosting.StaticWebAssets;
using ZB.MOM.WW.MxGateway.Contracts;
using ZB.MOM.WW.MxGateway.Server.Alarms;
using ZB.MOM.WW.MxGateway.Server.Configuration;
using ZB.MOM.WW.MxGateway.Server.Dashboard;
using ZB.MOM.WW.MxGateway.Server.Diagnostics;
using ZB.MOM.WW.MxGateway.Server.Galaxy;
using ZB.MOM.WW.MxGateway.Server.Grpc;
using ZB.MOM.WW.MxGateway.Server.Metrics;
using ZB.MOM.WW.MxGateway.Server.Security.Authentication;
using ZB.MOM.WW.MxGateway.Server.Security.Authorization;
using ZB.MOM.WW.MxGateway.Server.Sessions;
using ZB.MOM.WW.MxGateway.Server.Workers;

namespace ZB.MOM.WW.MxGateway.Server;

/// <summary>
/// Configures and builds the gateway web application.
/// </summary>
public static class GatewayApplication
{
    private const string StaticAssetsManifestFileName = "ZB.MOM.WW.MxGateway.Server.staticwebassets.endpoints.json";

    /// <summary>
    /// Builds a configured web application with all gateway services and middleware.
    /// </summary>
    /// <param name="args">Command-line arguments passed to the application.</param>
    /// <returns>A configured web application ready to run.</returns>
    public static WebApplication Build(string[] args)
    {
        WebApplicationBuilder builder = CreateBuilder(args);
        WebApplication app = builder.Build();

        app.UseGatewayRequestLoggingScope();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseAntiforgery();
        app.MapGatewayEndpoints();

        return app;
    }

    /// <summary>
    /// Creates a web application builder configured with gateway services.
    /// </summary>
    /// <param name="args">Command-line arguments passed to the application.</param>
    /// <returns>A configured web application builder.</returns>
    public static WebApplicationBuilder CreateBuilder(string[] args)
    {
        WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions
        {
            Args = args,
            ContentRootPath = ResolveContentRootPath(),
        });
        StaticWebAssetsLoader.UseStaticWebAssets(builder.Environment, builder.Configuration);

        builder.Services.AddGatewayConfiguration();
        builder.Services.AddSqliteAuthStore();
        builder.Services.AddGatewayGrpcAuthorization();
        builder.Services.AddHealthChecks();
        builder.Services.AddSingleton<GatewayMetrics>();
        builder.Services.AddSingleton<MxAccessGrpcMapper>();
        builder.Services.AddSingleton<MxAccessGrpcRequestValidator>();
        builder.Services.AddSingleton<IEventStreamService, EventStreamService>();
        builder.Services.AddWorkerProcessLauncher();
        builder.Services.AddGatewaySessions();
        builder.Services.AddGatewayAlarms();
        builder.Services.AddGatewayDashboard();
        builder.Services.AddGalaxyRepository();

        return builder;
    }

    private static string ResolveContentRootPath()
    {
        string? configuredContentRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_CONTENTROOT");
        if (!string.IsNullOrWhiteSpace(configuredContentRootPath)
            && IsServerContentRoot(configuredContentRootPath))
        {
            return configuredContentRootPath;
        }

        string currentDirectory = Environment.CurrentDirectory;
        if (IsServerContentRoot(currentDirectory))
        {
            return currentDirectory;
        }

        string baseDirectory = AppContext.BaseDirectory;
        if (IsServerContentRoot(baseDirectory))
        {
            return baseDirectory;
        }

        string? discoveredContentRootPath = DiscoverServerContentRoot(currentDirectory)
            ?? DiscoverServerContentRoot(baseDirectory);

        return discoveredContentRootPath ?? baseDirectory;
    }

    private static string? DiscoverServerContentRoot(string startPath)
    {
        DirectoryInfo? directory = new(startPath);
        while (directory is not null)
        {
            if (IsServerContentRoot(directory.FullName))
            {
                return directory.FullName;
            }

            string serverProjectPath = Path.Combine(directory.FullName, "src", "ZB.MOM.WW.MxGateway.Server");
            if (IsServerContentRoot(serverProjectPath))
            {
                return serverProjectPath;
            }

            directory = directory.Parent;
        }

        return null;
    }

    private static bool IsServerContentRoot(string path)
    {
        return File.Exists(Path.Combine(path, "appsettings.json"))
            && Directory.Exists(Path.Combine(path, "wwwroot"));
    }

    /// <summary>
    /// Maps gateway endpoints including gRPC services, health checks, and the dashboard.
    /// </summary>
    /// <param name="endpoints">Endpoint route builder to map endpoints to.</param>
    /// <returns>The same endpoint route builder for chaining.</returns>
    public static IEndpointRouteBuilder MapGatewayEndpoints(this IEndpointRouteBuilder endpoints)
    {
        endpoints.MapStaticAssets(ResolveStaticAssetsManifestPath());

        endpoints.MapGet(
                "/health/live",
                () => Results.Ok(new GatewayHealthReply(
                    Status: "Healthy",
                    DefaultBackend: GatewayContractInfo.DefaultBackendName,
                    WorkerProtocolVersion: GatewayContractInfo.WorkerProtocolVersion)))
            .WithName("LiveHealth");

        endpoints.MapGrpcService<MxAccessGatewayService>();
        endpoints.MapGrpcService<GalaxyRepositoryGrpcService>();
        endpoints.MapGatewayDashboard();

        return endpoints;
    }

    private static string ResolveStaticAssetsManifestPath()
    {
        string manifestPath = Path.Combine(AppContext.BaseDirectory, StaticAssetsManifestFileName);

        return File.Exists(manifestPath) ? manifestPath : StaticAssetsManifestFileName;
    }
}
