// PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
// Copyright 2025 TimeHexOn & foxhop & russell@unturf
// https://www.permacomputer.com

using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;

class ModelInfo
{
    public string Id { get; set; } = "";
    public string Endpoint { get; set; } = "";
    public int MaxTokens { get; set; } = 8192;
}

class Uncloseai
{
    static readonly HttpClient client = new HttpClient();
    static readonly List<ModelInfo> models = new List<ModelInfo>();
    static readonly List<string> ttsEndpoints = new List<string>();

    static async Task DiscoverModelsFromEndpoint(string endpoint)
    {
        Console.WriteLine($"Discovering models from: {endpoint}");
        try
        {
            var response = await client.GetAsync($"{endpoint}/models");
            var body = await response.Content.ReadAsStringAsync();

            using var doc = JsonDocument.Parse(body);
            var root = doc.RootElement;

            if (root.TryGetProperty("data", out var data))
            {
                foreach (var model in data.EnumerateArray())
                {
                    var modelId = model.GetProperty("id").GetString() ?? "";
                    var maxTokens = 8192;
                    if (model.TryGetProperty("max_model_len", out var maxModelLen))
                    {
                        maxTokens = maxModelLen.GetInt32();
                    }

                    models.Add(new ModelInfo
                    {
                        Id = modelId,
                        Endpoint = endpoint,
                        MaxTokens = maxTokens
                    });
                    Console.WriteLine($"  - Discovered: {modelId}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"  Error: {ex.Message}");
        }
    }

    static async Task DiscoverAllModels()
    {
        Console.WriteLine("=== Model Discovery ===");

        // Discover chat/code models
        for (int i = 1; i <= 9999; i++)
        {
            var endpoint = Environment.GetEnvironmentVariable($"MODEL_ENDPOINT_{i}");
            if (string.IsNullOrEmpty(endpoint)) break;
            await DiscoverModelsFromEndpoint(endpoint);
        }

        // Discover TTS endpoints
        for (int i = 1; i <= 9999; i++)
        {
            var endpoint = Environment.GetEnvironmentVariable($"TTS_ENDPOINT_{i}");
            if (string.IsNullOrEmpty(endpoint)) break;
            Console.WriteLine($"Discovering TTS from: {endpoint}");
            ttsEndpoints.Add(endpoint);
        }

        Console.WriteLine($"\n{models.Count} model(s) discovered");
        Console.WriteLine($"{ttsEndpoints.Count} TTS endpoint(s) discovered\n");
    }

    static async Task<string> MakeChatRequest(int modelIdx, string systemMsg, string userMsg, int maxTokens)
    {
        var model = models[modelIdx];
        var url = $"{model.Endpoint}/chat/completions";

        var payload = new
        {
            model = model.Id,
            messages = new[]
            {
                new { role = "system", content = systemMsg },
                new { role = "user", content = userMsg }
            },
            max_tokens = maxTokens
        };

        var json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await client.PostAsync(url, content);
        var body = await response.Content.ReadAsStringAsync();

        using var doc = JsonDocument.Parse(body);
        var root = doc.RootElement;
        return root.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString() ?? "";
    }

    static async Task MakeChatStreamRequest(int modelIdx, string systemMsg, string userMsg, int maxTokens)
    {
        var model = models[modelIdx];
        var url = $"{model.Endpoint}/chat/completions";

        var payload = new
        {
            model = model.Id,
            messages = new[]
            {
                new { role = "system", content = systemMsg },
                new { role = "user", content = userMsg }
            },
            max_tokens = maxTokens,
            stream = true
        };

        var json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        using var request = new HttpRequestMessage(HttpMethod.Post, url);
        request.Content = content;

        using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
        using var stream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(stream);

        Console.Write("Response: ");

        while (!reader.EndOfStream)
        {
            var line = await reader.ReadLineAsync();
            if (string.IsNullOrEmpty(line)) continue;

            if (line.StartsWith("data: "))
            {
                var data = line.Substring(6);
                if (data == "[DONE]") break;

                try
                {
                    using var doc = JsonDocument.Parse(data);
                    var root = doc.RootElement;
                    if (root.TryGetProperty("choices", out var choices) && choices.GetArrayLength() > 0)
                    {
                        var choice = choices[0];
                        if (choice.TryGetProperty("delta", out var delta))
                        {
                            if (delta.TryGetProperty("content", out var contentProp))
                            {
                                var contentStr = contentProp.GetString();
                                if (!string.IsNullOrEmpty(contentStr))
                                {
                                    Console.Write(contentStr);
                                }
                            }
                        }
                    }
                }
                catch
                {
                    // Ignore parse errors
                }
            }
        }

        Console.WriteLine();
    }

    static async Task<string> MakeTtsRequest(string text)
    {
        if (ttsEndpoints.Count == 0)
            return "ERROR: No TTS endpoints available";

        var endpoint = ttsEndpoints[0];
        var url = $"{endpoint}/audio/speech";

        var payload = new
        {
            model = "tts-1",
            voice = "alloy",
            input = text
        };

        var json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await client.PostAsync(url, content);
        var audioData = await response.Content.ReadAsByteArrayAsync();

        await File.WriteAllBytesAsync("output.mp3", audioData);
        return $"Audio saved to output.mp3 ({audioData.Length} bytes)";
    }

    static async Task HermesExample()
    {
        Console.WriteLine("\n=== Non-Streaming Chat (using first discovered model) ===");
        if (models.Count == 0)
        {
            Console.WriteLine("ERROR: No models available");
            return;
        }

        var model = models[0];
        Console.WriteLine($"Model: {model.Id}");
        Console.WriteLine($"Endpoint: {model.Endpoint}\n");

        try
        {
            var response = await MakeChatRequest(
                0,
                "You are Hermes, a helpful AI assistant from Nous Research.",
                "Explain quantum computing in one sentence.",
                100
            );
            Console.WriteLine($"Response: {response}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }

    static async Task QwenStreamExample()
    {
        Console.WriteLine("\n=== Streaming Chat (using second or first model) ===");
        if (models.Count == 0)
        {
            Console.WriteLine("ERROR: No models available");
            return;
        }

        var modelIdx = models.Count >= 2 ? 1 : 0;
        var model = models[modelIdx];
        Console.WriteLine($"Model: {model.Id}");
        Console.WriteLine($"Endpoint: {model.Endpoint}\n");

        try
        {
            await MakeChatStreamRequest(
                modelIdx,
                "You are Qwen, a coding assistant specialized in software development.",
                "Write a hello world function in C#.",
                200
            );
            Console.WriteLine();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }

    static async Task TtsExample()
    {
        Console.WriteLine("\n=== TTS Speech Generation Example ===");
        if (ttsEndpoints.Count == 0)
        {
            Console.WriteLine("ERROR: No TTS endpoints available");
            return;
        }

        Console.WriteLine($"Endpoint: {ttsEndpoints[0]}\n");

        try
        {
            var result = await MakeTtsRequest("Hello from C#! This is a text to speech example.");
            Console.WriteLine(result);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }

    static async Task Main(string[] args)
    {
        Console.WriteLine("C# AI API Examples (Dynamic Model Discovery)");
        Console.WriteLine("=============================================");
        Console.WriteLine();

        await DiscoverAllModels();

        if (models.Count == 0)
        {
            Console.WriteLine("ERROR: No models discovered. Check environment variables:");
            Console.WriteLine("  MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.");
            Environment.Exit(1);
        }

        await HermesExample();
        await QwenStreamExample();
        await TtsExample();
    }
}
