#!/usr/bin/env rdmd
// PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
// Copyright 2025 TimeHexOn & foxhop & russell@unturf
// https://www.permacomputer.com


// uncloseai. - D Language Client
// Multiple AI model examples with streaming and non-streaming chat, plus TTS

import std.stdio;
import std.net.curl;
import std.json;
import std.string;
import std.algorithm;
import std.array;
import std.file;
import std.process;
import std.conv;

void main()
{
    writeln("=== uncloseai. D Language Client ===\n");

    // Discover endpoints from environment variables
    string modelEndpoint1 = environment.get("MODEL_ENDPOINT_1", "");
    string modelEndpoint2 = environment.get("MODEL_ENDPOINT_2", "");
    string ttsEndpoint1 = environment.get("TTS_ENDPOINT_1", "");

    if (modelEndpoint1 == "" || modelEndpoint2 == "" || ttsEndpoint1 == "")
    {
        writeln("ERROR: No models discovered. Set environment variables:");
        writeln("  MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, TTS_ENDPOINT_1");
        return;
    }

    // Discover models from endpoint 1
    writeln("Discovering models from ", modelEndpoint1, "...");
    auto response1 = get(modelEndpoint1 ~ "/models");
    auto models1 = parseJSON(response1);
    string model1Id = models1["data"][0]["id"].str;
    writeln("Model 1: ", model1Id, "\n");

    // Discover models from endpoint 2
    writeln("Discovering models from ", modelEndpoint2, "...");
    auto response2 = get(modelEndpoint2 ~ "/models");
    auto models2 = parseJSON(response2);
    string model2Id = models2["data"][0]["id"].str;
    writeln("Model 2: ", model2Id, "\n");

    // Non-streaming chat with Model 1
    writeln("=== Non-Streaming Chat (Model 1) ===");
    try
    {
        JSONValue payload1;
        payload1["model"] = model1Id;
        payload1["messages"] = [
            JSONValue([
                "role": JSONValue("user"),
                "content": JSONValue("Give a Python Fizzbuzz solution in one line of code?")
            ])
        ];
        payload1["temperature"] = 0.5;
        payload1["max_tokens"] = 150;
        payload1["stream"] = false;

        auto http1 = HTTP();
        http1.addRequestHeader("Content-Type", "application/json");
        auto chatResponse1 = post(modelEndpoint1 ~ "/chat/completions", payload1.toString(), http1);
        auto jsonResponse1 = parseJSON(chatResponse1);
        auto content1 = jsonResponse1["choices"][0]["message"]["content"].str;
        writeln("Response: ", content1, "\n");
    }
    catch (Exception e)
    {
        writeln("Error: ", e.msg, "\n");
    }

    // Streaming chat with Model 2
    writeln("=== Streaming Chat (Model 2) ===");
    write("Response: ");
    stdout.flush();

    try
    {
        JSONValue payload2;
        payload2["model"] = model2Id;
        payload2["messages"] = [
            JSONValue([
                "role": JSONValue("user"),
                "content": JSONValue("Give a Python Fizzbuzz solution in one line of code?")
            ])
        ];
        payload2["temperature"] = 0.5;
        payload2["max_tokens"] = 150;
        payload2["stream"] = true;

        auto http2 = HTTP();
        http2.addRequestHeader("Content-Type", "application/json");
        http2.method = HTTP.Method.post;
        http2.url = modelEndpoint2 ~ "/chat/completions";
        http2.postData = payload2.toString();

        string buffer = "";

        // Process chunks as they arrive
        http2.onReceive = (ubyte[] data)
        {
            buffer ~= cast(string)data;

            // Process complete lines
            while (true)
            {
                auto idx = buffer.indexOf("\n");
                if (idx == -1) break;

                auto line = buffer[0..idx].strip();
                buffer = buffer[idx+1..$];

                // Process SSE data lines
                if (line.startsWith("data: "))
                {
                    auto jsonData = line[6..$];
                    if (jsonData == "[DONE]") continue;

                    try
                    {
                        auto parsed = parseJSON(jsonData);
                        if ("choices" in parsed && parsed["choices"].array.length > 0)
                        {
                            auto delta = parsed["choices"][0]["delta"];
                            if ("content" in delta)
                            {
                                write(delta["content"].str);
                                stdout.flush();
                            }
                        }
                    }
                    catch (Exception) {}
                }
            }

            return data.length;
        };

        http2.perform();
        writeln("\n");
    }
    catch (Exception e)
    {
        writeln("\nError: ", e.msg, "\n");
    }

    // TTS example
    writeln("=== TTS Speech Generation ===");
    try
    {
        JSONValue ttsPayload;
        ttsPayload["model"] = "tts-1";
        ttsPayload["voice"] = "alloy";
        ttsPayload["speed"] = 0.9;
        ttsPayload["input"] = "I think so therefore, Today is a wonderful day to grow something people love!";

        auto httpTts = HTTP();
        httpTts.addRequestHeader("Content-Type", "application/json");
        auto ttsResponse = post(ttsEndpoint1 ~ "/audio/speech", ttsPayload.toString(), httpTts);

        std.file.write("speech.mp3", ttsResponse);
        writeln("[OK] Speech file created: speech.mp3 (", ttsResponse.length, " bytes)\n");
    }
    catch (Exception e)
    {
        writeln("[ERROR] TTS failed: ", e.msg, "\n");
    }

    writeln("=== Examples Complete ===");
}
