using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Moq;
using Xunit;

namespace JobTrackerApi.Tests;

public sealed class CorrespondenceControllerTests
{
    [Fact]
    public async Task Create_tags_manually_entered_correspondence_with_manual_provider()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var company = new Company { Name = "Acme", OwnerUserId = "user-1" };
        db.Companies.Add(company);
        await db.SaveChangesAsync();

        var job = new JobApplication { JobTitle = "Backend Developer", CompanyId = company.Id, OwnerUserId = "user-1" };
        db.JobApplications.Add(job);
        await db.SaveChangesAsync();

        var controller = new CorrespondenceController(db);
        var request = new CorrespondenceController.CreateCorrespondenceRequestV2(
            job.Id, "Me", "Called to follow up.", "Follow-up call", "Call", null, "outbound", null, null, null, null, null, null);

        var result = await controller.Create(request, CancellationToken.None);

        Assert.IsType<Correspondence>(((CreatedAtActionResult)result.Result!).Value);
        var stored = await db.Correspondences.SingleAsync();
        Assert.Equal("manual", stored.Provider);
    }

    [Fact]
    public async Task Message_detail_is_owner_scoped_and_tolerates_bad_metadata()
    {
        var databaseName = Guid.NewGuid().ToString();
        var options = new DbContextOptionsBuilder<JobTrackerContext>().UseInMemoryDatabase(databaseName).Options;
        var owner = new Mock<ICurrentUserService>();
        owner.SetupGet(service => service.UserId).Returns("user-1");

        int messageId;
        await using (var ownerDb = new JobTrackerContext(options, owner.Object))
        {
            var company = new Company { Name = "Acme", OwnerUserId = "user-1" };
            ownerDb.Companies.Add(company);
            await ownerDb.SaveChangesAsync();
            var job = new JobApplication { JobTitle = "Backend Developer", CompanyId = company.Id, OwnerUserId = "user-1" };
            ownerDb.JobApplications.Add(job);
            await ownerDb.SaveChangesAsync();
            var message = new Correspondence
            {
                JobApplicationId = job.Id,
                From = "Company",
                Subject = "Interview",
                Content = "Safe saved copy",
                ExternalLabelsJson = "not-json",
                AttachmentMetadataJson = "also-not-json"
            };
            ownerDb.Correspondences.Add(message);
            await ownerDb.SaveChangesAsync();
            messageId = message.Id;

            var ownerResult = await new CorrespondenceController(ownerDb).GetMessage(messageId, CancellationToken.None);
            var ok = Assert.IsType<OkObjectResult>(ownerResult.Result);
            var detail = Assert.IsType<EmailMessageDetailDto>(ok.Value);
            Assert.Equal("Safe saved copy", detail.BodyText);
            Assert.Empty(detail.Labels);
            Assert.Empty(detail.Attachments);

            var inboxResult = await new CorrespondenceController(ownerDb).GetInbox(null, null, null, CancellationToken.None);
            var inbox = Assert.IsType<OkObjectResult>(inboxResult.Result);
            var item = Assert.Single(Assert.IsType<List<CorrespondenceController.CorrespondenceInboxItemDto>>(inbox.Value));
            Assert.Equal(0, item.LabelCount);
            Assert.Equal(0, item.AttachmentCount);
        }

        var other = new Mock<ICurrentUserService>();
        other.SetupGet(service => service.UserId).Returns("user-2");
        await using var otherDb = new JobTrackerContext(options, other.Object);
        var otherResult = await new CorrespondenceController(otherDb).GetMessage(messageId, CancellationToken.None);
        Assert.IsType<NotFoundResult>(otherResult.Result);
    }

    [Fact]
    public async Task Paged_inbox_returns_every_owned_message_without_the_legacy_cap()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var company = new Company { Name = "Scale fixture", OwnerUserId = "user-1" };
        var otherCompany = new Company { Name = "Other tenant", OwnerUserId = "user-2" };
        db.Companies.AddRange(company, otherCompany);
        await db.SaveChangesAsync();
        var job = new JobApplication { JobTitle = "Paged role", CompanyId = company.Id, OwnerUserId = "user-1" };
        var otherJob = new JobApplication { JobTitle = "Private role", CompanyId = otherCompany.Id, OwnerUserId = "user-2" };
        db.JobApplications.AddRange(job, otherJob);
        await db.SaveChangesAsync();
        db.Correspondences.AddRange(Enumerable.Range(1, 205).Select(index => new Correspondence
        {
            JobApplicationId = job.Id,
            From = "Recruiter",
            Subject = $"Message {index}",
            Content = $"Content {index}",
            Date = new DateTime(2026, 1, 1).AddMinutes(index)
        }));
        db.Correspondences.Add(new Correspondence
        {
            JobApplicationId = otherJob.Id,
            From = "Other recruiter",
            Subject = "Private message",
            Content = "Must stay tenant isolated",
            Date = new DateTime(2026, 2, 1)
        });
        await db.SaveChangesAsync();

        var result = await new CorrespondenceController(db).GetInboxPage(null, null, null, 3, 100, CancellationToken.None);
        var page = Assert.IsType<CorrespondenceController.CorrespondenceInboxPageDto>(Assert.IsType<OkObjectResult>(result.Result).Value);

        Assert.Equal(205, page.Total);
        Assert.Equal(3, page.TotalPages);
        Assert.Equal(3, page.Page);
        Assert.Equal(5, page.Items.Count);
        Assert.Equal("Message 5", page.Items[0].Subject);
        Assert.Equal("Message 1", page.Items[^1].Subject);
    }
}
