using System.Net;
using System.Text;
using System.Text.Json;
using GerbilManagerWebAPI.Names;
using GerbilManagerWebAPI.SaleAd;
using Microsoft.Extensions.Options;

namespace GerbilManager.Tests
{
    /// <summary>
    /// FEAT-NAMEGEN: NameSuggestionService — prompt assembly, JSON parse (incl. Markdown
    /// fence strip), 503-not-configured path, upstream-error path.
    /// </summary>
    public class NameSuggestionTests
    {
        // ── Prompt assembly ───────────────────────────────────────────────────

        [Fact]
        public void SystemPrompt_verlangt_reines_JSON_ohne_Erklärungen()
        {
            var prompt = NameSuggestionService.BuildSystemPrompt();
            Assert.Contains("reinen JSON-Array", prompt);
            Assert.Contains("KEINE Markdown-Code-Blöcke", prompt);
            Assert.Contains("name", prompt);
            Assert.Contains("meaning", prompt);
            Assert.Contains("origin", prompt);
        }

        [Fact]
        public void UserPrompt_enthält_Anzahl_und_Anfangsbuchstaben()
        {
            var prompt = NameSuggestionService.BuildUserPrompt("A", "female", "norn,mythg", 6);
            Assert.Contains("6", prompt);
            Assert.Contains("\"A\"", prompt);
            Assert.Contains("weibliche", prompt);
            Assert.Contains("norn,mythg", prompt);
        }

        [Fact]
        public void UserPrompt_ohne_optionale_Parameter_ist_gültig()
        {
            var prompt = NameSuggestionService.BuildUserPrompt(null, null, null, 5);
            Assert.Contains("5", prompt);
            Assert.DoesNotContain("Buchstaben", prompt);
            Assert.DoesNotContain("weibliche", prompt);
            Assert.DoesNotContain("männliche", prompt);
        }

        [Fact]
        public void UserPrompt_gender_any_wird_nicht_im_Prompt_erwähnt()
        {
            var prompt = NameSuggestionService.BuildUserPrompt(null, "any", null, 3);
            Assert.DoesNotContain("weibliche", prompt);
            Assert.DoesNotContain("männliche", prompt);
        }

        // ── JSON parsing ──────────────────────────────────────────────────────

        [Fact]
        public void ParseSuggestions_verarbeitet_reines_JSON()
        {
            var json = """[{"name":"Astrid","meaning":"göttliche Stärke","origin":"Altnordisch"}]""";
            var result = NameSuggestionService.ParseSuggestions(json);
            Assert.NotNull(result);
            Assert.Single(result);
            Assert.Equal("Astrid", result[0].Name);
            Assert.Equal("göttliche Stärke", result[0].Meaning);
            Assert.Equal("Altnordisch", result[0].Origin);
        }

        [Fact]
        public void ParseSuggestions_strippt_json_Markdown_Fence()
        {
            var fenced = "```json\n[{\"name\":\"Aiko\",\"meaning\":\"kleine Geliebte\",\"origin\":\"Japanisch\"}]\n```";
            var result = NameSuggestionService.ParseSuggestions(fenced);
            Assert.NotNull(result);
            Assert.Equal("Aiko", result![0].Name);
        }

        [Fact]
        public void ParseSuggestions_strippt_generische_Markdown_Fence()
        {
            var fenced = "```\n[{\"name\":\"Luna\",\"meaning\":\"Mond\",\"origin\":\"Lateinisch\"}]\n```";
            var result = NameSuggestionService.ParseSuggestions(fenced);
            Assert.NotNull(result);
            Assert.Equal("Luna", result![0].Name);
        }

        [Fact]
        public void ParseSuggestions_toleriert_umgebenden_Text_vor_Array()
        {
            var messy = "Hier sind die Namen:\n[{\"name\":\"Sol\",\"meaning\":\"Sonne\",\"origin\":\"Nordisch\"}]\nHoffnungslos.";
            var result = NameSuggestionService.ParseSuggestions(messy);
            Assert.NotNull(result);
            Assert.Equal("Sol", result![0].Name);
        }

        [Fact]
        public void ParseSuggestions_gibt_null_zurück_bei_ungültigem_JSON()
        {
            Assert.Null(NameSuggestionService.ParseSuggestions("kein json"));
            Assert.Null(NameSuggestionService.ParseSuggestions("{\"name\":\"X\"}"));
            Assert.Null(NameSuggestionService.ParseSuggestions(""));
        }

