internal static partial class Program
{
    static async Task TestEntryAiHealthDefaultAsync()
    {
        var entry = NewEntry();
        var response = await entry.HandleCommandAsync("""{"action":"ai.health"}""");
        using var doc = JsonDocument.Parse(response);

        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for ai.health.");
        var data = doc.RootElement.GetProperty("data");
        Assert(data.GetProperty("Enabled").GetBoolean() is false, "Expected AI disabled by default.");
        Assert(string.Equals(data.GetProperty("Provider").GetString(), "none", StringComparison.OrdinalIgnoreCase), "Expected default provider 'none'.");
    }

    static async Task TestEntryAiSummarizeEntryDisabledAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "ai.summarize_entry",
            payload = new
            {
                content = "sample entry"
            }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);

        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for disabled ai.summarize_entry.");
        var data = doc.RootElement.GetProperty("data").GetString() ?? "";
        Assert(data.Contains("disabled", StringComparison.OrdinalIgnoreCase), "Expected disabled provider message for ai.summarize_entry.");
    }

    static async Task TestEntryAiSummarizeAllDisabledAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "ai.summarize_all",
            payload = new
            {
                entries = new[] { "entry one", "entry two" }
            }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);

        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for disabled ai.summarize_all.");
        var data = doc.RootElement.GetProperty("data").GetString() ?? "";
        Assert(data.Contains("disabled", StringComparison.OrdinalIgnoreCase), "Expected disabled provider message for ai.summarize_all.");
    }

    static async Task TestEntryAiChatDisabledAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "ai.chat",
            payload = new
            {
                prompt = "hello cloud"
            }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);

        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for disabled ai.chat.");
        var data = doc.RootElement.GetProperty("data").GetString() ?? "";
        Assert(data.Contains("disabled", StringComparison.OrdinalIgnoreCase), "Expected disabled provider message for ai.chat.");
    }

    static async Task TestEntryAiEmbedDisabledAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "ai.embed",
            payload = new
            {
                content = "embedding source text"
            }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);

        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for disabled ai.embed.");
        var data = doc.RootElement.GetProperty("data");
        Assert(data.ValueKind == JsonValueKind.Array, "Expected ai.embed response to be a JSON array.");
        Assert(data.GetArrayLength() == 0, "Expected disabled ai.embed to return an empty vector.");
    }

    static async Task TestEntrySpeechDevicesListDisabledAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "speech.devices.list",
            payload = new { }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);
        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for speech.devices.list when disabled.");
        var data = doc.RootElement.GetProperty("data");
        Assert(data.ValueKind == JsonValueKind.Object, "Expected speech.devices.list data to be an object.");
    }

    static async Task TestEntrySpeechTranscribeDisabledAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "speech.transcribe",
            payload = new
            {
                text = "fixture transcript",
                engine = "whisper"
            }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);
        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for speech.transcribe when disabled.");
        var data = doc.RootElement.GetProperty("data");
        Assert(data.ValueKind == JsonValueKind.Object, "Expected speech.transcribe data to be an object.");
        var warning = data.TryGetProperty("Warning", out var warningNode) ? warningNode.GetString() ?? "" : "";
        Assert(warning.Contains("disabled", StringComparison.OrdinalIgnoreCase), "Expected disabled speech warning.");
    }

    static async Task TestPythonSidecarAiServiceJsonLineAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-ai-smoke", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(root);
        var scriptPath = Path.Combine(root, "fake_ai_sidecar.py");
        File.WriteAllText(scriptPath, """
import json, sys
request = json.loads(sys.stdin.readline())
action = request.get("action", "")
print("DEBUG prelude")
if action == "health":
    print(json.dumps({"ok": True, "data": {"provider": "python-sidecar", "healthy": True, "message": "ok"}}))
elif action == "summarize_entry":
    payload = request.get("payload") or {}
    print(json.dumps({"ok": True, "data": "ENTRY::" + str(payload.get("content", ""))}))
elif action == "summarize_all":
    payload = request.get("payload") or {}
    entries = payload.get("entries") or []
    print(json.dumps({"ok": True, "data": "ALL::" + str(len(entries))}))
elif action == "chat":
    payload = request.get("payload") or {}
    print(json.dumps({"ok": True, "data": "CHAT::" + str(payload.get("prompt", ""))}))
elif action == "embed":
    payload = request.get("payload") or {}
    text = str(payload.get("content", ""))
    print(json.dumps({"ok": True, "data": [float(len(text)), 2.5, -1.0]}))
else:
    print(json.dumps({"ok": False, "error": "unknown action"}))
""");

        try
        {
            var config = BuildAiConfig(scriptPath, timeoutMs: 4000);
            IAiService service = new PythonSidecarAiService(config);

            var health = await service.HealthAsync();
            Assert(health.Enabled, "Expected enabled=true for python-sidecar health.");
            Assert(health.Healthy, "Expected healthy=true from fake sidecar health.");

            var one = await service.SummarizeEntryAsync("hello");
            Assert(one == "ENTRY::hello", "Unexpected summarize_entry response.");

            var all = await service.SummarizeAllAsync(["a", "b", "c"]);
            Assert(all == "ALL::3", "Unexpected summarize_all response.");

            var chat = await service.ChatAsync("hello");
            Assert(chat == "CHAT::hello", "Unexpected chat response.");

            var vector = await service.EmbedAsync("hello");
            Assert(vector.Count == 3, "Unexpected embed vector length.");
            Assert(Math.Abs(vector[0] - 5d) < 0.0001d, "Unexpected embed vector first value.");
            Assert(Math.Abs(vector[1] - 2.5d) < 0.0001d, "Unexpected embed vector second value.");
            Assert(Math.Abs(vector[2] + 1.0d) < 0.0001d, "Unexpected embed vector third value.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }

    static async Task TestPythonSidecarAiServiceErrorAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-ai-smoke", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(root);
        var scriptPath = Path.Combine(root, "fake_ai_sidecar_error.py");
        File.WriteAllText(scriptPath, """
import json, sys
_ = json.loads(sys.stdin.readline())
print(json.dumps({"ok": False, "error": "simulated failure"}))
""");

        try
        {
            var config = BuildAiConfig(scriptPath, timeoutMs: 4000);
            IAiService service = new PythonSidecarAiService(config);

            try
            {
                _ = await service.SummarizeEntryAsync("hello");
            }
            catch (InvalidOperationException ex) when (ex.Message.Contains("simulated failure", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            throw new InvalidOperationException("Expected summarize_entry to surface sidecar error.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }

    static async Task TestPythonSidecarSpeechServiceNoDevicesAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-speech-smoke", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(root);
        var scriptPath = Path.Combine(root, "fake_speech_sidecar_nodes.py");
        File.WriteAllText(scriptPath, """
import json, sys
request = json.loads(sys.stdin.readline())
action = request.get("action", "")
if action == "speech.devices.list":
    print(json.dumps({"ok": True, "data": {"devices": [], "warning": "no devices"}}))
elif action == "speech.transcribe":
    payload = request.get("payload") or {}
    print(json.dumps({"ok": True, "data": {"text": payload.get("text", ""), "engine": payload.get("engine", "whisper")}}))
else:
    print(json.dumps({"ok": False, "error": "unknown action"}))
""");

        try
        {
            var config = BuildAiConfig(scriptPath, timeoutMs: 4000);
            ISpeechBridgeService service = new PythonSidecarSpeechService(config);

            var devices = await service.ListDevicesAsync();
            Assert(devices.Devices.Count == 0, "Expected empty devices list.");
            Assert((devices.Warning ?? "").Contains("no devices", StringComparison.OrdinalIgnoreCase), "Expected no-devices warning.");

            var transcript = await service.TranscribeAsync(new SpeechTranscribeRequestDto(Text: "fixture text", Engine: "whisper"));
            Assert(transcript.Text == "fixture text", "Expected passthrough transcript text.");
            Assert(transcript.Engine == "whisper", "Expected passthrough transcript engine.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }

    static async Task TestPythonSidecarSpeechServiceErrorAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-speech-smoke", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(root);
        var scriptPath = Path.Combine(root, "fake_speech_sidecar_error.py");
        File.WriteAllText(scriptPath, """
import json, sys
request = json.loads(sys.stdin.readline())
action = request.get("action", "")
if action == "speech.transcribe":
    print(json.dumps({"ok": False, "error": "engine unavailable"}))
else:
    print(json.dumps({"ok": True, "data": {"devices": []}}))
""");

        try
        {
            var config = BuildAiConfig(scriptPath, timeoutMs: 4000);
            ISpeechBridgeService service = new PythonSidecarSpeechService(config);

            try
            {
                _ = await service.TranscribeAsync(new SpeechTranscribeRequestDto(Text: "fixture", Engine: "faster-whisper"));
            }
            catch (InvalidOperationException ex) when (ex.Message.Contains("engine unavailable", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            throw new InvalidOperationException("Expected speech transcribe to surface sidecar engine error.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }

    static async Task TestPythonSidecarSpeechServiceTimeoutAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-speech-smoke", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(root);
        var scriptPath = Path.Combine(root, "fake_speech_sidecar_timeout.py");
        File.WriteAllText(scriptPath, """
import json, sys, time
request = json.loads(sys.stdin.readline())
payload = request.get("payload") or {}
sleep_ms = int(payload.get("simulate_delay_ms") or 0)
time.sleep(max(0, sleep_ms) / 1000.0)
print(json.dumps({"ok": True, "data": {"text": "", "engine": "whisper"}}))
""");

        try
        {
            var config = BuildAiConfig(scriptPath, timeoutMs: 100);
            ISpeechBridgeService service = new PythonSidecarSpeechService(config);

            try
            {
                _ = await service.TranscribeAsync(new SpeechTranscribeRequestDto(Text: "fixture", SimulateDelayMs: 500));
            }
            catch (TimeoutException)
            {
                return;
            }

            throw new InvalidOperationException("Expected speech transcribe timeout path.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }
}

