using System.Net;
using Baya.Application.Contracts.Identity;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.Persistence;
using Baya.Infrastructure.Persistence.Services.Seeding;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace Baya.Test.Api;

/// <summary>
/// Proves the Development demo seeder end-to-end over the real HTTP pipeline (refinement-phase-1): running it
/// populates a coherent marketplace, the verified nurses surface on the public search route, the unverified
/// one does not, the trust badge reflects verification, and a second run is a no-op (idempotent).
/// </summary>
public class DemoWorldSeederTests(BayaApiFactory factory) : IClassFixture<BayaApiFactory>
{
    private const long TehranCityId = 101;
    private const long ElderlyCareCategoryId = 1; // offered only by the verified nurses
    private const long InfantCareCategoryId = 3;   // offered only by the unverified nurse

    private async Task RunSeederAsync()
    {
        using var scope = factory.Services.CreateScope();
        var seeder = scope.ServiceProvider.GetRequiredService<DemoWorldSeeder>();
        await seeder.SeedAsync(CancellationToken.None);
    }

    [Fact]
    public async Task Seed_Then_Search_VerifiedNurseSurfaces_UnverifiedDoesNot()
    {
        await RunSeederAsync();

        var client = factory.CreateClient();

        // Elderly care is offered only by the verified nurses → at least one searchable result.
        var elderly = await client.GetAsync(
            $"/api/v1/search/nurses?service_category_id={ElderlyCareCategoryId}&city_id={TehranCityId}");
        Assert.Equal(HttpStatusCode.OK, elderly.StatusCode);
        var elderlyData = await AuthTestClient.ReadDataAsync(elderly);
        Assert.True(elderlyData.GetProperty("total").GetInt32() > 0);

        // Infant care is offered only by the unverified nurse → is_searchable=0, so nothing surfaces.
        var infant = await client.GetAsync(
            $"/api/v1/search/nurses?service_category_id={InfantCareCategoryId}&city_id={TehranCityId}");
        Assert.Equal(HttpStatusCode.OK, infant.StatusCode);
        var infantData = await AuthTestClient.ReadDataAsync(infant);
        Assert.Equal(0, infantData.GetProperty("total").GetInt32());
    }

    [Fact]
    public async Task Seed_VerifiedNurse_HasVerifiedBadge_UnverifiedDoesNot()
    {
        await RunSeederAsync();

        long verifiedNurseId;
        long unverifiedNurseId;
        using (var scope = factory.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            verifiedNurseId = await db.Set<NurseProfile>().Where(p => p.IsVerified).Select(p => p.Id).FirstAsync();
            unverifiedNurseId = await db.Set<NurseProfile>().Where(p => !p.IsVerified).Select(p => p.Id).FirstAsync();
        }

        var client = factory.CreateClient();

        var verified = await client.GetAsync($"/api/v1/nurses/{verifiedNurseId}/trust_badge");
        Assert.Equal(HttpStatusCode.OK, verified.StatusCode);
        var verifiedBadge = await AuthTestClient.ReadDataAsync(verified);
        Assert.True(verifiedBadge.GetProperty("isVerified").GetBoolean());
        Assert.NotEmpty(verifiedBadge.GetProperty("credentialTypes").EnumerateArray());

        var unverified = await client.GetAsync($"/api/v1/nurses/{unverifiedNurseId}/trust_badge");
        Assert.Equal(HttpStatusCode.OK, unverified.StatusCode);
        var unverifiedBadge = await AuthTestClient.ReadDataAsync(unverified);
        Assert.False(unverifiedBadge.GetProperty("isVerified").GetBoolean());
    }

    [Fact]
    public async Task Seed_IsIdempotent_SecondRunAddsNoDuplicates()
    {
        await RunSeederAsync();
        await RunSeederAsync();

        using var scope = factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

        Assert.Equal(DemoWorldDefinitions.Nurses.Length, await db.Set<NurseProfile>().CountAsync());
        Assert.Equal(DemoWorldDefinitions.Customers.Length, await db.Set<CustomerProfile>().CountAsync());
    }

    [Fact]
    public async Task Seed_AdminPersonas_AreReachableWithTheirGrantedRoles()
    {
        await RunSeederAsync();

        using var scope = factory.Services.CreateScope();
        var users = scope.ServiceProvider.GetRequiredService<IAppUserManager>();

        // The super_admin can reach the /admin console (routes there) and sees the RBAC console.
        var root = await users.GetUserByPhoneNumber("09120000020");
        Assert.NotNull(root);
        Assert.Contains(RoleNames.SuperAdmin, await users.GetRoleAsync(root));

        // The scoped finance operator carries only the finance role — the fine grain useAdminCapabilities gates on.
        var finance = await users.GetUserByPhoneNumber("09120000021");
        Assert.NotNull(finance);
        var financeRoles = await users.GetRoleAsync(finance);
        Assert.Contains(RoleNames.Finance, financeRoles);
        Assert.DoesNotContain(RoleNames.SuperAdmin, financeRoles);
    }
}
