using System;
using System.Threading;
using Auth;
using Cysharp.Net.Http;
using eagle;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using Net.Eagle0.Eagle.Api;

public class EagleConnection : IDisposable {
    public readonly string playerName;
    public readonly Metadata credentials;
    public readonly string authHeader;
    // Reduced from 45s to 15s for better NAT/firewall traversal
    private const double KeepAliveSeconds = 15.0;

    public static CancellationToken EagleCancellationToken =>
            ConnectionKiller.EagleCancellationToken;

    public Eagle.EagleClient EagleGrpcClient { get; private set; }
    private GrpcChannel _channel;
    private ILoggerFactory _loggerFactory;

    private CallInvoker MakeInvoker(string playerName, string password, string url) {
        var authInterceptor = new AuthInterceptor(playerName, password);

        _channel = GrpcChannel.ForAddress(
                "https://" + url,
                new GrpcChannelOptions {
                    HttpHandler = CreateHttpHandler(),
                    DisposeHttpClient = true,
                    LoggerFactory = _loggerFactory,
                    MaxReceiveMessageSize = null
                });

        var invoker = _channel.Intercept(authInterceptor);
        return invoker;
    }

    /// <summary>
    /// Create HTTP handler with timeouts configured for reliable connection management.
    /// These settings help detect and recover from dead connections, especially on Windows
    /// where firewalls may silently drop HTTP/2 keep-alive pings.
    /// </summary>
    private static YetAnotherHttpHandler CreateHttpHandler() {
        return new YetAnotherHttpHandler {
            Http2Only = true,
            // Send keep-alive pings every 15 seconds
            Http2KeepAliveInterval = TimeSpan.FromSeconds(KeepAliveSeconds),
            // Close connection if ping not acknowledged within 5 seconds
            Http2KeepAliveTimeout = TimeSpan.FromSeconds(5),
            // Continue pinging even when idle to detect dead connections
            Http2KeepAliveWhileIdle = true,
            // Don't wait forever for initial connection
            ConnectTimeout = TimeSpan.FromSeconds(10)
        };
    }

    private CallInvoker MakeJwtInvoker(string url) {
        var jwtInterceptor = new JwtAuthInterceptor();

        _channel = GrpcChannel.ForAddress(
                "https://" + url,
                new GrpcChannelOptions {
                    HttpHandler = CreateHttpHandler(),
                    DisposeHttpClient = true,
                    LoggerFactory = _loggerFactory,
                    MaxReceiveMessageSize = null
                });

        var invoker = _channel.Intercept(jwtInterceptor);
        return invoker;
    }

    /// <summary>
    /// Create connection using Basic Auth (legacy).
    /// </summary>
    public EagleConnection(string playerName, string password, string url) {
        this.playerName = playerName;
        credentials = new Metadata { { "user", playerName } };
        authHeader = AuthInterceptor.HeaderValue(playerName, password);

        _loggerFactory = LoggerFactory.Create(
                builder => { builder.AddProvider(new CustomFileLoggerProvider()); });

        EagleGrpcClient = new Eagle.EagleClient(MakeInvoker(playerName, password, url));
    }

    /// <summary>
    /// Create connection using JWT Bearer token auth.
    /// Tokens are read from TokenStorage on each request.
    /// </summary>
    public static EagleConnection CreateWithJwt(string url) {
        return new EagleConnection(url, useJwt: true);
    }

    private EagleConnection(string url, bool useJwt) {
        // For JWT auth, get display name from TokenStorage
        playerName = TokenStorage.DisplayName ?? "Unknown";
        credentials = new Metadata { { "user", TokenStorage.UserId ?? "" } };
        authHeader = null;  // Not used for JWT

        _loggerFactory = LoggerFactory.Create(
                builder => { builder.AddProvider(new CustomFileLoggerProvider()); });

        EagleGrpcClient = new Eagle.EagleClient(MakeJwtInvoker(url));
    }

    public void Dispose() {
        try {
            _channel?.Dispose();
        } catch (Exception ex) { Console.WriteLine($"Error disposing gRPC channel: {ex.Message}"); }

        try {
            _loggerFactory?.Dispose();
        } catch (Exception ex) {
            Console.WriteLine($"Error disposing logger factory: {ex.Message}");
        }
    }
}
