using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Workflow.Application.Form.DTOs;
using Workflow.Application.Form.FormData.Commands;
using Workflow.Application.Form.FormData.Queries;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Infrastructure.Persistence;
using Xunit;

namespace Workflow.Tests.Form;

[Collection("FormTests")]
public class FormDataTests
{
    private readonly FormTestFixture _fixture;

    public FormDataTests(FormTestFixtureClassFixture fixture)
    {
        _fixture = fixture;
    }

    /// <summary>
    /// 辅助方法：创建一个已发布的表单定义并返回其 Id，
    /// 用于 FormData 测试的前置条件。
    /// </summary>
    private static Domain.Entities.FormDefinition CreatePublishedFormDefinition(Guid formId)
    {
        var form = new Domain.Entities.FormDefinition
        {
            Id = formId,
            Name = "测试表单",
            Code = $"TEST_FORM_{formId:N}",
            Version = 1,
            Status = FormStatus.Published,
            SchemaJson = "{}",
        };
        form.Fields.Add(new Domain.Entities.FormDefinitionField
        {
            Id = Guid.NewGuid(),
            FormDefinitionId = formId,
            FieldKey = "name",
            Label = "姓名",
            FieldType = FieldType.Input,
            Required = true,
            Config = "{}",
            SortOrder = 0,
        });
        form.Fields.Add(new Domain.Entities.FormDefinitionField
        {
            Id = Guid.NewGuid(),
            FormDefinitionId = formId,
            FieldKey = "age",
            Label = "年龄",
            FieldType = FieldType.Number,
            Required = false,
            DefaultValue = "18",
            Config = """{"min":0,"max":150}""",
            SortOrder = 1,
        });
        form.Fields.Add(new Domain.Entities.FormDefinitionField
        {
            Id = Guid.NewGuid(),
            FormDefinitionId = formId,
            FieldKey = "gender",
            Label = "性别",
            FieldType = FieldType.Radio,
            Required = true,
            Config = """{"options":["男","女"]}""",
            SortOrder = 2,
        });
        form.Fields.Add(new Domain.Entities.FormDefinitionField
        {
            Id = Guid.NewGuid(),
            FormDefinitionId = formId,
            FieldKey = "resume",
            Label = "简历附件",
            FieldType = FieldType.FileUpload,
            Required = false,
            Config = """{"maxSize":10485760,"extensions":[".pdf",".doc",".docx"]}""",
            SortOrder = 3,
        });
        return form;
    }

    // ====================================================================
    // SubmitFormData
    // ====================================================================

