using System.Net;
using System.Net.Http.Json;
using Baya.Application.Contracts.Common;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace Baya.Test.Api;

public class VerifyOtpAndMeTests(BayaApiFactory factory) : IClassFixture<BayaApiFactory>
{
    [Fact]
    public async Task VerifyOtp_ValidCode_MintsTokensAndCreatesSession()
    {
        var client = factory.CreateClient();
        const string phone = "09120000010";

        var tokens = await AuthTestClient.LoginAsync(factory, client, phone);

        Assert.False(string.IsNullOrEmpty(tokens.GetProperty("accessToken").GetString()));
        Assert.False(string.IsNullOrEmpty(tokens.GetProperty("refreshToken").GetString()));
        Assert.True(tokens.GetProperty("isNewUser").GetBoolean());
        Assert.Empty(tokens.GetProperty("roles").EnumerateArray());

        using var scope = factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        var phoneHash = scope.ServiceProvider.GetRequiredService<IFieldEncryptor>().Hash(phone);
        var user = await db.Set<User>().SingleAsync(u => u.PhoneHash == phoneHash);

        Assert.True(user.IsActive);
        Assert.NotNull(user.PhoneVerifiedAt);
        var session = await db.Set<UserSession>().SingleAsync(s => s.UserId == user.Id);
        Assert.False(session.IsRevoked);
    }

    [Fact]
    public async Task VerifyOtp_WrongCode_Returns400()
    {
        var client = factory.CreateClient();
        const string phone = "09120000011";
        await AuthTestClient.CreateUserWithOtpCodeAsync(factory, phone);

        var response = await client.PostAsJsonAsync("/api/v1/auth/verify_otp", new { phone, code = "000000" });

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

    [Fact]
    public async Task Me_WithoutToken_Returns401()
    {
        var client = factory.CreateClient();

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

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

    [Fact]
    public async Task Me_WithToken_ReturnsMaskedPhoneAndDefaults()
    {
        var client = factory.CreateClient();
        const string phone = "09120000012";
        var tokens = await AuthTestClient.LoginAsync(factory, client, phone);
        AuthTestClient.UseBearer(client, tokens.GetProperty("accessToken").GetString()!);

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

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var me = await AuthTestClient.ReadDataAsync(response);
        var maskedPhone = me.GetProperty("phone").GetString()!;
        Assert.StartsWith("0912", maskedPhone);
        Assert.Contains('*', maskedPhone);
        Assert.DoesNotContain(phone, maskedPhone);
        Assert.Empty(me.GetProperty("roles").EnumerateArray());
        Assert.False(me.GetProperty("hasCustomerProfile").GetBoolean());
        Assert.False(me.GetProperty("hasNurseProfile").GetBoolean());
        Assert.Equal("not_started", me.GetProperty("nurseVerificationStatus").GetString());
    }
}