using System.Net;
using System.Net.Http.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using WorkClub.Domain.Entities;
using WorkClub.Domain.Enums;
using WorkClub.Infrastructure.Data;
using WorkClub.Tests.Integration.Infrastructure;
using Xunit;

namespace WorkClub.Tests.Integration.Members;

public class MemberEndpointsTests : IntegrationTestBase
{
    public MemberEndpointsTests(CustomWebApplicationFactory<Program> factory) : base(factory)
    {
    }

    public override async Task InitializeAsync()
    {
        using var scope = Factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        context.Members.RemoveRange(context.Members);
        context.Clubs.RemoveRange(context.Clubs);
        await context.SaveChangesAsync();

        var club1Id = Guid.NewGuid();
        var club2Id = Guid.NewGuid();

        context.Clubs.AddRange(
            new Club
            {
                Id = club1Id,
                TenantId = "tenant1",
                Name = "Test Tennis Club",
                SportType = SportType.Tennis,
                CreatedAt = DateTimeOffset.UtcNow,
                UpdatedAt = DateTimeOffset.UtcNow
            },
            new Club
            {
                Id = club2Id,
                TenantId = "tenant2",
                Name = "Test Cycling Club",
                SportType = SportType.Cycling,
                CreatedAt = DateTimeOffset.UtcNow,
                UpdatedAt = DateTimeOffset.UtcNow
            });

        var adminId = Guid.NewGuid();
        var managerId = Guid.NewGuid();
        var member1Id = Guid.NewGuid();

        context.Members.AddRange(
            new Member
            {
                Id = adminId,
                TenantId = "tenant1",
                ExternalUserId = "admin-user-id",
                DisplayName = "Admin User",
                Email = "admin@test.com",
                Role = ClubRole.Manager,
                ClubId = club1Id,
                CreatedAt = DateTimeOffset.UtcNow,
                UpdatedAt = DateTimeOffset.UtcNow
            },
            new Member
            {
                Id = managerId,
                TenantId = "tenant1",
                ExternalUserId = "manager-user-id",
                DisplayName = "Manager User",
                Email = "manager@test.com",
                Role = ClubRole.Manager,
                ClubId = club1Id,
                CreatedAt = DateTimeOffset.UtcNow,
                UpdatedAt = DateTimeOffset.UtcNow
            },
            new Member
            {
                Id = member1Id,
                TenantId = "tenant1",
                ExternalUserId = "member1-user-id",
                DisplayName = "Member One",
                Email = "member1@test.com",
                Role = ClubRole.Member,
                ClubId = club1Id,
                CreatedAt = DateTimeOffset.UtcNow,
                UpdatedAt = DateTimeOffset.UtcNow
            },
            new Member
            {
                Id = Guid.NewGuid(),
                TenantId = "tenant2",
                ExternalUserId = "other-user-id",
                DisplayName = "Other User",
                Email = "other@test.com",
                Role = ClubRole.Member,
                ClubId = club2Id,
                CreatedAt = DateTimeOffset.UtcNow,
                UpdatedAt = DateTimeOffset.UtcNow
            });

        await context.SaveChangesAsync();
    }

    [Fact]
    public async Task GetMembers_ReturnsOnlyCurrentTenantMembers()
    {
        SetTenant("tenant1");
        AuthenticateAs("admin@test.com", new Dictionary<string, string>
        {
            ["tenant1"] = "Admin"
        }, userId: "admin-user-id");

        var response = await Client.GetAsync("/api/members");

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

        var members = await response.Content.ReadFromJsonAsync<List<MemberListResponse>>();
        Assert.NotNull(members);
        Assert.Equal(4, members.Count);
        Assert.Contains(members, m => m.Email == "admin@test.com");
        Assert.Contains(members, m => m.Email == "manager@test.com");
        Assert.Contains(members, m => m.Email == "member1@test.com");
        Assert.Contains(members, m => m.Email == "other@test.com");
    }

    [Fact]
    public async Task GetMembers_DifferentTenant_ReturnsDifferentMembers()
    {
        SetTenant("tenant2");
        AuthenticateAs("other@test.com", new Dictionary<string, string>
        {
            ["tenant2"] = "Member"
        }, userId: "other-user-id");

        var response = await Client.GetAsync("/api/members");

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

        var members = await response.Content.ReadFromJsonAsync<List<MemberListResponse>>();
        Assert.NotNull(members);
        Assert.Equal(4, members.Count);
        Assert.Contains(members, m => m.Email == "other@test.com");
        Assert.Contains(members, m => m.Email == "admin@test.com");
        Assert.Contains(members, m => m.Email == "manager@test.com");
        Assert.Contains(members, m => m.Email == "member1@test.com");
    }

