using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using PowderCoating.Application.Interfaces;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Infrastructure.Data;
using PowderCoating.Shared.Constants;
using PowderCoating.Web.Controllers;

namespace PowderCoating.UnitTests;

public class EmailBroadcastControllerTests
{
    [Fact]
    public async Task Preview_RendersMergedSampleAndSanitizesHtml()
    {
        await using var context = CreateContext();
        context.Companies.Add(new Company
        {
            Id = 7,
            CompanyId = 7,
            CompanyName = "River City Powder",
            PrimaryContactName = "Jamie Rivera",
            PrimaryContactEmail = "jamie@example.com",
            IsActive = true
        });
        context.Users.Add(new ApplicationUser
        {
            Id = "admin-7",
            CompanyId = 7,
            CompanyRole = AppConstants.CompanyRoles.CompanyAdmin,
            FirstName = "Alex",
            LastName = "Admin",
            Email = "alex@example.com",
            UserName = "alex@example.com",
            IsActive = true
        });
        await context.SaveChangesAsync();

        var controller = CreateController(context);

        var result = await controller.Preview(new AdminEmailSelectionModel
        {
            Subject = "Update for {{CompanyName}}",
            BodyHtml = "<p>Hi {{FirstName}}, contact {{CompanyAdminName}} at {{CompanyAdminEmail}}.</p><script>alert('x')</script>",
            CompanyIds = [7]
        });

        var view = Assert.IsType<ViewResult>(result);
        var model = Assert.IsType<AdminEmailPreviewModel>(view.Model);

        Assert.Equal("Update for River City Powder", model.SamplePreview.RenderedSubject);
        Assert.Contains("Hi Jamie", model.SamplePreview.RenderedHtmlBody);
        Assert.Contains("Alex Admin", model.SamplePreview.RenderedHtmlBody);
        Assert.Contains("alex@example.com", model.SamplePreview.RenderedHtmlBody);
        Assert.DoesNotContain("<script", model.SamplePreview.RenderedHtmlBody, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public async Task Send_WhenEmailSucceeds_WritesAdminEmailLogAndUsesPlatformReplyTo()
    {
        await using var context = CreateContext();
        context.Companies.Add(new Company
        {
            Id = 11,
            CompanyId = 11,
            CompanyName = "Summit Coatings",
            PrimaryContactName = "Morgan Lee",
            PrimaryContactEmail = "morgan@example.com",
            IsActive = true
        });
        await context.SaveChangesAsync();

        var emailService = new Mock<IEmailService>();
        emailService
            .Setup(x => x.SendEmailAsync(
                "morgan@example.com",
                "Morgan Lee",
                "Notice for Summit Coatings",
                It.IsAny<string>(),
                It.IsAny<string>(),
                null,
                null,
                null,
                "admin-notify@example.com",
                "Powder Coating Logix Admin"))
            .ReturnsAsync((true, (string?)null));

        var platformSettings = new Mock<IPlatformSettingsService>();
        platformSettings
            .Setup(x => x.GetAsync(PlatformSettingKeys.AdminNotificationEmail))
            .ReturnsAsync("admin-notify@example.com,backup@example.com");

        var controller = CreateController(context, emailService, platformSettings);

        var result = await controller.Send(new AdminEmailSendRequest
        {
            Subject = "Notice for {{CompanyName}}",
            BodyHtml = "<p>Hello {{FirstName}},</p><p>Thanks for using {{CompanyName}}.</p>",
            CompanyIds = [11]
        });

        var redirect = Assert.IsType<RedirectToActionResult>(result);
        Assert.Equal("Index", redirect.ActionName);

        var log = await context.NotificationLogs.IgnoreQueryFilters().SingleAsync();
        Assert.Equal(NotificationType.AdminEmail, log.NotificationType);
        Assert.Equal(NotificationStatus.Sent, log.Status);
        Assert.Equal("morgan@example.com", log.Recipient);
        Assert.Equal("Notice for Summit Coatings", log.Subject);
        Assert.Contains("Hello Morgan", log.Message);
        emailService.VerifyAll();
    }

    [Fact]
    public async Task Send_WhenPrimaryContactEmailMissing_WritesSkippedLogWithoutSending()
    {
        await using var context = CreateContext();
        context.Companies.Add(new Company
        {
            Id = 13,
            CompanyId = 13,
            CompanyName = "No Inbox Inc",
            PrimaryContactName = "Taylor Noemail",
            PrimaryContactEmail = string.Empty,
            IsActive = true
        });
        await context.SaveChangesAsync();

        var emailService = new Mock<IEmailService>();
        var controller = CreateController(context, emailService);

        var result = await controller.Send(new AdminEmailSendRequest
        {
            Subject = "Heads up for {{CompanyName}}",
            BodyHtml = "<p>Hello {{FirstName}}</p>",
            CompanyIds = [13]
        });

        var redirect = Assert.IsType<RedirectToActionResult>(result);
        Assert.Equal("Index", redirect.ActionName);

        var log = await context.NotificationLogs.IgnoreQueryFilters().SingleAsync();
        Assert.Equal(NotificationType.AdminEmail, log.NotificationType);
        Assert.Equal(NotificationStatus.Skipped, log.Status);
        Assert.Equal(string.Empty, log.Recipient);
        Assert.Equal("Company primary contact email is not configured.", log.ErrorMessage);
        emailService.Verify(
            x => x.SendEmailAsync(
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string?>(),
                It.IsAny<byte[]?>(),
                It.IsAny<string?>(),
                It.IsAny<string?>(),
                It.IsAny<string?>(),
                It.IsAny<string?>()),
            Times.Never);
    }

    private static EmailBroadcastController CreateController(
        ApplicationDbContext context,
        Mock<IEmailService>? emailService = null,
        Mock<IPlatformSettingsService>? platformSettings = null)
    {
        var controller = new EmailBroadcastController(
            context,
            (emailService ?? new Mock<IEmailService>()).Object,
            (platformSettings ?? CreatePlatformSettings()).Object,
            Mock.Of<ILogger<EmailBroadcastController>>());

        var httpContext = new DefaultHttpContext();
        controller.ControllerContext = new ControllerContext
        {
            HttpContext = httpContext
        };
        controller.TempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());

        return controller;
    }

    private static Mock<IPlatformSettingsService> CreatePlatformSettings()
    {
        var settings = new Mock<IPlatformSettingsService>();
        settings.Setup(x => x.GetAsync(It.IsAny<string>())).ReturnsAsync((string?)null);
        return settings;
    }

    private static ApplicationDbContext CreateContext()
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;

        return new ApplicationDbContext(options);
    }
}
