using AyCode.Core.Loggers;
using Mango.Nop.Core.Loggers;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Services.Configuration;
using System.Text;
using System.Text.Json;

#nullable enable
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
    public class CerebrasAPIService : IAIAPIService
    {
        private readonly ILogger _logger;

        private readonly ISettingService _settingService;
        private readonly FruitBankSettings _fruitBankSettings;
        private readonly HttpClient _httpClient;
        private static Action<string, string>? _callback;
        private static Action<string>? _onComplete;
        private static Action<string, string>? _onError;

        private const string CerebrasEndpoint = "https://api.cerebras.ai/v1/chat/completions";

        public CerebrasAPIService(ISettingService settingService, HttpClient httpClient, IEnumerable<IAcLogWriterBase> logWriters)
        {
            _logger = new Logger<CerebrasAPIService>(logWriters.ToArray());

            _settingService = settingService;
            _fruitBankSettings = _settingService.LoadSetting<FruitBankSettings>();

            _httpClient = httpClient;
            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {GetApiKey()}");
        }

        public string GetApiKey() => _fruitBankSettings.ApiKey; //_configuration?.GetSection("Cerebras")?.GetValue<string>("ApiKey") ?? string.Empty;

        public string GetModelName() => _fruitBankSettings.ModelName; //_configuration?.GetSection("Cerebras")?.GetValue<string>("Model") ?? string.Empty;

        public void RegisterCallback(Action<string, string> callback, Action<string> onCompleteCallback, Action<string, string> onErrorCallback)
        {
            _callback = callback;
            _onComplete = onCompleteCallback;
            _onError = onErrorCallback;
        }


        public async Task<string?> GetSimpleResponseAsync(string systemMessage, string userMessage, string? assistantMessage = null)
        {
            var modelName = GetModelName();

            var requestBody = new CerebrasAIChatRequest
            {
                Model = modelName,
                Temperature = 0.2,
                Messages = string.IsNullOrEmpty(assistantMessage)
                    ?
                    [
                        new AIChatMessage { Role = "system", Content = systemMessage },
                        new AIChatMessage { Role = "user", Content = userMessage }
                    ]
                    :
                    [
                        new AIChatMessage { Role = "system", Content = systemMessage },
                        new AIChatMessage { Role = "assistant", Content = assistantMessage },
                        new AIChatMessage { Role = "user", Content = userMessage }
                    ],
                Stream = false
            };

            var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });

            var requestContent = new StringContent(requestJson, Encoding.UTF8, "application/json");

            using var response = await _httpClient.PostAsync(CerebrasEndpoint, requestContent);
            response.EnsureSuccessStatusCode();

            await using var responseStream = await response.Content.ReadAsStreamAsync();
            using var document = await JsonDocument.ParseAsync(responseStream);
            var inputTokens = document.RootElement.GetProperty("usage").GetProperty("prompt_tokens").GetInt32();
            var outputTokens = document.RootElement.GetProperty("usage").GetProperty("completion_tokens").GetInt32();
            var sum = inputTokens + outputTokens;

            _logger.Info($"USAGE STATS - Tokens: {inputTokens.ToString()} + {outputTokens.ToString()} = {sum.ToString()}");

            return document.RootElement
                           .GetProperty("choices")[0]
                           .GetProperty("message")
                           .GetProperty("content")
                           .GetString() ?? "No response";
        }


        public async Task<string> GetStreamedResponseAsync(string sessionId, string systemMessage, string userMessage, string? assistantMessage = null)
        {
            var modelName = GetModelName();

            var requestBody = new CerebrasAIChatRequest
            {
                Model = modelName,
                Temperature = 0.2,
                Messages = assistantMessage == null
                    ?
                    [
                        new AIChatMessage { Role = "system", Content = systemMessage },
                        new AIChatMessage { Role = "user", Content = userMessage }
                    ]
                    :
                    [
                        new AIChatMessage { Role = "system", Content = systemMessage },
                        new AIChatMessage { Role = "assistant", Content = assistantMessage },
                        new AIChatMessage { Role = "user", Content = userMessage }
                    ],
                Stream = true
            };

            var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });

            var requestContent = new StringContent(requestJson, Encoding.UTF8, "application/json");
            using var httpRequest = new HttpRequestMessage(HttpMethod.Post, CerebrasEndpoint);
            httpRequest.Content = requestContent;

            using var response = await _httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
            response.EnsureSuccessStatusCode();

            var stringBuilder = new StringBuilder();
            await using var responseStream = await response.Content.ReadAsStreamAsync();
            using var reader = new StreamReader(responseStream);

            try
            {
                while (!reader.EndOfStream)
                {
                    var line = await reader.ReadLineAsync();

                    if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data: ")) continue;

                    var jsonResponse = line.Substring(6);

                    // ✅ Detect explicit end of stream
                    if (jsonResponse == "[DONE]")
                    {
                        _onComplete?.Invoke(sessionId); // Optional: notify stream end
                        break;
                    }

                    try
                    {
                        using var jsonDoc = JsonDocument.Parse(jsonResponse);

                        if (jsonDoc.RootElement.TryGetProperty("choices", out var choices) &&
                            choices[0].TryGetProperty("delta", out var delta) &&
                            delta.TryGetProperty("content", out var contentElement))
                        {
                            var content = contentElement.GetString();

                            if (!string.IsNullOrEmpty(content))
                            {
                                stringBuilder.Append(content);
                                _callback?.Invoke(sessionId, stringBuilder.ToString());
                            }
                        }
                    }
                    catch (JsonException ex)
                    {
                        _onError?.Invoke(sessionId, $"Malformed JSON: {ex.Message}");
                        break; // Optionally stop stream
                    }
                }

                // ✅ Check for unexpected end (in case no [DONE])
                if (reader.EndOfStream && !stringBuilder.ToString().EndsWith("[DONE]"))
                {
                    _onError?.Invoke(sessionId, "Unexpected end of stream");
                }
            }
            catch (Exception ex)
            {
                _onError?.Invoke(sessionId, $"Exception: {ex.Message}");
            }


            return stringBuilder.ToString();
        }
    }
}

