using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using Xunit;
using IamService.Domain.AggregatesModel.UserAggregate;

namespace IamService.FunctionalTests.Controllers;

/// <summary>
/// EN: Functional tests for Authorization Policies.
/// VI: Functional tests cho Authorization Policies.
/// </summary>
/// <remarks>
/// EN: These tests require additional authentication configuration in CustomWebApplicationFactory
///     to properly inject role claims into the test JWT. The tests verify that:
///     - RequireSuperAdmin: Only SuperAdmin can access PAM endpoints
///     - RequireAdmin: Admin/SuperAdmin can access management endpoints
///     - RequireAuditor: Auditor/Admin/SuperAdmin can access audit endpoints
///     - OwnerOrAdmin: User can access own profile or Admin can access any
///     
///     TODO: Configure test authentication to properly inject role claims.
///     Current unit tests in IamService.UnitTests cover authorization handler logic.
/// VI: Các tests này yêu cầu cấu hình thêm authentication trong CustomWebApplicationFactory
///     để inject role claims vào test JWT. Unit tests trong IamService.UnitTests đã cover
///     logic authorization handler.
/// </remarks>
[Collection("Sequential")]
public class AuthorizationPolicyTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory _factory;

    public AuthorizationPolicyTests(CustomWebApplicationFactory factory)
    {
        _factory = factory;
        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
    }

    #region Token Generation Helpers

    /// <summary>
    /// EN: Generate a test JWT token with specified roles.
    /// VI: Tạo test JWT token với các roles được chỉ định.
    /// </summary>
    /// <remarks>
    /// EN: Creates an unsigned JWT that the CustomWebApplicationFactory accepts.
    ///     The test factory is configured to skip signature validation.
    /// VI: Tạo unsigned JWT mà CustomWebApplicationFactory chấp nhận.
    ///     Test factory được cấu hình để bỏ qua signature validation.
    /// </remarks>
    private string GenerateTestToken(Guid userId, string email, params string[] roles)
    {
        var claims = new List<Claim>
        {
            new("sub", userId.ToString()),
            new("email", email),
            new("name", "Test User"),
            new("iss", "http://localhost"),
            new("aud", "api"),
            new("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
            new("exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds().ToString())
        };

        // Add role claims
        foreach (var role in roles)
        {
            claims.Add(new Claim("role", role));
        }

        // Create unsigned token descriptor
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.UtcNow.AddHours(1),
            Issuer = "http://localhost",
            Audience = "api"
        };

        // Generate JWT without signature (for test purposes)
        var handler = new JwtSecurityTokenHandler();
        var token = handler.CreateJwtSecurityToken(tokenDescriptor);
        
        return handler.WriteToken(token);
    }

    private HttpRequestMessage CreateRequest(HttpMethod method, string url, string token)
    {
        var request = new HttpRequestMessage(method, url);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return request;
    }

    #endregion

    #region RequireAdmin Policy Tests

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetUsers_WithoutAdminRole_ShouldReturn403()
    {
        // Arrange - User with no admin role
        var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/users", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetUsers_WithAdminRole_ShouldReturn200()
    {
        // Arrange - User with Admin role
        var token = GenerateTestToken(Guid.NewGuid(), "admin@example.com", "Admin");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/users", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetUsers_WithSuperAdminRole_ShouldReturn200()
    {
        // Arrange - SuperAdmin can access Admin endpoints
        var token = GenerateTestToken(Guid.NewGuid(), "superadmin@example.com", "SuperAdmin");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/users", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

    #endregion

    #region RequireSuperAdmin Policy Tests (PAM)

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetActivePrivilegedAccess_WithAdminRole_ShouldReturn403()
    {
        // Arrange - Admin cannot access SuperAdmin endpoints
        var userId = Guid.NewGuid();
        var token = GenerateTestToken(userId, "admin@example.com", "Admin");
        var request = CreateRequest(HttpMethod.Get, $"/api/v1/privileged-access/active?userId={userId}", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetActivePrivilegedAccess_WithSuperAdminRole_ShouldReturn200()
    {
        // Arrange - Only SuperAdmin can access PAM
        var userId = Guid.NewGuid();
        var token = GenerateTestToken(userId, "superadmin@example.com", "SuperAdmin");
        var request = CreateRequest(HttpMethod.Get, $"/api/v1/privileged-access/active?userId={userId}", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

    #endregion

    #region RequireAuditor Policy Tests

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetAuditLogs_WithUserRole_ShouldReturn403()
    {
        // Arrange - Regular user cannot access audit logs
        var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/audit/logs", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetAuditLogs_WithAuditorRole_ShouldReturn200()
    {
        // Arrange - Auditor can access audit logs
        var token = GenerateTestToken(Guid.NewGuid(), "auditor@example.com", "Auditor");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/audit/logs", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GetComplianceReports_WithAdminRole_ShouldReturn200()
    {
        // Arrange - Admin can access compliance (auditor policy allows admin)
        var token = GenerateTestToken(Guid.NewGuid(), "admin@example.com", "Admin");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/compliance/reports", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

    #endregion

    #region Controller-Level Policy Tests

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task RolesController_WithUserRole_ShouldReturn403()
    {
        // Arrange - User cannot access roles management
        var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User");
        var request = CreateRequest(HttpMethod.Get, "/api/v1/roles", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task OrganizationsController_WithUserRole_ShouldReturn403()
    {
        // Arrange - User cannot access organizations management
        var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User");
        var orgId = Guid.NewGuid();
        var request = CreateRequest(HttpMethod.Get, $"/api/v1/organizations/{orgId}", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
    }

    [Fact(Skip = "Requires test authentication configuration for role claims")]
    public async Task GroupsController_WithUserRole_ShouldReturn403()
    {
        // Arrange - User cannot access groups management
        var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User");
        var groupId = Guid.NewGuid();
        var request = CreateRequest(HttpMethod.Get, $"/api/v1/groups/{groupId}", token);

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
    }

    #endregion

    #region Edge Cases

    [Fact]
    public async Task AnyEndpoint_WithoutToken_ShouldReturn401()
    {
        // Arrange - No token provided
        var response = await _client.GetAsync("/api/v1/users");

        // Assert
        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Fact]
    public async Task AnyEndpoint_WithInvalidToken_ShouldReturn401()
    {
        // Arrange - Invalid token
        var request = CreateRequest(HttpMethod.Get, "/api/v1/users", "invalid-token");

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    #endregion
}