        // ── 503: nicht konfiguriert ───────────────────────────────────────────

        [Theory]
        [InlineData(null, "key", "model")]
        [InlineData("https://api.example.com/v1", null, "model")]
        [InlineData("https://api.example.com/v1", "key", null)]
        [InlineData(null, null, null)]
        public async Task SuggestAsync_gibt_NotConfigured_wenn_AI_Key_fehlt(
            string? baseUrl, string? apiKey, string? model)
        {
            var service = CreateService(baseUrl, apiKey, model,
                new StubHandler(_ => throw new InvalidOperationException("darf nicht aufgerufen werden")));

            var result = await service.SuggestAsync(null, null, null, 5);

            Assert.Equal(NameSuggestionStatus.NotConfigured, result.Status);
            Assert.Null(result.Suggestions);
        }

        // ── Gemini-Antwort wird geparst ───────────────────────────────────────

        [Fact]
        public async Task SuggestAsync_parst_valide_JSON_Antwort()
        {
            var payload = """[{"name":"Astrid","meaning":"göttliche Stärke","origin":"Altnordisch"},{"name":"Aiko","meaning":"kleine Geliebte","origin":"Japanisch"}]""";
            var handler = new StubHandler(_ => Canned(payload));
            var service = CreateService("https://generativelanguage.googleapis.com/v1beta/openai", "k", "gemini-2.0-flash", handler);

            var result = await service.SuggestAsync("A", "female", "norn,japa", 2);

            Assert.Equal(NameSuggestionStatus.Ok, result.Status);
            Assert.NotNull(result.Suggestions);
            Assert.Equal(2, result.Suggestions!.Count);
            Assert.Equal("Astrid", result.Suggestions[0].Name);
        }

        [Fact]
        public async Task SuggestAsync_parst_JSON_in_Markdown_Fence()
        {
            var fenced = "```json\n[{\"name\":\"Luna\",\"meaning\":\"Mond\",\"origin\":\"Lateinisch\"}]\n```";
            var handler = new StubHandler(_ => Canned(fenced));
            var service = CreateService("https://api.example.com/v1", "k", "m", handler);

            var result = await service.SuggestAsync(null, null, null, 1);

            Assert.Equal(NameSuggestionStatus.Ok, result.Status);
            Assert.Equal("Luna", result.Suggestions![0].Name);
        }

        [Fact]
        public async Task SuggestAsync_gibt_UpstreamError_bei_ungültigem_JSON()
        {
            var handler = new StubHandler(_ => Canned("das ist kein json"));
            var service = CreateService("https://api.example.com/v1", "k", "m", handler);

            var result = await service.SuggestAsync(null, null, null, 3);

            Assert.Equal(NameSuggestionStatus.UpstreamError, result.Status);
            Assert.Null(result.Suggestions);
        }

        [Fact]
        public async Task SuggestAsync_gibt_UpstreamError_bei_HTTP_Fehler()
        {
            var handler = new StubHandler(_ => new HttpResponseMessage(HttpStatusCode.TooManyRequests));
            var service = CreateService("https://api.example.com/v1", "k", "m", handler);

            var result = await service.SuggestAsync(null, null, null, 3);

            Assert.Equal(NameSuggestionStatus.UpstreamError, result.Status);
        }

        // ── Helfer ────────────────────────────────────────────────────────────

        private static NameSuggestionService CreateService(
            string? baseUrl, string? apiKey, string? model, StubHandler handler)
        {
            var options = Options.Create(new AiOptions { BaseUrl = baseUrl, ApiKey = apiKey, Model = model });
            return new NameSuggestionService(new HttpClient(handler), options);
        }

        private static HttpResponseMessage Canned(string content)
        {
            var completion = new
            {
                choices = new[] { new { message = new { role = "assistant", content } } },
            };
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(JsonSerializer.Serialize(completion),
                    Encoding.UTF8, "application/json"),
            };
        }

        private sealed class StubHandler(Func<HttpRequestMessage, HttpResponseMessage> respond)
            : HttpMessageHandler
        {
            protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, CancellationToken cancellationToken)
                => Task.FromResult(respond(request));
        }
    }
}
