using System.Text;
using System.Text.Json;
using GerbilManagerWebAPI.Ai;
using GerbilManagerWebAPI.SaleAd;
using Microsoft.Extensions.Options;

namespace GerbilManagerWebAPI.Names
{
    /// <summary>
    /// FEAT-NAMEGEN: generates meaningful gerbil name suggestions via Gemini
    /// (the same OpenAiChatClient used by sale-ads and reply-drafts).
    /// Returns NotConfigured when the AI section is missing — callers map to 503.
    /// </summary>
    public sealed class NameSuggestionService(HttpClient http, IOptions<AiOptions> options)
    {
        private readonly OpenAiChatClient _client = new(http, options);

        private static readonly JsonSerializerOptions JsonOpts = new()
        {
            PropertyNameCaseInsensitive = true,
        };

        public async Task<NameSuggestionResult> SuggestAsync(
            string? letter, string? gender, string? usages, int count,
            CancellationToken ct = default)
        {
            var aiResult = await _client.CompleteAsync(
                BuildSystemPrompt(),
                BuildUserPrompt(letter, gender, usages, count),
                ct);

            if (aiResult.Status == AiCallStatus.NotConfigured)
                return new NameSuggestionResult(NameSuggestionStatus.NotConfigured, null, aiResult.Error);
            if (aiResult.Status != AiCallStatus.Ok || aiResult.Text is null)
                return new NameSuggestionResult(NameSuggestionStatus.UpstreamError, null, aiResult.Error);

            var suggestions = ParseSuggestions(aiResult.Text);
            return suggestions is null
                ? new NameSuggestionResult(NameSuggestionStatus.UpstreamError, null, "Ungültiges JSON in KI-Antwort.")
                : new NameSuggestionResult(NameSuggestionStatus.Ok, suggestions, null);
        }

        internal static string BuildSystemPrompt() =>
            "Du bist ein Helfer für Rennmaus-Züchter. " +
            "Antworte IMMER mit einem reinen JSON-Array — KEINE Markdown-Code-Blöcke, " +
            "KEINE Erklärungen, KEIN Text außerhalb des Arrays. " +
            "Jedes Element hat genau die Felder: name, meaning, origin (alle Strings, alle auf Deutsch).";

        internal static string BuildUserPrompt(string? letter, string? gender, string? usages, int count)
        {
            var sb = new StringBuilder();
            sb.Append($"Schlage {count} Rennmaus-Namen vor");
            if (!string.IsNullOrWhiteSpace(letter))
                sb.Append($" die mit dem Buchstaben \"{letter.ToUpperInvariant()}\" beginnen");
            if (!string.IsNullOrWhiteSpace(gender) &&
                !gender.Equals("any", StringComparison.OrdinalIgnoreCase))
                sb.Append($", passend für {(gender.Equals("female", StringComparison.OrdinalIgnoreCase) ? "weibliche" : "männliche")} Tiere");
            if (!string.IsNullOrWhiteSpace(usages))
                sb.Append($", aus den Kulturkreisen: {usages}");
            sb.Append(". Jeder Name muss eine echte etymologische Bedeutung und Herkunft haben ");
            sb.Append("(keine erfundenen oder zufälligen Namen). ");
            sb.Append($"Antworte mit genau {count} Elementen als reines JSON-Array: ");
            sb.Append("[{\"name\":\"...\",\"meaning\":\"...\",\"origin\":\"...\"}]");
            return sb.ToString();
        }

        /// <summary>
        /// Strips optional markdown fences (```json ... ```) Gemini sometimes wraps around
        /// its JSON output, then deserialises the array.
        /// </summary>
        internal static List<NameSuggestion>? ParseSuggestions(string raw)
        {
            var text = raw.Trim();

            // Strip ```json ... ``` or ``` ... ``` fences.
            if (text.StartsWith("```", StringComparison.Ordinal))
            {
                var firstNewline = text.IndexOf('\n');
                if (firstNewline >= 0) text = text[(firstNewline + 1)..];
                if (text.EndsWith("```", StringComparison.Ordinal))
                    text = text[..^3].TrimEnd();
            }

            // Find the JSON array bounds defensively.
            var start = text.IndexOf('[');
            var end = text.LastIndexOf(']');
            if (start < 0 || end <= start) return null;
            text = text[start..(end + 1)];

            try
            {
                return JsonSerializer.Deserialize<List<NameSuggestion>>(text, JsonOpts);
            }
            catch (JsonException)
            {
                return null;
            }
        }
    }

    public enum NameSuggestionStatus { Ok, NotConfigured, UpstreamError }

    public sealed record NameSuggestionResult(
        NameSuggestionStatus Status,
        List<NameSuggestion>? Suggestions,
        string? Error);
}
