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.Clubs;

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

    private static readonly string Tenant1Id = Guid.Parse("00000000-0000-0000-0000-000000000001").ToString();
    private static readonly string Tenant2Id = Guid.Parse("00000000-0000-0000-0000-000000000002").ToString();

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

        // Clean up and setup test data
        context.Clubs.RemoveRange(context.Clubs);
        context.Members.RemoveRange(context.Members);
        await context.SaveChangesAsync();

        // Create test clubs with Guid-format tenant IDs
        var club1Id = Guid.NewGuid();
        var club2Id = Guid.NewGuid();

        var club1 = new Club
        {
            Id = club1Id,
            TenantId = Tenant1Id,
            Name = "Test Tennis Club",
            SportType = SportType.Tennis,
            Description = "Test club 1",
            CreatedAt = DateTimeOffset.UtcNow,
            UpdatedAt = DateTimeOffset.UtcNow
        };

        var club2 = new Club
        {
            Id = club2Id,
            TenantId = Tenant2Id,
            Name = "Test Cycling Club",
            SportType = SportType.Cycling,
            Description = "Test club 2",
            CreatedAt = DateTimeOffset.UtcNow,
            UpdatedAt = DateTimeOffset.UtcNow
        };

        context.Clubs.AddRange(club1, club2);

        // Create test members (membership links)
        var adminUserId = "admin-user-id";
        var managerUserId = "manager-user-id";

        // Admin is member of both clubs
        context.Members.Add(new Member
        {
            Id = Guid.NewGuid(),
            TenantId = Tenant1Id,
            ExternalUserId = adminUserId,
            DisplayName = "Admin User",
            Email = "admin@test.com",
            Role = ClubRole.Manager,
            ClubId = club1Id,
            CreatedAt = DateTimeOffset.UtcNow,
            UpdatedAt = DateTimeOffset.UtcNow
        });

        context.Members.Add(new Member
        {
            Id = Guid.NewGuid(),
            TenantId = Tenant2Id,
            ExternalUserId = adminUserId,
            DisplayName = "Admin User",
            Email = "admin@test.com",
            Role = ClubRole.Member,
            ClubId = club2Id,
            CreatedAt = DateTimeOffset.UtcNow,
            UpdatedAt = DateTimeOffset.UtcNow
        });

        // Manager is only member of club1
        context.Members.Add(new Member
        {
            Id = Guid.NewGuid(),
            TenantId = Tenant1Id,
            ExternalUserId = managerUserId,
            DisplayName = "Manager User",
            Email = "manager@test.com",
            Role = ClubRole.Manager,
            ClubId = club1Id,
            CreatedAt = DateTimeOffset.UtcNow,
            UpdatedAt = DateTimeOffset.UtcNow
        });

        await context.SaveChangesAsync();
    }

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

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

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

        var clubs = await response.Content.ReadFromJsonAsync<List<ClubListResponse>>();
        Assert.NotNull(clubs);
        Assert.Equal(2, clubs.Count);
        Assert.Contains(clubs, c => c.Name == "Test Tennis Club");
        Assert.Contains(clubs, c => c.Name == "Test Cycling Club");
    }

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

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

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

        var clubs = await response.Content.ReadFromJsonAsync<List<ClubListResponse>>();
        Assert.NotNull(clubs);
        Assert.Single(clubs);
        Assert.Equal("Test Tennis Club", clubs[0].Name);
    }

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

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

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

        var club = await response.Content.ReadFromJsonAsync<ClubDetailResponse>();
        Assert.NotNull(club);
        Assert.Equal("Test Tennis Club", club.Name);
        Assert.Equal("Tennis", club.SportType);
        Assert.Equal("Test club 1", club.Description);
    }

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

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

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

        var club = await response.Content.ReadFromJsonAsync<ClubDetailResponse>();
        Assert.NotNull(club);
        Assert.Equal("Test Cycling Club", club.Name);
        Assert.Equal("Cycling", club.SportType);
    }

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

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

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

    [Fact]
    public async Task GetClubsMe_Unauthenticated_ReturnsUnauthorized()
    {
        // Act
        var response = await Client.GetAsync("/api/clubs/me");

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

// Response DTOs for test assertions
public record ClubListResponse(Guid Id, string Name, string SportType, int MemberCount);
public record ClubDetailResponse(Guid Id, string Name, string SportType, string? Description, DateTimeOffset CreatedAt);
