using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

[Collection("Database")]
public class UserManagementTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;
    private HttpClient _client = null!;

    public UserManagementTests(ApiFixture fixture) => _fixture = fixture;

    public async Task InitializeAsync()
    {
        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        await DbResetHelper.ResetAsync(db);
        await DataSeeder.SeedAsync(db);
        _client = await AuthHelper.LoginAsync(_fixture, "admin1");
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task CreateUser_ValidRequest_Returns201WithUser()
    {
        var request = new
        {
            Username = "newclerk1",
            Password = "Secure1Pass",
            FullName = "New Clerk",
            Role = "DATA_ENTRY_CLERK"
        };

        var response = await _client.PostAsJsonAsync("/api/v1/users", request);

        response.StatusCode.Should().Be(HttpStatusCode.Created);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>();
        var data = body.GetProperty("data");
        data.GetProperty("username").GetString().Should().Be("newclerk1");
        data.GetProperty("fullName").GetString().Should().Be("New Clerk");
        data.GetProperty("role").GetString().Should().Be("DATA_ENTRY_CLERK");
        data.GetProperty("id").GetGuid().Should().NotBeEmpty();

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var user = await db.Users.FirstOrDefaultAsync(u => u.Username == "newclerk1");
        user.Should().NotBeNull();
        user!.IsActive.Should().BeTrue();
    }

    [Fact]
    public async Task CreateUser_DuplicateUsername_Returns409()
    {
        var request = new
        {
            Username = "entry1",
            Password = "Secure1Pass",
            FullName = "Duplicate Entry",
            Role = "DATA_ENTRY_CLERK"
        };

        var response = await _client.PostAsJsonAsync("/api/v1/users", request);

        response.StatusCode.Should().Be(HttpStatusCode.Conflict);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>();
        body.GetProperty("error").GetProperty("code").GetString().Should().Be("USERNAME_TAKEN");
    }

    [Fact]
    public async Task CreateUser_WeakPassword_Returns422()
    {
        var request = new
        {
            Username = "weakuser",
            Password = "short",
            FullName = "Weak Password User",
            Role = "VERIFIER"
        };

        var response = await _client.PostAsJsonAsync("/api/v1/users", request);

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

    [Fact]
    public async Task CreateUser_NonAdmin_Returns403()
    {
        var entryClient = await AuthHelper.LoginAsync(_fixture, "entry1");

        var request = new
        {
            Username = "unauthorized",
            Password = "Secure1Pass",
            FullName = "Unauthorized Create",
            Role = "VERIFIER"
        };

        var response = await entryClient.PostAsJsonAsync("/api/v1/users", request);

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

    [Fact]
    public async Task UpdateUser_ChangeRoleAndName_Returns200()
    {
        // First create a user to update
        var createReq = new
        {
            Username = "toupdate1",
            Password = "Secure1Pass",
            FullName = "Original Name",
            Role = "DATA_ENTRY_CLERK"
        };
        var createResp = await _client.PostAsJsonAsync("/api/v1/users", createReq);
        createResp.StatusCode.Should().Be(HttpStatusCode.Created);
        var created = await createResp.Content.ReadFromJsonAsync<JsonElement>();
        var userId = created.GetProperty("data").GetProperty("id").GetGuid();

        // Update
        var updateReq = new
        {
            FullName = "Updated Name",
            Role = "VERIFIER"
        };

        var response = await _client.PatchAsJsonAsync($"/api/v1/users/{userId}", updateReq);

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>();
        var data = body.GetProperty("data");
        data.GetProperty("fullName").GetString().Should().Be("Updated Name");
        data.GetProperty("role").GetString().Should().Be("VERIFIER");
    }

    [Fact]
    public async Task UpdateUser_Deactivate_Returns200()
    {
        var createReq = new
        {
            Username = "todeactivate1",
            Password = "Secure1Pass",
            FullName = "Will Be Deactivated",
            Role = "INTAKE_CLERK"
        };
        var createResp = await _client.PostAsJsonAsync("/api/v1/users", createReq);
        createResp.StatusCode.Should().Be(HttpStatusCode.Created);
        var created = await createResp.Content.ReadFromJsonAsync<JsonElement>();
        var userId = created.GetProperty("data").GetProperty("id").GetGuid();

        var response = await _client.PatchAsJsonAsync(
            $"/api/v1/users/{userId}", new { IsActive = false });

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var user = await db.Users.FindAsync(userId);
        user!.IsActive.Should().BeFalse();
    }

    [Fact]
    public async Task UpdateUser_NotFound_Returns404()
    {
        var response = await _client.PatchAsJsonAsync(
            $"/api/v1/users/{Guid.NewGuid()}", new { FullName = "Ghost" });

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

    [Fact]
    public async Task ResetPassword_AdminResets_Returns204()
    {
        var createReq = new
        {
            Username = "resetme1",
            Password = "OldPass1Word",
            FullName = "Reset Target",
            Role = "DATA_ENTRY_CLERK"
        };
        var createResp = await _client.PostAsJsonAsync("/api/v1/users", createReq);
        createResp.StatusCode.Should().Be(HttpStatusCode.Created);
        var created = await createResp.Content.ReadFromJsonAsync<JsonElement>();
        var userId = created.GetProperty("data").GetProperty("id").GetGuid();

        var response = await _client.PostAsJsonAsync(
            $"/api/v1/users/{userId}/reset-password",
            new { NewPassword = "NewPass1Word" });

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

        // Verify new password works by logging in
        var loginClient = _fixture.CreateClient();
        var loginResp = await loginClient.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "resetme1", password = "NewPass1Word" });
        loginResp.StatusCode.Should().Be(HttpStatusCode.OK);
    }

    [Fact]
    public async Task ChangePassword_CorrectCurrent_Returns204()
    {
        // Create a user, then log in as them and change their own password
        var createReq = new
        {
            Username = "selfchange1",
            Password = "Original1Pass",
            FullName = "Self Changer",
            Role = "VERIFIER"
        };
        await _client.PostAsJsonAsync("/api/v1/users", createReq);

        var userClient = await AuthHelper.LoginAsync(_fixture, "selfchange1", "Original1Pass");

        var response = await userClient.PostAsJsonAsync("/api/v1/users/me/change-password",
            new { CurrentPassword = "Original1Pass", NewPassword = "Changed1Pass" });

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

        // Verify new password works
        var loginClient = _fixture.CreateClient();
        var loginResp = await loginClient.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "selfchange1", password = "Changed1Pass" });
        loginResp.StatusCode.Should().Be(HttpStatusCode.OK);
    }

    [Fact]
    public async Task ChangePassword_WrongCurrent_Returns422()
    {
        var createReq = new
        {
            Username = "wrongpw1",
            Password = "Correct1Pass",
            FullName = "Wrong Password",
            Role = "VERIFIER"
        };
        await _client.PostAsJsonAsync("/api/v1/users", createReq);

        var userClient = await AuthHelper.LoginAsync(_fixture, "wrongpw1", "Correct1Pass");

        var response = await userClient.PostAsJsonAsync("/api/v1/users/me/change-password",
            new { CurrentPassword = "Wrong1Pass", NewPassword = "New1Pass" });

        response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>();
        body.GetProperty("error").GetProperty("code").GetString().Should().Be("INVALID_PASSWORD");
    }
}
