using System.Net;
using System.Net.Http.Json;
using FluentAssertions;
using ColaFlow.Modules.IssueManagement.Application.DTOs;
using ColaFlow.Modules.IssueManagement.Domain.Enums;
using ColaFlow.Modules.IssueManagement.IntegrationTests.Infrastructure;
using Xunit;

namespace ColaFlow.Modules.IssueManagement.IntegrationTests;

/// <summary>
/// Integration tests for Issue Management Module
/// Tests all CRUD operations, status changes, assignments, and multi-tenant isolation
/// </summary>
public class IssueManagementIntegrationTests : IClassFixture<IssueManagementWebApplicationFactory>
{
    private readonly HttpClient _client;
    private readonly Guid _projectId = Guid.NewGuid();
    private readonly Guid _tenantId = Guid.NewGuid();
    private readonly Guid _userId = Guid.NewGuid();
    private readonly string _tenantSlug = "test-tenant";
    private readonly string _userEmail = "test@example.com";

    public IssueManagementIntegrationTests(IssueManagementWebApplicationFactory factory)
    {
        _client = factory.CreateClient();
        _client.AddAuthHeader(_userId, _tenantId, _tenantSlug, _userEmail, "Member");
    }

    [Fact]
    public async Task CreateIssue_WithStoryType_ShouldReturnCreatedIssue()
    {
        // Arrange
        var request = new
        {
            Title = "Implement user authentication",
            Description = "Add JWT authentication to the API",
            Type = "Story",
            Priority = "High"
        };

        // Act
        var response = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);
        var issue = await response.Content.ReadFromJsonAsync<IssueDto>();
        issue.Should().NotBeNull();
        issue!.Title.Should().Be(request.Title);
        issue.Description.Should().Be(request.Description);
        issue.Type.Should().Be("Story");
        issue.Priority.Should().Be("High");
        issue.Status.Should().Be("Backlog");
        issue.ProjectId.Should().Be(_projectId);
        issue.TenantId.Should().Be(_tenantId);
        issue.ReporterId.Should().Be(_userId);
    }

    [Fact]
    public async Task CreateIssue_WithTaskType_ShouldReturnCreatedIssue()
    {
        // Arrange
        var request = new
        {
            Title = "Write unit tests",
            Description = "Add unit tests for Issue Management module",
            Type = "Task",
            Priority = "Medium"
        };

        // Act
        var response = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);
        var issue = await response.Content.ReadFromJsonAsync<IssueDto>();
        issue.Should().NotBeNull();
        issue!.Type.Should().Be("Task");
        issue.Priority.Should().Be("Medium");
    }

    [Fact]
    public async Task CreateIssue_WithBugType_ShouldReturnCreatedIssue()
    {
        // Arrange
        var request = new
        {
            Title = "Fix login redirect issue",
            Description = "Users are not redirected after login",
            Type = "Bug",
            Priority = "Critical"
        };

        // Act
        var response = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);
        var issue = await response.Content.ReadFromJsonAsync<IssueDto>();
        issue.Should().NotBeNull();
        issue!.Type.Should().Be("Bug");
        issue.Priority.Should().Be("Critical");
    }

    [Fact]
    public async Task GetIssueById_WithExistingIssue_ShouldReturnIssue()
    {
        // Arrange - Create an issue first
        var createRequest = new
        {
            Title = "Test issue for retrieval",
            Description = "This issue will be retrieved by ID",
            Type = "Task",
            Priority = "Low"
        };
        var createResponse = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", createRequest);
        var createdIssue = await createResponse.Content.ReadFromJsonAsync<IssueDto>();

        // Act
        var response = await _client.GetAsync($"/api/v1/projects/{_projectId}/issues/{createdIssue!.Id}");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var issue = await response.Content.ReadFromJsonAsync<IssueDto>();
        issue.Should().NotBeNull();
        issue!.Id.Should().Be(createdIssue.Id);
        issue.Title.Should().Be(createRequest.Title);
    }

    [Fact]
    public async Task ListIssues_WithMultipleIssues_ShouldReturnAllIssues()
    {
        // Arrange - Create multiple issues
        var issues = new[]
        {
            new { Title = "Issue 1", Description = "Description 1", Type = "Story", Priority = "High" },
            new { Title = "Issue 2", Description = "Description 2", Type = "Task", Priority = "Medium" },
            new { Title = "Issue 3", Description = "Description 3", Type = "Bug", Priority = "Low" }
        };

        foreach (var issueRequest in issues)
        {
            await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", issueRequest);
        }

        // Act
        var response = await _client.GetAsync($"/api/v1/projects/{_projectId}/issues");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var retrievedIssues = await response.Content.ReadFromJsonAsync<List<IssueDto>>();
        retrievedIssues.Should().NotBeNull();
        retrievedIssues!.Count.Should().BeGreaterThanOrEqualTo(3);
    }

    [Fact]
    public async Task ChangeIssueStatus_FromBacklogToInProgress_ShouldUpdateStatus()
    {
        // Arrange - Create an issue
        var createRequest = new
        {
            Title = "Issue for status change",
            Description = "This issue will have its status changed",
            Type = "Story",
            Priority = "High"
        };
        var createResponse = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", createRequest);
        var createdIssue = await createResponse.Content.ReadFromJsonAsync<IssueDto>();

        // Act - Change status to InProgress
        var statusRequest = new
        {
            Status = "InProgress",
            OldStatus = "Backlog"
        };
        var response = await _client.PutAsJsonAsync(
            $"/api/v1/projects/{_projectId}/issues/{createdIssue!.Id}/status",
            statusRequest);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NoContent);

        // Verify status was changed
        var getResponse = await _client.GetAsync($"/api/v1/projects/{_projectId}/issues/{createdIssue.Id}");
        var updatedIssue = await getResponse.Content.ReadFromJsonAsync<IssueDto>();
        updatedIssue.Should().NotBeNull();
        updatedIssue!.Status.Should().Be("InProgress");
    }

    [Fact]
    public async Task AssignIssue_ToUser_ShouldUpdateAssignee()
    {
        // Arrange - Create an issue
        var createRequest = new
        {
            Title = "Issue for assignment",
            Description = "This issue will be assigned to a user",
            Type = "Task",
            Priority = "Medium"
        };
        var createResponse = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", createRequest);
        var createdIssue = await createResponse.Content.ReadFromJsonAsync<IssueDto>();

        var assigneeId = Guid.NewGuid();

        // Act - Assign issue to user
        var assignRequest = new { AssigneeId = assigneeId };
        var response = await _client.PutAsJsonAsync(
            $"/api/v1/projects/{_projectId}/issues/{createdIssue!.Id}/assign",
            assignRequest);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NoContent);

        // Verify assignment
        var getResponse = await _client.GetAsync($"/api/v1/projects/{_projectId}/issues/{createdIssue.Id}");
        var updatedIssue = await getResponse.Content.ReadFromJsonAsync<IssueDto>();
        updatedIssue.Should().NotBeNull();
        updatedIssue!.AssigneeId.Should().Be(assigneeId);
    }

    [Fact]
    public async Task MultiTenantIsolation_DifferentTenant_ShouldNotAccessIssues()
    {
        // Arrange - Create an issue with tenant A
        var createRequest = new
        {
            Title = "Tenant A issue",
            Description = "This issue belongs to Tenant A",
            Type = "Story",
            Priority = "High"
        };
        var createResponse = await _client.PostAsJsonAsync($"/api/v1/projects/{_projectId}/issues", createRequest);
        var createdIssue = await createResponse.Content.ReadFromJsonAsync<IssueDto>();

        // Act - Try to access with tenant B credentials
        var tenantBId = Guid.NewGuid();
        var tenantBClient = _client;
        tenantBClient.DefaultRequestHeaders.Clear();
        tenantBClient.AddAuthHeader(_userId, tenantBId, "tenant-b", _userEmail, "Member");

        var response = await tenantBClient.GetAsync($"/api/v1/projects/{_projectId}/issues/{createdIssue!.Id}");

        // Assert - Tenant B should not see Tenant A's issue
        // The issue will return NotFound or the global query filter will exclude it
        response.StatusCode.Should().Be(HttpStatusCode.NotFound);
    }
}