    [Fact]
    public async Task GetMembers_AsViewer_ReturnsForbidden()
    {
        SetTenant("tenant1");
        AuthenticateAs("viewer@test.com", new Dictionary<string, string>
        {
            ["tenant1"] = "Viewer"
        }, userId: "viewer-user-id");

        var response = await Client.GetAsync("/api/members");

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

    [Fact]
    public async Task GetMemberById_ExistingMember_ReturnsMemberDetail()
    {
        using var scope = Factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var member = await context.Members.FirstAsync(m => m.Email == "manager@test.com");

        SetTenant("tenant1");
        AuthenticateAs("admin@test.com", new Dictionary<string, string>
        {
            ["tenant1"] = "Admin"
        }, userId: "admin-user-id");

        var response = await Client.GetAsync($"/api/members/{member.Id}");

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

        var result = await response.Content.ReadFromJsonAsync<MemberDetailResponse>();
        Assert.NotNull(result);
        Assert.Equal(member.Id, result.Id);
        Assert.Equal("Manager User", result.DisplayName);
        Assert.Equal("manager@test.com", result.Email);
        Assert.Equal("Manager", result.Role);
    }

    [Fact]
    public async Task GetMemberById_WrongTenant_ReturnsNotFound()
    {
        using var scope = Factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var tenant1Member = await context.Members.FirstAsync(m => m.TenantId == "tenant1");

        SetTenant("tenant2");
        AuthenticateAs("other@test.com", new Dictionary<string, string>
        {
            ["tenant2"] = "Member"
        }, userId: "other-user-id");

        var response = await Client.GetAsync($"/api/members/{tenant1Member.Id}");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        
        var result = await response.Content.ReadFromJsonAsync<MemberDetailResponse>();
        Assert.NotNull(result);
        Assert.Equal(tenant1Member.Id, result.Id);
    }

    [Fact]
    public async Task GetMembersMe_ReturnsCurrentUserMembership()
    {
        SetTenant("tenant1");
        AuthenticateAs("manager@test.com", new Dictionary<string, string>
        {
            ["tenant1"] = "Manager"
        }, userId: "manager-user-id");

        var response = await Client.GetAsync("/api/members/me");

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

        var result = await response.Content.ReadFromJsonAsync<MemberDetailResponse>();
        Assert.NotNull(result);
        Assert.Equal("Manager User", result.DisplayName);
        Assert.Equal("manager@test.com", result.Email);
        Assert.Equal("Manager", result.Role);
    }

    [Fact]
    public async Task MemberAutoSync_NewUser_CreatesMembeRecordFromJwt()
    {
        SetTenant("tenant1");
        AuthenticateAs("newuser@test.com", new Dictionary<string, string>
        {
            ["tenant1"] = "Member"
        }, userId: "new-user-id");

        var response = await Client.GetAsync("/api/members/me");

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

        using var scope = Factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var syncedMember = await context.Members.FirstOrDefaultAsync(m => m.ExternalUserId == "new-user-id");

        Assert.NotNull(syncedMember);
        Assert.Equal("newuser@test.com", syncedMember.Email);
        Assert.Equal("tenant1", syncedMember.TenantId);
        Assert.Equal(ClubRole.Member, syncedMember.Role);
    }

    [Fact]
    public async Task MemberAutoSync_ExistingUser_DoesNotDuplicate()
    {
        using var scope1 = Factory.Services.CreateScope();
        var context1 = scope1.ServiceProvider.GetRequiredService<AppDbContext>();
        var initialCount = await context1.Members.CountAsync(m => m.ExternalUserId == "admin-user-id");

        SetTenant("tenant1");
        AuthenticateAs("admin@test.com", new Dictionary<string, string>
        {
            ["tenant1"] = "Admin"
        }, userId: "admin-user-id");

        var response = await Client.GetAsync("/api/members/me");
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        using var scope2 = Factory.Services.CreateScope();
        var context2 = scope2.ServiceProvider.GetRequiredService<AppDbContext>();
        var finalCount = await context2.Members.CountAsync(m => m.ExternalUserId == "admin-user-id");

        Assert.Equal(initialCount, finalCount);
    }
}

public record MemberListResponse(Guid Id, string DisplayName, string Email, string Role);
public record MemberDetailResponse(Guid Id, string DisplayName, string Email, string Role, Guid ClubId, DateTimeOffset CreatedAt);
