using System.Text.Json;
using Journal.Core.Dtos;
using Journal.Core.Models;
using Journal.Core.Services.Sidecar;

namespace Journal.Core.Services.Ai;

public sealed class PythonSidecarAiService : IAiService
{
    private readonly PythonSidecarClient _client;

    public PythonSidecarAiService(JournalConfig config)
    {
        if (string.IsNullOrWhiteSpace(config.PythonAiSidecarPath))
            throw new ArgumentException("Python AI sidecar path is required.");
        if (!File.Exists(config.PythonAiSidecarPath))
            throw new FileNotFoundException($"Python AI sidecar not found: {config.PythonAiSidecarPath}");
        _client = new PythonSidecarClient(config);
    }

    public async Task<AiHealthDto> HealthAsync(CancellationToken cancellationToken = default)
    {
        var data = await _client.SendAsync("health", payload: new { }, cancellationToken);
        if (data is not JsonElement payload || payload.ValueKind != JsonValueKind.Object)
            return new AiHealthDto("python-sidecar", Enabled: true, Healthy: true, Message: "ok");

        var provider = payload.TryGetProperty("provider", out var providerNode)
            ? providerNode.GetString() ?? "python-sidecar"
            : "python-sidecar";
        var message = payload.TryGetProperty("message", out var messageNode)
            ? messageNode.GetString() ?? "ok"
            : "ok";
        var healthy = !payload.TryGetProperty("healthy", out var healthyNode) ||
                      healthyNode.ValueKind is JsonValueKind.True ||
                      (healthyNode.ValueKind is not JsonValueKind.False);
        return new AiHealthDto(provider, Enabled: true, Healthy: healthy, Message: message);
    }

    public async Task<string> SummarizeEntryAsync(string content, string? fileStem = null, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(content))
            throw new ArgumentException("Entry content is required.", nameof(content));

        var data = await _client.SendAsync("summarize_entry", new { content, file_stem = fileStem }, cancellationToken);
        return data?.GetString() ?? "";
    }

    public async Task<string> SummarizeAllAsync(IReadOnlyList<string> entries, CancellationToken cancellationToken = default)
    {
        entries ??= [];
        var data = await _client.SendAsync("summarize_all", new { entries }, cancellationToken);
        return data?.GetString() ?? "";
    }

    public async Task<string> ChatAsync(string prompt, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(prompt))
            throw new ArgumentException("Prompt is required.", nameof(prompt));

        var data = await _client.SendAsync("chat", new { prompt }, cancellationToken);
        return data?.GetString() ?? "";
    }

    public Task<string> ChatWithHistoryAsync(IReadOnlyList<(string Role, string Text)> history,
        string prompt, CancellationToken cancellationToken = default)
    {
        // Python sidecar does not support multi-turn — fall back to single-turn
        return ChatAsync(prompt, cancellationToken);
    }

    public async Task<IReadOnlyList<double>> EmbedAsync(string content, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(content))
            throw new ArgumentException("Content is required.", nameof(content));

        var data = await _client.SendAsync("embed", new { content }, cancellationToken);
        if (data is null || data.Value.ValueKind == JsonValueKind.Null)
            return [];

        if (data.Value.ValueKind != JsonValueKind.Array)
            throw new InvalidOperationException("Python AI sidecar embed response must be a numeric array.");

        var values = new List<double>();
        foreach (var item in data.Value.EnumerateArray())
        {
            if (item.ValueKind != JsonValueKind.Number)
                throw new InvalidOperationException("Python AI sidecar embed response contains a non-numeric value.");
            values.Add(item.GetDouble());
        }

        return values;
    }
}