    [Fact]
    public async Task SubmitFormData_StoresDataJson()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var instanceId = Guid.NewGuid();
        var dataJson = """{"name":"张三","age":25,"gender":"男"}""";

        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(SubmitFormData_StoresDataJson),
            seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));

        var handler = new SubmitFormDataCommandHandler(db);
        var command = new SubmitFormDataCommand(
            FormDefinitionId: formId,
            InstanceId: instanceId,
            DataJson: dataJson
        );

        // Act
        var formDataId = await handler.Handle(command, CancellationToken.None);

        // Assert
        formDataId.Should().NotBe(Guid.Empty);

        var savedData = await db.FormDatas.FindAsync(formDataId);
        savedData.Should().NotBeNull();
        savedData!.DataJson.Should().Be(dataJson);
    }

    [Fact]
    public async Task SubmitFormData_AssociatesWithFormDefinition()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var instanceId = Guid.NewGuid();

        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(SubmitFormData_AssociatesWithFormDefinition),
            seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));

        var handler = new SubmitFormDataCommandHandler(db);
        var command = new SubmitFormDataCommand(
            FormDefinitionId: formId,
            InstanceId: instanceId,
            DataJson: """{"name":"李四","age":30,"gender":"女"}"""
        );

        // Act
        var formDataId = await handler.Handle(command, CancellationToken.None);

        // Assert
        var savedData = await db.FormDatas.FindAsync(formDataId);
        savedData.Should().NotBeNull();
        savedData!.FormDefinitionId.Should().Be(formId);
    }

    [Fact]
    public async Task SubmitFormData_AssociatesWithWorkflowInstance()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var instanceId = Guid.NewGuid();

        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(SubmitFormData_AssociatesWithWorkflowInstance),
            seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));

        var handler = new SubmitFormDataCommandHandler(db);
        var command = new SubmitFormDataCommand(
            FormDefinitionId: formId,
            InstanceId: instanceId,
            DataJson: """{"name":"王五","age":28,"gender":"男"}"""
        );

        // Act
        var formDataId = await handler.Handle(command, CancellationToken.None);

        // Assert
        var savedData = await db.FormDatas.FindAsync(formDataId);
        savedData.Should().NotBeNull();
        savedData!.InstanceId.Should().Be(instanceId);
    }

    [Fact]
    public async Task SubmitFormData_RequiredFieldMissing_ThrowsBusinessException()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var instanceId = Guid.NewGuid();
        // name 是必填字段，但 DataJson 中没有 name
        var dataJson = """{"age":25,"gender":"男"}""";

        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(SubmitFormData_RequiredFieldMissing_ThrowsBusinessException),
            seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));

        var handler = new SubmitFormDataCommandHandler(db);
        var command = new SubmitFormDataCommand(
            FormDefinitionId: formId,
            InstanceId: instanceId,
            DataJson: dataJson
        );

        // Act
        var act = () => handler.Handle(command, CancellationToken.None);

        // Assert
        await act.Should().ThrowAsync<BusinessException>()
            .WithMessage("*必填*name*");

        // 验证数据未被写入
        var count = await db.FormDatas.CountAsync();
        count.Should().Be(0);
    }

    [Fact]
    public async Task SubmitFormData_InvalidFieldType_ThrowsBusinessException()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var instanceId = Guid.NewGuid();
        // age 字段类型为 Number，但提交了非数字值 "abc"
        var dataJson = """{"name":"赵六","age":"abc","gender":"男"}""";

        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(SubmitFormData_InvalidFieldType_ThrowsBusinessException),
            seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));

        var handler = new SubmitFormDataCommandHandler(db);
        var command = new SubmitFormDataCommand(
            FormDefinitionId: formId,
            InstanceId: instanceId,
            DataJson: dataJson
        );

        // Act
        var act = () => handler.Handle(command, CancellationToken.None);

        // Assert
        await act.Should().ThrowAsync<BusinessException>()
            .WithMessage("*age*类型*");

        // 验证数据未被写入
        var count = await db.FormDatas.CountAsync();
        count.Should().Be(0);
    }

    // ====================================================================
    // GetFormDataByInstance
    // ====================================================================

    [Fact]
    public async Task GetFormDataByInstance_ReturnsFormData()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var instanceId = Guid.NewGuid();
        var formDataId = Guid.NewGuid();
        var dataJson = """{"name":"钱七","age":35,"gender":"男"}""";

        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(GetFormDataByInstance_ReturnsFormData),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId));
                ctx.FormDatas.Add(new Domain.Entities.FormData
                {
                    Id = formDataId,
                    FormDefinitionId = formId,
                    InstanceId = instanceId,
                    DataJson = dataJson,
                });
            });

        var handler = new GetFormDataByInstanceQueryHandler(db);
        var query = new GetFormDataByInstanceQuery(InstanceId: instanceId);

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result!.Id.Should().Be(formDataId);
        result.FormDefinitionId.Should().Be(formId);
        result.InstanceId.Should().Be(instanceId);
        result.DataJson.Should().Be(dataJson);
    }

    [Fact]
    public async Task GetFormDataByInstance_NotFound_ReturnsNull()
    {
        // Arrange
        await using var db = _fixture.CreateDbContext(testName: nameof(GetFormDataByInstance_NotFound_ReturnsNull));
        var handler = new GetFormDataByInstanceQueryHandler(db);
        var query = new GetFormDataByInstanceQuery(InstanceId: Guid.NewGuid());

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.Should().BeNull();
    }
}
