using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using GerbilManagerWebAPI.Inbox;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace GerbilManager.Tests;

/// <summary>INBOX-0 HTTP: sync endpoint imports (via a canned reader), status filter + triage,
/// and the Gmail App Password is never returned by GET /api/mail-settings.</summary>
public class RequestEndpointsTests : IClassFixture<ApiFactory>
{
    private readonly ApiFactory _factory;
    public RequestEndpointsTests(ApiFactory factory) => _factory = factory;

    /// <summary>Test double for the IMAP reader: returns whatever the test stages, filtered by UID.</summary>
    private sealed class CannedReader : IGmailMailReader
    {
        public static List<MailSummary> Messages { get; set; } = new();
        public Task<IReadOnlyList<MailSummary>> FetchAsync(MailConnection c, uint sinceUid, CancellationToken ct = default)
            => Task.FromResult((IReadOnlyList<MailSummary>)Messages.Where(m => m.Uid > sinceUid).ToList());
    }

    private HttpClient ClientWithCannedReader() =>
        _factory.WithWebHostBuilder(b => b.ConfigureTestServices(s =>
        {
            s.RemoveAll<IGmailMailReader>();
            s.AddScoped<IGmailMailReader, CannedReader>();
        })).CreateClient();

    [Fact]
    public async Task Sync_imports_then_triage_assigns_contact()
    {
        var client = ClientWithCannedReader();
        CannedReader.Messages = new()
        {
            new("<triage@1>", null, null, null, "interessent@example.de", "Anna", "Suche Rennmäuse", "Hallo!",
                DateTimeOffset.UtcNow, Uid: 1001),
        };

        // configure mail (otherwise sync reports MailNotConfigured)
        Assert.Equal(HttpStatusCode.NoContent, (await client.PutAsJsonAsync("/api/mail-settings",
            new { gmailAddress = "zucht@gmail.com", appPassword = "app-pw-xyz" })).StatusCode);

        var sync = await client.PostAsync("/api/requests/sync", null);
        Assert.Equal(HttpStatusCode.OK, sync.StatusCode);
        Assert.True(JsonDocument.Parse(await sync.Content.ReadAsStringAsync()).RootElement.GetProperty("imported").GetInt32() >= 1);

        // find the imported request via the status filter
        var listed = JsonDocument.Parse(await client.GetStringAsync("/api/requests?filter=status==New&pageSize=100"));
        var item = listed.RootElement.GetProperty("items").EnumerateArray()
            .Single(r => r.GetProperty("gmailMessageId").GetString() == "<triage@1>");
        var reqId = item.GetProperty("id").GetString();

        // a contact + triage
        var contactId = JsonDocument.Parse(await (await client.PostAsJsonAsync("/contacts", new { name = "Anna" }))
            .Content.ReadAsStringAsync()).RootElement.GetProperty("id").GetString();
        var put = await client.PutAsJsonAsync($"/api/requests/{reqId}", new { status = "Assigned", assignedContactId = contactId });
        Assert.Equal(HttpStatusCode.NoContent, put.StatusCode);

        var got = JsonDocument.Parse(await client.GetStringAsync($"/api/requests/{reqId}")).RootElement;
        Assert.Equal("Assigned", got.GetProperty("status").GetString());
        Assert.Equal(contactId, got.GetProperty("assignedContactId").GetString());
    }

    [Fact]
    public async Task MailSettings_stores_password_but_never_returns_it()
    {
        var client = _factory.CreateClient();
        var put = await client.PutAsJsonAsync("/api/mail-settings",
            new { gmailAddress = "sekret@gmail.com", appPassword = "abcd efgh ijkl mnop" });
        Assert.Equal(HttpStatusCode.NoContent, put.StatusCode);

        var json = await client.GetStringAsync("/api/mail-settings");
        Assert.Contains("sekret@gmail.com", json);
        Assert.Contains("\"hasAppPassword\":true", json.Replace(" ", ""));
        Assert.DoesNotContain("abcd", json);                 // the app password must never leave the server
        Assert.DoesNotContain("appPassword", json);
    }
}
