using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using GerbilManagerWebAPI.Inbox;
using GerbilManagerWebAPI.Models;
using GerbilManagerWebAPI.SaleAd;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace GerbilManager.Tests
{
    /// <summary>
    /// INBOX-2: KI-Antwortentwurf — Prompt-Assembly (inkl. Datenminimierung +
    /// Zitat-/Signatur-Stripping), 503-unkonfiguriert, und der Endpoint-Round-Trip
    /// gegen einen Stub-Anbieter (Entwurf wird gespeichert UND zurückgegeben).
    /// </summary>
    public class InboxDraftTests
    {
        private static Request SampleRequest() => new()
        {
            Id = Guid.NewGuid(),
            GmailMessageId = $"<{Guid.NewGuid():N}@mail.example>",
            FromAddress = "anna.musterfrau@example.de",
            FromName = "Anna Musterfrau",
            Subject = "Anfrage: zwei Weibchen?",
            BodyText = "Hallo! Habt ihr aktuell zwei junge Weibchen zur Abgabe?\nViele Grüße, Anna",
            ReceivedAt = DateTimeOffset.UtcNow,
        };

        // ── Zitat-/Signatur-Stripping ──────────────────────────────────────

        [Fact]
        public void StripQuotedText_entfernt_Zitate_Signatur_und_Outlook_Header()
        {
            var body = string.Join('\n',
                "Hallo, habt ihr Tiere abzugeben?",
                "> alte zitierte Zeile",
                "Von: jemand@example.de",
                "Danke!",
                "-- ",
                "Anna Musterfrau",
                "Musterweg 1");
            var stripped = DraftReplyService.StripQuotedText(body);

            Assert.Contains("Hallo, habt ihr Tiere abzugeben?", stripped);
            Assert.Contains("Danke!", stripped);
            Assert.DoesNotContain("alte zitierte Zeile", stripped);
            Assert.DoesNotContain("jemand@example.de", stripped);
            Assert.DoesNotContain("Musterweg 1", stripped); // Signatur weg
        }

        [Fact]
        public void StripQuotedText_schneidet_beim_Gmail_Zitat_Intro_ab()
        {
            var body = "Meine Frage steht oben.\nAm 05.06.2026 um 10:00 schrieb Zucht der kleinen Chaoten:\n> früherer Verlauf";
            var stripped = DraftReplyService.StripQuotedText(body);

            Assert.Equal("Meine Frage steht oben.", stripped);
        }

        // ── Prompt-Assembly + Datenminimierung ─────────────────────────────

        [Fact]
        public void UserPrompt_enthält_Anfrage_und_Abgabeliste_aber_keine_Mailadresse()
        {
            var prompt = DraftReplyService.BuildUserPrompt(SampleRequest(),
                [new DraftReplyService.ForSaleAnimal("Frieda", "Gold"),
                 new DraftReplyService.ForSaleAnimal("Fine", null)]);

            Assert.Contains("Von: Anna Musterfrau", prompt);
            Assert.Contains("Betreff: Anfrage: zwei Weibchen?", prompt);
            Assert.Contains("zwei junge Weibchen zur Abgabe", prompt);
            Assert.Contains("- Frieda (Gold)", prompt);
            Assert.Contains("- Fine", prompt);
            // Datenminimierung: die E-Mail-Adresse der Absenderin geht NICHT zum Anbieter
            Assert.DoesNotContain("anna.musterfrau@example.de", prompt);
        }

        [Fact]
        public void UserPrompt_ohne_Abgabetiere_enthält_keine_Liste()
        {
            var prompt = DraftReplyService.BuildUserPrompt(SampleRequest(), []);
            Assert.DoesNotContain("abzugebende Tiere", prompt);
        }

        [Fact]
        public void SystemPrompt_verlangt_Entwurf_ohne_Preise_und_ohne_erfundene_Fakten()
        {
            var prompt = DraftReplyService.BuildSystemPrompt();
            Assert.Contains("ENTWERFEN", prompt);
            Assert.Contains("KEINE Fakten erfinden", prompt);
            Assert.Contains("Keine Preise", prompt);
            Assert.Contains("keine Adressen", prompt);
        }

        // ── 503: unkonfiguriert (Service-Ebene) ────────────────────────────

        [Fact]
        public async Task DraftAsync_meldet_NotConfigured_ohne_AI_Konfiguration()
        {
            var service = new DraftReplyService(
                new HttpClient(new StubHandler(_ => throw new InvalidOperationException("darf nicht aufgerufen werden"))),
                Options.Create(new AiOptions()));

            var result = await service.DraftAsync(SampleRequest(), []);

            Assert.Equal(GerbilManagerWebAPI.Ai.AiCallStatus.NotConfigured, result.Status);
        }

        // ── Endpoint-Round-Trip ────────────────────────────────────────────

        [Fact]
        public async Task Draft_Endpoint_meldet_503_AiKeyMissing_wenn_unkonfiguriert()
        {
            using var factory = new ApiFactory();
            var id = await SeedRequestAsync(factory);

            var response = await factory.CreateClient().PostAsync($"/api/requests/{id}/draft", null);

            Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
            Assert.Contains("AiKeyMissing", await response.Content.ReadAsStringAsync());
        }

        [Fact]
        public async Task Draft_Endpoint_speichert_und_liefert_den_Entwurf_des_Stubs()
        {
            const string canned = "Hallo Anna,\n\nschön, dass du fragst — aktuell suchen Frieda und Fine ein Zuhause.\n\nHerzliche Grüße";
            var stub = new StubHandler(_ => Canned(canned));
            using var factory = new ApiFactory
            {
                ConfigureTestServices = services =>
                {
                    services.PostConfigure<AiOptions>(o =>
                    {
                        o.BaseUrl = "https://api.example.com/v1";
                        o.ApiKey = "test";
                        o.Model = "test-model";
                    });
                    services.AddHttpClient<DraftReplyService>()
                        .ConfigurePrimaryHttpMessageHandler(() => stub);
                },
            };

            var id = await SeedRequestAsync(factory, alsoForSaleGerbil: true);
            var client = factory.CreateClient();

            var response = await client.PostAsync($"/api/requests/{id}/draft", null);
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            using var dto = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
            Assert.Equal(canned, dto.RootElement.GetProperty("draftReply").GetString());

            // Persistiert: erneutes GET liefert den Entwurf
            var again = await client.GetFromJsonAsync<JsonElement>($"/api/requests/{id}");
            Assert.Equal(canned, again.GetProperty("draftReply").GetString());

            // Wire-Privacy: Anfragetext + ForSale-Tier gingen zum Anbieter, die
            // Mailadresse der Absenderin NICHT.
            Assert.NotNull(stub.LastRequestBody);
            Assert.Contains("zwei junge Weibchen", stub.LastRequestBody);
            Assert.Contains("Aki", stub.LastRequestBody);
            Assert.DoesNotContain("anna.musterfrau@example.de", stub.LastRequestBody);
        }

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

        private static async Task<Guid> SeedRequestAsync(ApiFactory factory, bool alsoForSaleGerbil = false)
        {
            using var scope = factory.Services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
            var request = SampleRequest();
            db.Add(request);
            if (alsoForSaleGerbil)
            {
                db.Add(new Gerbil
                {
                    Id = Guid.NewGuid(),
                    Name = "Aki",
                    Gender = Gender.female,
                    Status = GerbilStatus.ForSale,
                });
            }
            await db.SaveChangesAsync();
            return request.Id;
        }

        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"),
            };
        }

        /// <summary>HttpMessageHandler-Stub (Variante von SaleAdTests, hier mit Body-Capture).</summary>
        private sealed class StubHandler(Func<HttpRequestMessage, HttpResponseMessage> respond)
            : HttpMessageHandler
        {
            public string? LastRequestBody { get; private set; }

            protected override async Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, CancellationToken cancellationToken)
            {
                LastRequestBody = request.Content is null
                    ? null
                    : await request.Content.ReadAsStringAsync(cancellationToken);
                return respond(request);
            }
        }
    }
}
