using GerbilManagerWebAPI.Inbox;
using GerbilManagerWebAPI.Models;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.EntityFrameworkCore;

namespace GerbilManager.Tests;

/// <summary>INBOX-0: sync dedup + idempotency + config-guard, against an in-memory DB and a
/// fake IGmailMailReader (the live Gmail/MailKit path is gated on Julian's App Password).</summary>
public class RequestSyncServiceTests
{
    private static ApplicationContext NewDb()
    {
        var opts = new DbContextOptionsBuilder<ApplicationContext>()
            .UseInMemoryDatabase("inbox-" + Guid.NewGuid().ToString("N")).Options;
        var db = new ApplicationContext(opts);
        db.Database.EnsureCreated();   // seeds the MailSettings singleton
        return db;
    }

    private sealed class FakeReader : IGmailMailReader
    {
        public List<MailSummary> Canned { get; set; } = new();
        public Task<IReadOnlyList<MailSummary>> FetchAsync(MailConnection c, uint sinceUid, CancellationToken ct = default)
            => Task.FromResult((IReadOnlyList<MailSummary>)Canned.Where(m => m.Uid > sinceUid).ToList());
    }

    private static MailSummary M(uint uid, string msgId, string from) =>
        new(msgId, ThreadId: null, InReplyTo: null, References: null,
            FromAddress: from, FromName: null, Subject: "Anfrage", BodyText: "Hallo",
            ReceivedAt: DateTimeOffset.UtcNow, Uid: uid);

    private static async Task<(ApplicationContext db, RequestSyncService sync, FakeReader reader)> SetupAsync(bool configured = true)
    {
        var db = NewDb();
        var settings = new MailSettingsService(db, new EphemeralDataProtectionProvider());
        var s = await settings.GetAsync();
        if (configured)
        {
            s.GmailAddress = "zucht@gmail.com";
            settings.SetPassword(s, "app-pw-1234");
            await db.SaveChangesAsync();
        }
        var reader = new FakeReader();
        return (db, new RequestSyncService(db, reader, settings), reader);
    }

    [Fact]
    public async Task Sync_imports_new_and_dedups_within_batch()
    {
        var (db, sync, reader) = await SetupAsync();
        reader.Canned = new() { M(1, "<a@x>", "a@x.de"), M(2, "<b@y>", "b@y.de"), M(3, "<b@y>", "b@y.de") };

        var res = await sync.SyncAsync();

        Assert.Null(res.Error);
        Assert.Equal(2, res.Imported);                 // <b@y> deduped
        Assert.Equal(2, await db.Requests.CountAsync());
        Assert.All(await db.Requests.ToListAsync(), r => Assert.Equal(RequestStatus.New, r.Status));
    }

    [Fact]
    public async Task Resync_imports_nothing_new()
    {
        var (db, sync, reader) = await SetupAsync();
        reader.Canned = new() { M(1, "<a@x>", "a@x.de"), M(2, "<b@y>", "b@y.de") };
        await sync.SyncAsync();

        var second = await sync.SyncAsync();           // LastUid advanced -> reader returns nothing newer

        Assert.Equal(0, second.Imported);
        Assert.Equal(2, await db.Requests.CountAsync());
    }

    [Fact]
    public async Task Sync_without_config_reports_not_configured()
    {
        var (_, sync, reader) = await SetupAsync(configured: false);
        reader.Canned = new() { M(1, "<a@x>", "a@x.de") };

        var res = await sync.SyncAsync();

        Assert.Equal("MailNotConfigured", res.Error);
        Assert.Equal(0, res.Imported);
    }
}
