using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Domain.Common;
using Tiku.Domain.Content;
using Tiku.Domain.Identity;
using Tiku.Domain.Operations;
using Tiku.Domain.Platform;
using Tiku.Domain.QuestionBanks;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Auth;
using Tiku.Infrastructure.Persistence;

namespace Tiku.IntegrationTests.Api;

public sealed class PlatformQuestionBankEndpointTests
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) }
    };

    [Fact]
    public async Task Platform_question_bank_workflow_is_scoped_versioned_and_idempotent()
    {
        await using var factory = new ApiTestFactory(configurationOverrides: new Dictionary<string, string?>
        {
            ["Tenancy:Resolution:PlatformHosts:0"] = "localhost"
        });
        var platformTenantId = Guid.NewGuid();
        var ordinaryTenantId = Guid.NewGuid();
        var ordinaryBankId = Guid.NewGuid();
        var platformUser = await SeedPlatformQuestionBankUserAsync(factory);
        await factory.SeedAsync(
            new Tenant { Id = platformTenantId, Slug = "platform-content-test", Name = "平台公共内容", Mode = TenantMode.PlatformOwned, Status = TenantStatus.Active, BillingStatus = BillingStatus.Active },
            new Tenant { Id = ordinaryTenantId, Slug = "ordinary-question-bank", Name = "普通租户", Status = TenantStatus.Active, BillingStatus = BillingStatus.Active },
            new QuestionBank { Id = ordinaryBankId, TenantId = ordinaryTenantId, Name = "普通租户私有题库" });

        using var client = factory.CreateClient();
        client.UseAccessToken(await client.LoginAsPlatformAsync(platformUser.Email));

        var bankResponse = await client.PutAsJsonAsync("/api/platform-admin/question-banks", new UpsertPlatformQuestionBankCommand(null, "平台高等数学公共题库", JsonDefaults.Object()));
        Assert.True(bankResponse.StatusCode == HttpStatusCode.OK, await bankResponse.Content.ReadAsStringAsync());
        var bank = await bankResponse.Content.ReadFromJsonAsync<PlatformQuestionBankItem>(JsonOptions);
        Assert.NotNull(bank);
        Assert.NotNull(bank.ContentEntryId);

        var nodeResponse = await client.PutAsJsonAsync("/api/platform-admin/question-banks/nodes", new UpsertPlatformQuestionBankNodeCommand(
            null, bank.Id, null, "math-chapter-1", "函数、极限与连续", ContentNodeType.Chapter, 10, true, JsonDefaults.Object()));
        Assert.Equal(HttpStatusCode.OK, nodeResponse.StatusCode);
        var node = await nodeResponse.Content.ReadFromJsonAsync<PlatformQuestionBankNodeItem>(JsonOptions);
        Assert.NotNull(node);

        var createQuestion = QuestionCommand(null, bank.Id, node.Id, "platform-question-1", "函数极限的定义是什么？");
        var questionResponse = await client.PutAsJsonAsync("/api/platform-admin/question-banks/questions", createQuestion);
        Assert.Equal(HttpStatusCode.OK, questionResponse.StatusCode);
        var question = await questionResponse.Content.ReadFromJsonAsync<PlatformQuestionItem>(JsonOptions);
        Assert.NotNull(question);
        Assert.Equal(1, question.VersionNo);

        var updatedResponse = await client.PutAsJsonAsync(
            "/api/platform-admin/question-banks/questions",
            createQuestion with { Id = question.Id, Content = "请说明函数极限的严格定义。" });
        Assert.Equal(HttpStatusCode.OK, updatedResponse.StatusCode);
        var updated = await updatedResponse.Content.ReadFromJsonAsync<PlatformQuestionItem>(JsonOptions);
        Assert.NotNull(updated);
        Assert.Equal(2, updated.VersionNo);

        var import = new PlatformQuestionImportCommand(
            bank.Id,
            node.Id,
            "simple",
            "平台普通 JSON 回归",
            JsonSerializer.SerializeToElement(new object[]
            {
                new { legacyId = "import-stable-1", type = "true_false", content = "函数极限一定存在。", answerText = "错误" },
                new { legacyId = "invalid-1", type = "choice", content = "" }
            }));
        var jobsBeforePreview = await CountImportJobsAsync(factory, platformTenantId);
        var previewResponse = await client.PostAsJsonAsync("/api/platform-admin/question-banks/imports/preview", import);
        Assert.Equal(HttpStatusCode.OK, previewResponse.StatusCode);
        var preview = await previewResponse.Content.ReadFromJsonAsync<PlatformQuestionImportResult>(JsonOptions);
        Assert.NotNull(preview);
        Assert.Equal(2, preview.Detail.Job.TotalCount);
        Assert.Equal(1, preview.Detail.Job.ValidCount);
        Assert.Equal(1, preview.Detail.Job.ErrorCount);
        Assert.Equal(jobsBeforePreview, await CountImportJobsAsync(factory, platformTenantId));

        var firstImportResponse = await client.PostAsJsonAsync("/api/platform-admin/question-banks/imports", import);
        var repeatedImportResponse = await client.PostAsJsonAsync("/api/platform-admin/question-banks/imports", import);
        Assert.True(firstImportResponse.StatusCode == HttpStatusCode.OK, await firstImportResponse.Content.ReadAsStringAsync());
        Assert.True(repeatedImportResponse.StatusCode == HttpStatusCode.OK, await repeatedImportResponse.Content.ReadAsStringAsync());
        var firstImport = await firstImportResponse.Content.ReadFromJsonAsync<PlatformQuestionImportResult>(JsonOptions);
        var repeatedImport = await repeatedImportResponse.Content.ReadFromJsonAsync<PlatformQuestionImportResult>(JsonOptions);
        Assert.NotNull(firstImport);
        Assert.NotNull(repeatedImport);
        Assert.Equal(1, firstImport.InsertedQuestionCount);
        Assert.Equal(1, repeatedImport.SkippedQuestionCount);

        var structuredImport = new PlatformQuestionImportCommand(
            bank.Id,
            null,
            "structured-v2",
            "平台结构化 JSON 回归",
            JsonSerializer.SerializeToElement(new
            {
                _tikuExport = "2.0",
                subjects = new[]
                {
                    new
                    {
                        code = "advanced-math",
                        name = "高等数学",
                        chapters = new[]
                        {
                            new
                            {
                                code = "limits",
                                name = "极限",
                                questions = new[]
                                {
                                    new { legacyId = "structured-stable-1", type = "choice", content = "下列极限存在的是？", options = new[] { "A", "B" }, answerText = "A" }
                                }
                            }
                        }
                    }
                }
            }));
        var structuredResponse = await client.PostAsJsonAsync("/api/platform-admin/question-banks/imports", structuredImport);
        var repeatedStructuredResponse = await client.PostAsJsonAsync("/api/platform-admin/question-banks/imports", structuredImport);
        Assert.True(structuredResponse.StatusCode == HttpStatusCode.OK, await structuredResponse.Content.ReadAsStringAsync());
        Assert.True(repeatedStructuredResponse.StatusCode == HttpStatusCode.OK, await repeatedStructuredResponse.Content.ReadAsStringAsync());
        var structured = await structuredResponse.Content.ReadFromJsonAsync<PlatformQuestionImportResult>(JsonOptions);
        var repeatedStructured = await repeatedStructuredResponse.Content.ReadFromJsonAsync<PlatformQuestionImportResult>(JsonOptions);
        Assert.NotNull(structured);
        Assert.NotNull(repeatedStructured);
        Assert.Equal(2, structured.CreatedNodeCount);
        Assert.Equal(1, structured.InsertedQuestionCount);
        Assert.Equal(0, repeatedStructured.CreatedNodeCount);
        Assert.Equal(1, repeatedStructured.SkippedQuestionCount);

        var listedResponse = await client.GetAsync($"/api/platform-admin/question-banks/questions?questionBankId={bank.Id}&page=1&pageSize=20");
        Assert.Equal(HttpStatusCode.OK, listedResponse.StatusCode);
        var listed = await listedResponse.Content.ReadFromJsonAsync<PlatformQuestionPage>(JsonOptions);
        Assert.NotNull(listed);
        Assert.Equal(3, listed.Total);

        var bankList = await client.GetFromJsonAsync<PlatformQuestionBankItem[]>("/api/platform-admin/question-banks?status=all", JsonOptions);
        Assert.NotNull(bankList);
        Assert.Contains(bankList, item => item.Id == bank.Id);
        Assert.DoesNotContain(bankList, item => item.Id == ordinaryBankId);

        using var scope = factory.CreateSystemScope("验证平台公共题库租户隔离与版本");
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.Equal(platformTenantId, await dbContext.QuestionBanks.Where(item => item.Id == bank.Id).Select(item => item.TenantId).SingleAsync());
        Assert.Equal(2, await dbContext.QuestionVersions.CountAsync(item => item.TenantId == platformTenantId && item.QuestionId == question.Id));
        Assert.Equal(1, await dbContext.Questions.CountAsync(item => item.TenantId == platformTenantId && item.LegacyId == "import-stable-1"));
    }

    [Fact]
    public async Task Platform_question_bank_rejects_missing_permission_and_non_platform_host()
    {
        await using var factory = new ApiTestFactory(configurationOverrides: new Dictionary<string, string?>
        {
            ["Tenancy:Resolution:PlatformHosts:0"] = "platform.example.test"
        });
        await factory.SeedAsync(
            new Tenant { Id = Guid.NewGuid(), Slug = "platform-content-auth", Name = "平台公共内容", Mode = TenantMode.PlatformOwned });
        var platformWithoutPermission = await SeedPlatformUserAsync(factory, BackendPermissions.PlatformDashboardView);
        using var client = factory.CreateClient();
        client.UseAccessToken(await client.LoginAsPlatformAsync(platformWithoutPermission.Email));

        using var wrongHostRequest = new HttpRequestMessage(HttpMethod.Get, "/api/platform-admin/question-banks");
        wrongHostRequest.Headers.Host = "tenant.example.test";
        var wrongHost = await client.SendAsync(wrongHostRequest);

        using var platformHostRequest = new HttpRequestMessage(HttpMethod.Get, "/api/platform-admin/question-banks");
        platformHostRequest.Headers.Host = "platform.example.test";
        var missingPermission = await client.SendAsync(platformHostRequest);

        Assert.Equal(HttpStatusCode.NotFound, wrongHost.StatusCode);
        Assert.Equal(HttpStatusCode.Forbidden, missingPermission.StatusCode);
    }

    private static UpsertPlatformQuestionCommand QuestionCommand(Guid? id, Guid bankId, Guid nodeId, string legacyId, string content) => new(
        id, bankId, nodeId, legacyId, "short_answer", "简答题", 3,
        JsonSerializer.SerializeToElement(new[] { "极限" }), content, JsonDefaults.Array(), null, JsonDefaults.Array(),
        "按定义作答", "考查函数极限定义", JsonDefaults.Array(), null, null, null, "published", JsonDefaults.Object(), null);

    private static async Task<int> CountImportJobsAsync(ApiTestFactory factory, Guid tenantId)
    {
        using var scope = factory.CreateSystemScope("统计平台公共题库导入任务");
        return await scope.ServiceProvider.GetRequiredService<TikuDbContext>().ContentImportJobs.CountAsync(item => item.TenantId == tenantId);
    }

    private static Task<(Guid UserId, string Email)> SeedPlatformQuestionBankUserAsync(ApiTestFactory factory) =>
        SeedPlatformUserAsync(factory, BackendPermissions.PlatformQuestionBankManage);

    private static async Task<(Guid UserId, string Email)> SeedPlatformUserAsync(ApiTestFactory factory, string permissionCode)
    {
        var userId = Guid.NewGuid();
        var roleId = Guid.NewGuid();
        var email = $"platform-question-bank-{Guid.NewGuid():N}@example.test";
        var moduleCode = PermissionModuleCatalog.ResolvePermissionModuleCode(permissionCode);
        await factory.SeedAsync(
            new PermissionModule { Code = moduleCode, Name = "平台公共题库", Area = BackendPermissionArea.Platform },
            new BackendPermission { Code = permissionCode, Name = "平台公共题库运营", Area = BackendPermissionArea.Platform, PermissionModuleCode = moduleCode, IsSystem = true },
            new User { Id = userId, Email = email, NormalizedEmail = email.ToUpperInvariant(), UserName = email, NormalizedUserName = email.ToUpperInvariant(), Name = "平台题库运营", PrimaryRole = "platform_admin", RawProfile = JsonDefaults.Object() }.WithTestPassword(),
            new PlatformBackendRole { Id = roleId, Code = $"platform_question_bank_{roleId:N}", Name = "平台题库运营", Status = BackendRoleStatus.Active },
            new PlatformBackendRolePermission { RoleId = roleId, PermissionCode = permissionCode },
            new PlatformBackendUserRole { UserId = userId, RoleId = roleId });
        return (userId, email);
    }
}
