using System.Net;
using System.Text.Json;
using API.Models.Outgoing;
using API.Services;
using Moq;
using Moq.Protected;

namespace API.Tests.Services;

public class PlunkEmailSenderTests
{
    private const string TestSecretKey = "test-secret-key";
    private const string PlunkApiUrl = "https://api.useplunk.com/v1/send";

    private static HttpClient CreateMockHttpClient(HttpStatusCode statusCode, string responseContent = "{}")
    {
        var mockHandler = new Mock<HttpMessageHandler>();

        mockHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = statusCode,
                Content = new StringContent(responseContent)
            });

        return new HttpClient(mockHandler.Object);
    }

    private static (HttpClient client, Mock<HttpMessageHandler> handler) CreateMockHttpClientWithHandler(
        HttpStatusCode statusCode, string responseContent = "{}")
    {
        var mockHandler = new Mock<HttpMessageHandler>();

        mockHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = statusCode,
                Content = new StringContent(responseContent)
            });

        return (new HttpClient(mockHandler.Object), mockHandler);
    }

    [Fact]
    public async Task SendEmailAsync_SuccessfulSend_DoesNotThrow()
    {
        // Arrange
        var httpClient = CreateMockHttpClient(HttpStatusCode.OK);
        var emailSender = new PlunkEmailSender(httpClient, TestSecretKey);

        // Act & Assert - should not throw
        await emailSender.SendEmailAsync("test@example.com", "Test Subject", "<p>Test Body</p>");
    }

    [Fact]
    public async Task SendEmailAsync_ApiReturnsError_ThrowsException()
    {
        // Arrange
        var errorMessage = "Invalid API key";
        var httpClient = CreateMockHttpClient(HttpStatusCode.Unauthorized, errorMessage);
        var emailSender = new PlunkEmailSender(httpClient, TestSecretKey);

        // Act & Assert
        var exception = await Assert.ThrowsAsync<Exception>(() =>
            emailSender.SendEmailAsync("test@example.com", "Test Subject", "<p>Test Body</p>"));

        Assert.Contains("Fehler beim Senden der E-Mail über Plunk", exception.Message);
        Assert.Contains(errorMessage, exception.Message);
    }

    [Fact]
    public async Task SendEmailAsync_ServerError_ThrowsException()
    {
        // Arrange
        var httpClient = CreateMockHttpClient(HttpStatusCode.InternalServerError, "Server error");
        var emailSender = new PlunkEmailSender(httpClient, TestSecretKey);

        // Act & Assert
        await Assert.ThrowsAsync<Exception>(() =>
            emailSender.SendEmailAsync("test@example.com", "Test Subject", "<p>Test Body</p>"));
    }

    [Fact]
    public async Task SendEmailAsync_BadRequest_ThrowsException()
    {
        // Arrange
        var httpClient = CreateMockHttpClient(HttpStatusCode.BadRequest, "Invalid request");
        var emailSender = new PlunkEmailSender(httpClient, TestSecretKey);

        // Act & Assert
        await Assert.ThrowsAsync<Exception>(() =>
            emailSender.SendEmailAsync("test@example.com", "Test Subject", "<p>Test Body</p>"));
    }

    [Fact]
    public async Task SendEmailAsync_SendsCorrectPayload()
    {
        // Arrange
        var mockHandler = new Mock<HttpMessageHandler>();
        HttpRequestMessage? capturedRequest = null;

        mockHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .Callback<HttpRequestMessage, CancellationToken>((request, _) => capturedRequest = request)
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("{}")
            });

        var httpClient = new HttpClient(mockHandler.Object);
        var emailSender = new PlunkEmailSender(httpClient, TestSecretKey);

        // Act
        await emailSender.SendEmailAsync("recipient@example.com", "My Subject", "<h1>Hello</h1>");

        // Assert
        Assert.NotNull(capturedRequest);
        Assert.Equal(HttpMethod.Post, capturedRequest.Method);
        Assert.Equal(PlunkApiUrl, capturedRequest.RequestUri?.ToString());

        var requestContent = await capturedRequest.Content!.ReadAsStringAsync();
        var payload = JsonSerializer.Deserialize<PlunkEmailDto>(requestContent, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        Assert.NotNull(payload);
        Assert.Equal("recipient@example.com", payload.To);
        Assert.Equal("My Subject", payload.Subject);
        Assert.Equal("<h1>Hello</h1>", payload.Body);
    }

    [Fact]
    public async Task SendEmailAsync_SendsCorrectAuthorizationHeader()
    {
        // Arrange
        var mockHandler = new Mock<HttpMessageHandler>();
        HttpRequestMessage? capturedRequest = null;

        mockHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .Callback<HttpRequestMessage, CancellationToken>((request, _) => capturedRequest = request)
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("{}")
            });

        var httpClient = new HttpClient(mockHandler.Object);
        var emailSender = new PlunkEmailSender(httpClient, "my-secret-api-key");

        // Act
        await emailSender.SendEmailAsync("test@example.com", "Subject", "Body");

        // Assert
        Assert.NotNull(capturedRequest);
        var authHeader = httpClient.DefaultRequestHeaders.Authorization;
        Assert.NotNull(authHeader);
        Assert.Equal("Bearer", authHeader.Scheme);
        Assert.Equal("my-secret-api-key", authHeader.Parameter);
    }

    [Fact]
    public async Task SendEmailAsync_SendsCorrectContentType()
    {
        // Arrange
        var mockHandler = new Mock<HttpMessageHandler>();
        HttpRequestMessage? capturedRequest = null;

        mockHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .Callback<HttpRequestMessage, CancellationToken>((request, _) => capturedRequest = request)
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("{}")
            });

        var httpClient = new HttpClient(mockHandler.Object);
        var emailSender = new PlunkEmailSender(httpClient, TestSecretKey);

        // Act
        await emailSender.SendEmailAsync("test@example.com", "Subject", "Body");

        // Assert
        Assert.NotNull(capturedRequest);
        Assert.NotNull(capturedRequest.Content);
        Assert.Equal("application/json", capturedRequest.Content.Headers.ContentType?.MediaType);
        Assert.Equal("utf-8", capturedRequest.Content.Headers.ContentType?.CharSet);
    }
}
