using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Dapper;
using FluentAssertions;
using Microsoft.Data.SqlClient;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.DependencyInjection;
using SIGCM2.Api.Controllers;
using SIGCM2.Application.Abstractions.Security;
using SIGCM2.Domain.Entities;
using SIGCM2.TestSupport;
using Xunit;

namespace SIGCM2.Api.Tests.Audit;

/// UDT-010 Batch 10 — AuditController integration tests.
[Collection("ApiIntegration")]
public sealed class AuditControllerTests : IClassFixture<TestWebAppFactory>
{
    private const string ConnectionString = TestConnectionStrings.ApiTestDb;

    private readonly TestWebAppFactory _factory;

    public AuditControllerTests(TestWebAppFactory factory)
    {
        _factory = factory;
    }

    private async Task<(HttpClient client, int adminId)> AuthedAdminClientAsync()
    {
        await using var conn = new SqlConnection(ConnectionString);
        await conn.OpenAsync();
        await conn.ExecuteAsync("DELETE FROM dbo.AuditEvent;");

        var adminId = await conn.QuerySingleAsync<int>("SELECT Id FROM dbo.Usuario WHERE Username = 'admin'");

        var client = _factory.CreateClient();
        var jwt = _factory.Services.GetRequiredService<IJwtService>();
        var token = jwt.GenerateAccessToken(new Usuario(
            id: adminId, username: "admin", passwordHash: "x",
            nombre: "Admin", apellido: "Sys", email: null,
            rol: "admin", permisosJson: """{"grant":[],"deny":[]}""", activo: true));
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return (client, adminId);
    }

    [Fact]
    public async Task GetEvents_WithoutPermission_Returns403()
    {
        var client = _factory.CreateClient();
        var jwt = _factory.Services.GetRequiredService<IJwtService>();
        // Use a role without administracion:auditoria:ver (cajero only has ventas:contado:*)
        var operadorToken = jwt.GenerateAccessToken(new Usuario(
            id: 9999, username: "opx", passwordHash: "x",
            nombre: "X", apellido: "Y", email: null,
            rol: "cajero", permisosJson: """{"grant":[],"deny":[]}""", activo: true));
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", operadorToken);

        var response = await client.GetAsync("/api/v1/audit/events");

        response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
    }

    [Fact]
    public async Task GetEvents_WithoutAuth_Returns401()
    {
        var client = _factory.CreateClient();
        var response = await client.GetAsync("/api/v1/audit/events");
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task GetEvents_AuthenticatedAdmin_ReturnsAuditEvents()
    {
        var (client, adminId) = await AuthedAdminClientAsync();

        // Seed 3 events directly
        await using var conn = new SqlConnection(ConnectionString);
        await conn.OpenAsync();
        for (var i = 0; i < 3; i++)
        {
            await conn.ExecuteAsync("""
                INSERT INTO dbo.AuditEvent (OccurredAt, ActorUserId, Action, TargetType, TargetId)
                VALUES (@O, @A, @Ac, 'Usuario', @T);
                """, new
            {
                O = DateTime.UtcNow.AddSeconds(-i),
                A = adminId,
                Ac = $"test.seed{i}",
                T = i.ToString(),
            });
        }

        var response = await client.GetAsync("/api/v1/audit/events?targetType=Usuario");
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await response.Content.ReadFromJsonAsync<AuditEventPageResponse>();
        body.Should().NotBeNull();
        body!.Items.Should().HaveCount(3);
        body.Items.Should().OnlyContain(e => e.TargetType == "Usuario");
    }

    [Fact]
    public async Task GetEvents_InvalidLimit_Returns400()
    {
        var (client, _) = await AuthedAdminClientAsync();

        var response = await client.GetAsync("/api/v1/audit/events?limit=0");
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var response2 = await client.GetAsync("/api/v1/audit/events?limit=101");
        response2.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }

    [Fact]
    public async Task GetEvents_FromGreaterThanTo_Returns400()
    {
        var (client, _) = await AuthedAdminClientAsync();

        var response = await client.GetAsync(
            "/api/v1/audit/events?from=2026-05-01T00:00:00Z&to=2026-04-01T00:00:00Z");
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }
}
