using System.Runtime.CompilerServices;
using System.Text;
using ChatService.Domain.Contracts;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace ChatService.Infrastructure.Services;

/// <summary>
/// EN: AI service implementation using OpenAI API.
/// VI: Implementation AI service sử dụng OpenAI API.
/// </summary>
public class AIService : IAIService
{
    private readonly HttpClient _httpClient;
    private readonly AIServiceOptions _options;
    private readonly ILogger<AIService> _logger;

    public AIService(
        HttpClient httpClient,
        IOptions<AIServiceOptions> options,
        ILogger<AIService> logger)
    {
        _httpClient = httpClient;
        _options = options.Value;
        _logger = logger;

        // EN: Configure base address for OpenAI API
        // VI: Cấu hình base address cho OpenAI API
        _httpClient.BaseAddress = new Uri("https://api.openai.com/v1/");
        _httpClient.DefaultRequestHeaders.Add("Accept", "text/event-stream");
    }

    /// <inheritdoc/>
    public async IAsyncEnumerable<string> StreamResponseAsync(
        string prompt,
        IEnumerable<ChatMessage> conversationHistory,
        [EnumeratorCancellation] CancellationToken ct = default)
    {
        _logger.LogInformation(
            "Starting AI stream response for prompt: {PromptPreview}...",
            prompt.Length > 50 ? prompt[..50] : prompt);

        var messages = BuildMessagesPayload(prompt, conversationHistory);
        var requestBody = new
        {
            model = _options.Model,
            messages = messages,
            max_tokens = _options.MaxTokens,
            temperature = _options.Temperature,
            stream = true
        };

        var jsonContent = System.Text.Json.JsonSerializer.Serialize(requestBody);
        using var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions")
        {
            Content = new StringContent(jsonContent, Encoding.UTF8, "application/json")
        };

        using var response = await _httpClient.SendAsync(
            request,
            HttpCompletionOption.ResponseHeadersRead,
            ct);

        response.EnsureSuccessStatusCode();

        await using var stream = await response.Content.ReadAsStreamAsync(ct);
        using var reader = new StreamReader(stream);

        string? line;
        while ((line = await reader.ReadLineAsync(ct)) != null && !ct.IsCancellationRequested)
        {
            if (string.IsNullOrEmpty(line)) continue;
            if (!line.StartsWith("data: ")) continue;

            var data = line[6..];
            if (data == "[DONE]") break;

            // EN: Parse SSE data and extract content delta
            // VI: Parse SSE data và extract content delta
            var chunk = ParseStreamChunk(data);
            if (!string.IsNullOrEmpty(chunk))
            {
                yield return chunk;
            }
        }

        _logger.LogInformation("AI stream response completed");
    }

    /// <inheritdoc/>
    public async Task<string> GetResponseAsync(
        string prompt,
        IEnumerable<ChatMessage> conversationHistory,
        CancellationToken ct = default)
    {
        var sb = new StringBuilder();
        await foreach (var chunk in StreamResponseAsync(prompt, conversationHistory, ct))
        {
            sb.Append(chunk);
        }
        return sb.ToString();
    }

    /// <inheritdoc/>
    public async Task<bool> IsAvailableAsync(CancellationToken ct = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, "models");
            using var response = await _httpClient.SendAsync(request, ct);
            return response.IsSuccessStatusCode;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "AI service availability check failed");
            return false;
        }
    }

    /// <summary>
    /// EN: Build the messages array for OpenAI API.
    /// VI: Xây dựng mảng messages cho OpenAI API.
    /// </summary>
    private List<object> BuildMessagesPayload(
        string prompt,
        IEnumerable<ChatMessage> conversationHistory)
    {
        var messages = new List<object>
        {
            new { role = "system", content = _options.SystemPrompt }
        };

        // EN: Add conversation history (limit to MaxHistoryMessages)
        // VI: Thêm lịch sử hội thoại (giới hạn MaxHistoryMessages)
        var history = conversationHistory
            .OrderByDescending(m => m.Timestamp)
            .Take(_options.MaxHistoryMessages)
            .Reverse()
            .ToList();

        foreach (var msg in history)
        {
            messages.Add(new { role = msg.Role, content = msg.Content });
        }

        // EN: Add current user prompt
        // VI: Thêm prompt hiện tại của user
        messages.Add(new { role = "user", content = prompt });

        return messages;
    }

    /// <summary>
    /// EN: Parse a streaming chunk from OpenAI SSE format.
    /// VI: Parse một chunk từ định dạng SSE của OpenAI.
    /// </summary>
    private static string? ParseStreamChunk(string data)
    {
        try
        {
            using var doc = System.Text.Json.JsonDocument.Parse(data);
            var choices = doc.RootElement.GetProperty("choices");
            if (choices.GetArrayLength() == 0) return null;

            var delta = choices[0].GetProperty("delta");
            if (delta.TryGetProperty("content", out var content))
            {
                return content.GetString();
            }
        }
        catch
        {
            // EN: Silently ignore parsing errors for malformed chunks
            // VI: Bỏ qua lỗi parsing cho các chunk không hợp lệ
        }
        return null;
    }
}

/// <summary>
/// EN: Null implementation of AI service for when AI is disabled.
/// VI: Null implementation của AI service khi AI bị tắt.
/// </summary>
public class NullAIService : IAIService
{
    public async IAsyncEnumerable<string> StreamResponseAsync(
        string prompt,
        IEnumerable<ChatMessage> conversationHistory,
        [EnumeratorCancellation] CancellationToken ct = default)
    {
        yield return "AI service is not configured. Please set up OPENAI_API_KEY.";
        await Task.CompletedTask;
    }

    public Task<string> GetResponseAsync(
        string prompt,
        IEnumerable<ChatMessage> conversationHistory,
        CancellationToken ct = default)
        => Task.FromResult("AI service is not configured. Please set up OPENAI_API_KEY.");

    public Task<bool> IsAvailableAsync(CancellationToken ct = default)
        => Task.FromResult(false);
}
