namespace ErsatzTV.Mcp;

public static class Program
{
    public static async Task Main()
    {
        string baseUrl = Environment.GetEnvironmentVariable("ERSATZTV_URL") ?? "http://localhost:8409";
        string? apiKey = Environment.GetEnvironmentVariable("ERSATZTV_API_KEY");
        bool allowWrites = ParseBool(Environment.GetEnvironmentVariable("ERSATZTV_ALLOW_WRITES"));
        int maxResponseBytes = ParseInt(
            Environment.GetEnvironmentVariable("ERSATZTV_MAX_RESPONSE_BYTES"),
            fallback: ErsatzTvApiClientOptions.DefaultMaxResponseBytes,
            min: 1024,
            max: ErsatzTvApiClientOptions.MaxAllowedResponseBytes);
        int timeoutSeconds = ParseInt(
            Environment.GetEnvironmentVariable("ERSATZTV_REQUEST_TIMEOUT_SECONDS"),
            fallback: 30,
            min: 1,
            max: 3600);
        var requestTimeout = TimeSpan.FromSeconds(timeoutSeconds);

        // The per-request timeout is enforced via a CancellationToken inside the client (it must
        // cover the streamed body read too), so leave HttpClient's own timeout off to avoid a
        // second, header-only timer racing it.
        using var httpClient = new HttpClient { Timeout = Timeout.InfiniteTimeSpan };
        var apiClient = new ErsatzTvApiClient(
            httpClient,
            new ErsatzTvApiClientOptions(new Uri(baseUrl), apiKey, allowWrites, maxResponseBytes, requestTimeout));
        var server = new McpServer(apiClient);

        while (true)
        {
            BoundedLineReader.Line line = await BoundedLineReader.ReadLineAsync(Console.In);
            if (line.EndOfStream)
            {
                break;
            }

            if (line.Overflowed)
            {
                await Console.Error.WriteLineAsync("[ersatztv-mcp] dropped oversized request line.");
                continue;
            }

            if (string.IsNullOrWhiteSpace(line.Text))
            {
                continue;
            }

            try
            {
                string? response = await server.HandleAsync(line.Text, CancellationToken.None);
                if (response is not null)
                {
                    await Console.Out.WriteLineAsync(response);
                    await Console.Out.FlushAsync();
                }
            }
            catch (Exception ex)
            {
                // Last-resort guard: a single failed request must never terminate the session.
                await Console.Error.WriteLineAsync($"[ersatztv-mcp] error handling request: {ex.Message}");
            }
        }
    }

    private static bool ParseBool(string? value)
    {
        if (value is null)
        {
            return false;
        }

        if (bool.TryParse(value, out bool parsed))
        {
            return parsed;
        }

        string trimmed = value.Trim();
        return trimmed is "1"
            || string.Equals(trimmed, "yes", StringComparison.OrdinalIgnoreCase)
            || string.Equals(trimmed, "on", StringComparison.OrdinalIgnoreCase);
    }

    private static int ParseInt(string? value, int fallback, int min, int max) =>
        int.TryParse(value, out int parsed) ? Math.Clamp(parsed, min, max) : fallback;
}
