using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Api.Contracts;
using Tiku.Domain.Catalog;
using Tiku.Domain.Commerce;
using Tiku.Domain.Common;
using Tiku.Domain.Identity;
using Tiku.Domain.Learning;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.IntegrationTests.Api;

public sealed class TenantAdminDirectEndpointTests
{
    [Fact]
    public async Task Tenant_admin_can_view_operational_overview()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        var studentId = Guid.NewGuid();
        var classId = Guid.NewGuid();
        await factory.SeedAsync(
            new User { Id = studentId, Phone = "13900009901", Name = "概览学生" },
            new TenantMembership
            {
                TenantId = seed.TenantId, UserId = studentId, Role = TenantRole.Student,
                Status = MembershipStatus.Active
            },
            new StudentProfile { TenantId = seed.TenantId, UserId = studentId },
            new TenantClass
                { Id = classId, TenantId = seed.TenantId, Name = "概览班级", Status = TenantRecordStatus.Active },
            new TenantStudentFollowup
            {
                TenantId = seed.TenantId,
                StudentUserId = studentId,
                Title = "待跟进",
                Status = StudentFollowupStatus.Open
            },
            new UserNotification
            {
                TenantId = seed.TenantId,
                UserId = studentId,
                NotificationType = "admin",
                Title = "通知",
                Message = "概览通知",
                Status = NotificationStatus.Unread
            },
            new Order
            {
                TenantId = seed.TenantId,
                UserId = studentId,
                OrderNo = "OVERVIEW-ORDER",
                Status = OrderStatus.Paid,
                AmountCents = 2_000,
                PaidAt = DateTimeOffset.UtcNow
            });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var response = await client.GetAsync("/api/tenant/overview");
        var body = await ReadJsonAsync(response);

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.Equal(1, body.RootElement.GetProperty("studentCount").GetInt32());
        Assert.Equal(1, body.RootElement.GetProperty("classCount").GetInt32());
        Assert.Equal(1, body.RootElement.GetProperty("pendingFollowupCount").GetInt32());
        Assert.Equal(1, body.RootElement.GetProperty("unreadNotificationCount").GetInt32());
        Assert.Equal(1, body.RootElement.GetProperty("paidOrderCount").GetInt32());
        Assert.Equal(2_000, body.RootElement.GetProperty("revenueCents").GetInt32());
    }

    [Fact]
    public async Task Tenant_admin_can_run_student_bulk_operations_supervision_and_reports()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        var regionId = Guid.NewGuid();
        var classId = Guid.NewGuid();
        var pointTaskId = Guid.NewGuid();
        var pointItemId = Guid.NewGuid();
        var existingStudentId = Guid.NewGuid();
        await factory.SeedAsync(
            new Region { Id = regionId, TenantId = seed.TenantId, Name = "批量区域" },
            new TenantClass
            {
                Id = classId, TenantId = seed.TenantId, RegionId = regionId, Name = "批量班级",
                Status = TenantRecordStatus.Active
            },
            new User { Id = existingStudentId, Phone = "13900008888", Name = "已有学生", Score = -10 },
            new TenantMembership
            {
                TenantId = seed.TenantId, UserId = existingStudentId, Role = TenantRole.Student,
                Status = MembershipStatus.Active
            },
            new StudentProfile
            {
                TenantId = seed.TenantId,
                UserId = existingStudentId,
                RegionId = regionId,
                LastCheckInDate = DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-10)),
                QuestionsAnsweredToday = 0
            },
            new Report
            {
                TenantId = seed.TenantId, UserId = existingStudentId, Status = ReportStatus.Pending,
                Type = ReportType.Suggestion
            },
            new PointActivityTask
                { Id = pointTaskId, TenantId = seed.TenantId, TaskKey = "bulk-risk", Title = "风险任务", Points = 1000 },
            new PointActivityClaim
            {
                TenantId = seed.TenantId,
                TaskId = pointTaskId,
                UserId = existingStudentId,
                TaskKey = "bulk-risk",
                Points = 1000,
                Status = PointActivityClaimStatus.Claimed
            },
            new PointExchangeItem
                { Id = pointItemId, TenantId = seed.TenantId, ItemKey = "risk-item", Name = "风险兑换", PointsCost = 10 },
            new PointExchangeOrder
            {
                TenantId = seed.TenantId,
                ItemId = pointItemId,
                UserId = existingStudentId,
                OrderNo = "POINT-RISK-1",
                ItemName = "风险兑换",
                Status = PointExchangeOrderStatus.Cancelled,
                PointsCost = 10
            });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);
        var importRequest = new TenantAdminStudentImportDto
        {
            Rows =
            [
                new TenantAdminStudentImportRowDto
                {
                    User = new TenantAdminUserLookupDto { Phone = "13900007777", Name = "批量学生" },
                    RegionId = regionId,
                    ClassId = classId
                },
                new TenantAdminStudentImportRowDto()
            ]
        };

        var previewResponse = await client.PostAsJsonAsync("/api/tenant/students/import/preview", importRequest);
        var importResponse = await client.PostAsJsonAsync("/api/tenant/students/import", importRequest);
        var importJson = await ReadJsonAsync(importResponse);
        var importedUserId = await GetUserIdByPhoneAsync(factory, "13900007777");
        var assignResponse = await client.PostAsJsonAsync(
            "/api/tenant/students/bulk-assign-class",
            new TenantAdminBulkAssignClassDto { ClassId = classId, UserIds = [existingStudentId, importedUserId] });
        var statusResponse = await client.PostAsJsonAsync(
            "/api/tenant/students/bulk-status",
            new TenantAdminBulkStatusDto { UserIds = [importedUserId], Status = "disabled", Reason = "batch test" });
        var ruleResponse = await client.PutAsJsonAsync(
            "/api/tenant/students/supervision/rules",
            new UpsertTenantSupervisionRuleDto
            {
                Code = "inactive",
                Title = "长时间未学习",
                DaysWithoutCheckIn = 3,
                MaxQuestionsAnsweredToday = 0
            });
        var rulesResponse = await client.GetAsync("/api/tenant/students/supervision/rules");
        var previewRiskResponse = await client.GetAsync("/api/tenant/students/supervision/preview");
        var generateResponse = await client.PostAsJsonAsync(
            "/api/tenant/students/supervision/generate",
            new TenantSupervisionGenerateDto { UserIds = [existingStudentId], AssignedToUserId = seed.UserId });
        var followupReportResponse = await client.GetAsync("/api/tenant/student-followups/report");
        var feedbackReportResponse = await client.GetAsync("/api/tenant/feedbacks/report");
        var pointRiskReportResponse = await client.GetAsync("/api/tenant/points/risk-report");

        Assert.Equal(HttpStatusCode.OK, previewResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, importResponse.StatusCode);
        Assert.Equal(1, importJson.RootElement.GetProperty("createdOrUpdatedCount").GetInt32());
        Assert.Equal(1, importJson.RootElement.GetProperty("invalidItems").GetArrayLength());
        Assert.Equal(HttpStatusCode.OK, assignResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, ruleResponse.StatusCode);
        Assert.Contains("inactive", await rulesResponse.Content.ReadAsStringAsync(),
            StringComparison.OrdinalIgnoreCase);
        Assert.Contains(existingStudentId.ToString(), await previewRiskResponse.Content.ReadAsStringAsync(),
            StringComparison.OrdinalIgnoreCase);
        Assert.Equal(HttpStatusCode.OK, generateResponse.StatusCode);
        Assert.Contains("openCount", await followupReportResponse.Content.ReadAsStringAsync(),
            StringComparison.OrdinalIgnoreCase);
        Assert.Contains("pendingCount", await feedbackReportResponse.Content.ReadAsStringAsync(),
            StringComparison.OrdinalIgnoreCase);
        Assert.Contains("negativeScoreUserCount", await pointRiskReportResponse.Content.ReadAsStringAsync(),
            StringComparison.OrdinalIgnoreCase);

        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.True(await dbContext.TenantClassMembers.AnyAsync(item =>
            item.TenantId == seed.TenantId &&
            item.ClassId == classId &&
            item.UserId == importedUserId));
        Assert.True(await dbContext.TenantMemberships.AnyAsync(item =>
            item.TenantId == seed.TenantId &&
            item.UserId == importedUserId &&
            item.Status == MembershipStatus.Disabled));
        Assert.True(await dbContext.TenantStudentFollowups.AnyAsync(item =>
            item.TenantId == seed.TenantId &&
            item.StudentUserId == existingStudentId &&
            item.FollowupType == StudentFollowupType.Risk));
    }

    [Fact]
    public async Task Tenant_admin_can_manage_classes_members_and_students()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        var regionId = Guid.NewGuid();
        await factory.SeedAsync(new Region { Id = regionId, TenantId = seed.TenantId, Name = "四川" });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var classResponse = await client.PutAsJsonAsync(
            "/api/tenant/classes",
            new UpsertTenantAdminClassDto
            {
                RegionId = regionId,
                Code = "A001",
                Name = "冲刺一班",
                Order = 1
            });
        var classJson = await ReadJsonAsync(classResponse);
        var classId = classJson.RootElement.GetProperty("item").GetProperty("id").GetGuid();

        var memberResponse = await client.PutAsJsonAsync(
            "/api/tenant/classes/members",
            new UpsertTenantAdminClassMemberDto
            {
                ClassId = classId,
                MemberType = "student",
                User = new TenantAdminUserLookupDto
                {
                    Phone = "13900000001",
                    Name = "张同学"
                },
                Metadata = JsonSerializer.SerializeToElement(new { source = "test" })
            });
        var memberJson = await ReadJsonAsync(memberResponse);
        var studentUserId = memberJson.RootElement.GetProperty("item").GetProperty("userId").GetGuid();

        var listResponse = await client.GetAsync($"/api/tenant/students?classId={classId}");
        var listJson = await ReadJsonAsync(listResponse);
        var classesResponse = await client.GetAsync("/api/tenant/classes");
        var classesJson = await ReadJsonAsync(classesResponse);

        Assert.Equal(HttpStatusCode.OK, classResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, memberResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
        Assert.Equal(studentUserId, listJson.RootElement.GetProperty("items")[0].GetProperty("userId").GetGuid());
        Assert.Equal(1, classesJson.RootElement.GetProperty("items")[0].GetProperty("studentCount").GetInt32());

        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.True(await dbContext.StudentProfiles.AnyAsync(profile =>
            profile.TenantId == seed.TenantId && profile.UserId == studentUserId));
        Assert.True(await dbContext.TenantMemberships.AnyAsync(member =>
            member.TenantId == seed.TenantId && member.UserId == studentUserId && member.Role == TenantRole.Student));
        Assert.True(await dbContext.AuditLogs.AnyAsync(log =>
            log.TenantId == seed.TenantId && log.Action == "tenant.class_member.upserted"));
    }

    [Fact]
    public async Task Tenant_admin_can_write_student_notes_and_followups()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        var studentId = Guid.NewGuid();
        await factory.SeedAsync(
            new User { Id = studentId, Phone = "13900000002", Name = "李同学" },
            new TenantMembership
            {
                TenantId = seed.TenantId, UserId = studentId, Role = TenantRole.Student,
                Status = MembershipStatus.Active
            },
            new StudentProfile { TenantId = seed.TenantId, UserId = studentId });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var noteResponse = await client.PutAsJsonAsync(
            "/api/tenant/student-notes",
            new UpsertTenantAdminStudentNoteDto
            {
                StudentUserId = studentId,
                NoteType = "learning",
                Content = "需要关注英语词汇",
                IsPinned = true
            });
        var followupResponse = await client.PutAsJsonAsync(
            "/api/tenant/student-followups",
            new UpsertTenantAdminStudentFollowupDto
            {
                StudentUserId = studentId,
                AssignedToUserId = seed.UserId,
                Title = "电话回访",
                FollowupType = "service",
                Priority = "high",
                Status = "in_progress"
            });
        var notesResponse = await client.GetAsync($"/api/tenant/student-notes?studentUserId={studentId}");
        var followupsResponse = await client.GetAsync($"/api/tenant/student-followups?studentUserId={studentId}");
        var notesJson = await ReadJsonAsync(notesResponse);
        var followupsJson = await ReadJsonAsync(followupsResponse);

        Assert.Equal(HttpStatusCode.OK, noteResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, followupResponse.StatusCode);
        Assert.Single(notesJson.RootElement.GetProperty("items").EnumerateArray());
        Assert.Single(followupsJson.RootElement.GetProperty("items").EnumerateArray());
        Assert.Equal("电话回访", followupsJson.RootElement.GetProperty("items")[0].GetProperty("title").GetString());
    }

    [Fact]
    public async Task Student_status_update_revokes_active_sessions()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        var studentId = Guid.NewGuid();
        await factory.SeedAsync(
            new User { Id = studentId, Phone = "13900000003", Name = "王同学" },
            new TenantMembership
            {
                TenantId = seed.TenantId, UserId = studentId, Role = TenantRole.Student,
                Status = MembershipStatus.Active
            },
            new AuthSession
            {
                TenantId = seed.TenantId,
                UserId = studentId,
                TokenHash = "student-session",
                Provider = "password",
                ExpiresAt = DateTimeOffset.UtcNow.AddHours(1)
            });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var statusResponse = await client.PostAsJsonAsync(
            "/api/tenant/students/status",
            new UpdateTenantAdminStudentStatusDto
            {
                UserId = studentId,
                Status = "disabled",
                Reason = "测试禁用"
            });

        Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode);
        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.True(await dbContext.AuthSessions.AnyAsync(session =>
            session.UserId == studentId && session.RevokedAt != null));
    }

    [Fact]
    public async Task Tenant_operator_cannot_access_tenant_admin_direct_endpoints()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory, TenantRole.TenantOperator);
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        using var response = await client.GetAsync("/api/tenant/students");

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

    [Fact]
    public async Task Tenant_admin_can_manage_branding_settings_theme_domains_and_auth_providers()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        await factory.SeedAsync(new TenantThemeTemplate
        {
            Code = "classic",
            Name = "经典主题",
            Theme = JsonSerializer.SerializeToElement(new { color = "blue" }),
            PublicAssets = JsonSerializer.SerializeToElement(new { logo = "/logo.png" }),
            Status = TenantThemeTemplateStatus.Active
        });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var brandingResponse = await client.PutAsJsonAsync(
            "/api/tenant/branding",
            new UpsertTenantBrandingDto
            {
                BrandName = "机构题库",
                ShortName = "题库",
                ServiceWechat = "wechat"
            });
        var settingsResponse = await client.PutAsJsonAsync(
            "/api/tenant/settings",
            new UpsertTenantSettingsDto
            {
                FeatureFlags = JsonSerializer.SerializeToElement(new { catalog = true }),
                PublicConfig = JsonSerializer.SerializeToElement(new { icp = "蜀ICP备" })
            });
        var previewResponse = await client.PostAsJsonAsync(
            "/api/tenant/theme/preview",
            new PreviewTenantThemeDto
            {
                TemplateCode = "classic",
                Theme = JsonSerializer.SerializeToElement(new { color = "red" })
            });
        var publishResponse = await client.PostAsJsonAsync(
            "/api/tenant/theme/publish",
            new PublishTenantThemeDto { UseDraft = true });
        var domainResponse = await client.PostAsJsonAsync(
            "/api/tenant/domains",
            new CreateTenantDomainDto
            {
                Host = "learn.example.com",
                DomainType = "custom",
                IsPrimary = true
            });
        var authProviderResponse = await client.PutAsJsonAsync(
            "/api/tenant/auth-providers",
            new UpsertTenantIdentityProviderDto
            {
                Provider = "wechat_mp",
                Status = "active",
                DisplayName = "微信公众号",
                ConfigPublic = JsonSerializer.SerializeToElement(new { appId = "wx-test" })
            });
        var secretSettingsResponse = await client.PutAsJsonAsync(
            "/api/tenant/settings",
            new UpsertTenantSettingsDto
            {
                PublicConfig = JsonSerializer.SerializeToElement(new { appSecret = "nope" })
            });

        Assert.Equal(HttpStatusCode.OK, brandingResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, settingsResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, previewResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, publishResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, domainResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, authProviderResponse.StatusCode);
        Assert.Equal(HttpStatusCode.BadRequest, secretSettingsResponse.StatusCode);

        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.True(await dbContext.TenantBrandings.AnyAsync(item =>
            item.TenantId == seed.TenantId && item.BrandName == "机构题库"));
        Assert.True(await dbContext.TenantThemeConfigs.AnyAsync(item =>
            item.TenantId == seed.TenantId && item.ActiveTemplateCode == "classic"));
        Assert.True(await dbContext.TenantDomains.AnyAsync(item =>
            item.TenantId == seed.TenantId && item.Host == "learn.example.com" && item.IsPrimary));
        Assert.True(await dbContext.TenantExternalProviders.AnyAsync(item =>
            item.TenantId == seed.TenantId &&
            item.Capability == TenantExternalProviderCapability.Identity &&
            item.Provider == "wechat_mp"));
    }

    [Fact]
    public async Task Tenant_admin_can_manage_members_and_audit_logs()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var memberResponse = await client.PutAsJsonAsync(
            "/api/tenant/members",
            new UpsertTenantAdminMemberDto
            {
                Role = "teacher",
                User = new TenantAdminUserLookupDto
                {
                    Phone = "13900000004",
                    Name = "教师"
                }
            });
        var membersResponse = await client.GetAsync("/api/tenant/members?role=teacher");
        var auditResponse = await client.GetAsync("/api/tenant/audit-logs?action=tenant.member");
        var membersJson = await ReadJsonAsync(membersResponse);
        var auditJson = await ReadJsonAsync(auditResponse);

        Assert.Equal(HttpStatusCode.OK, memberResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, membersResponse.StatusCode);
        Assert.Single(membersJson.RootElement.GetProperty("items").EnumerateArray());
        Assert.NotEmpty(auditJson.RootElement.GetProperty("items").EnumerateArray());
    }

    [Fact]
    public async Task Tenant_admin_can_manage_badges_notifications_and_feedbacks()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedAdminAsync(factory);
        var studentId = Guid.NewGuid();
        var feedbackId = Guid.NewGuid();
        await factory.SeedAsync(
            new User { Id = studentId, Phone = "13900000005", Name = "反馈学生" },
            new TenantMembership
            {
                TenantId = seed.TenantId, UserId = studentId, Role = TenantRole.Student,
                Status = MembershipStatus.Active
            },
            new StudentProfile { TenantId = seed.TenantId, UserId = studentId },
            new Report
            {
                Id = feedbackId,
                TenantId = seed.TenantId,
                UserId = studentId,
                Type = ReportType.Suggestion,
                Title = "希望增加解析",
                Description = "视频解析再详细一点",
                Status = ReportStatus.Pending
            });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var badgeResponse = await client.PutAsJsonAsync(
            "/api/tenant/badges",
            new UpsertTenantAdminBadgeDto
            {
                Name = "反馈达人",
                Category = "feedback",
                UnlockType = "manual",
                Order = 1
            });
        var badgeJson = await ReadJsonAsync(badgeResponse);
        var badgeId = badgeJson.RootElement.GetProperty("item").GetProperty("id").GetGuid();

        var grantResponse = await client.PostAsJsonAsync(
            "/api/tenant/badge-grants",
            new GrantTenantAdminBadgeDto
            {
                UserId = studentId,
                BadgeId = badgeId,
                Note = "感谢反馈"
            });
        var notificationResponse = await client.PutAsJsonAsync(
            "/api/tenant/notifications",
            new UpsertTenantAdminNotificationDto
            {
                UserId = studentId,
                NotificationType = "admin_message",
                Severity = "info",
                Title = "学习提醒",
                Message = "记得复习错题",
                DedupeKey = "daily-review"
            });
        var notificationsListResponse = await client.GetAsync($"/api/tenant/notifications?userId={studentId}");
        var feedbackUpdateResponse = await client.PostAsJsonAsync(
            "/api/tenant/feedbacks/status",
            new UpdateTenantAdminFeedbackDto
            {
                FeedbackId = feedbackId,
                Status = "resolved",
                Priority = "high",
                Resolution = "已安排补充解析",
                Note = "后台处理完成"
            });
        var feedbackListResponse = await client.GetAsync("/api/tenant/feedbacks?status=resolved");
        var notificationsJson = await ReadJsonAsync(notificationsListResponse);
        var feedbackJson = await ReadJsonAsync(feedbackListResponse);

        Assert.Equal(HttpStatusCode.OK, badgeResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, grantResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, notificationResponse.StatusCode);
        Assert.Equal(HttpStatusCode.OK, feedbackUpdateResponse.StatusCode);
        Assert.True(notificationsJson.RootElement.GetProperty("items").GetArrayLength() >= 2);
        Assert.Single(feedbackJson.RootElement.GetProperty("items").EnumerateArray());

        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.True(await dbContext.UserBadges.AnyAsync(item =>
            item.TenantId == seed.TenantId && item.UserId == studentId && item.BadgeId == badgeId));
        Assert.True(await dbContext.UserNotifications.AnyAsync(item =>
            item.TenantId == seed.TenantId && item.UserId == studentId && item.NotificationType == "badge_granted"));
        Assert.True(await dbContext.ReportStatusEvents.AnyAsync(item =>
            item.TenantId == seed.TenantId && item.ReportId == feedbackId && item.ToStatus == ReportStatus.Resolved));
    }

    private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedAdminAsync(
        ApiTestFactory factory,
        TenantRole role = TenantRole.TenantAdmin)
    {
        var tenantId = Guid.NewGuid();
        var userId = Guid.NewGuid();
        var phone = $"137{Random.Shared.Next(10000000, 99999999)}";
        await factory.SeedAsync(
            new Tenant
            {
                Id = tenantId,
                Slug = tenantId.ToString("N"),
                Name = "Test Tenant",
                Status = TenantStatus.Active,
                Metadata = JsonDefaults.Object()
            },
            new User
            {
                Id = userId,
                Phone = phone,
                Name = "Tenant Admin"
            }.WithTestPassword(),
            new TenantMembership
            {
                TenantId = tenantId,
                UserId = userId,
                Role = role,
                Status = MembershipStatus.Active
            });

        return (tenantId, userId, phone);
    }

    private static async Task LoginAsync(
        HttpClient client,
        (Guid TenantId, Guid UserId, string Phone) seed)
    {
        client.UseAccessToken(await client.LoginAsTenantAsync(seed.TenantId, seed.Phone));
    }

    private static async Task<Guid> GetUserIdByPhoneAsync(ApiTestFactory factory, string phone)
    {
        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        return await dbContext.Users
            .Where(user => user.Phone == phone)
            .Select(user => user.Id)
            .SingleAsync();
    }

    private static async Task<JsonDocument> ReadJsonAsync(HttpResponseMessage response)
    {
        var stream = await response.Content.ReadAsStreamAsync();
        return await JsonDocument.ParseAsync(stream);
    }
}