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

namespace Workflow.Tests.Form;

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

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

    // ====================================================================
    // CreateFormDefinition
    // ====================================================================

    [Fact]
    public async Task CreateFormDefinition_WithValidData_ReturnsDto()
    {
        // Arrange
        await using var db = _fixture.CreateDbContext();
        var handler = new CreateFormDefinitionCommandHandler(db);
        var command = new CreateFormDefinitionCommand(
            Name: "员工入职登记表",
            Code: "EMP_ONBOARD",
            Description: "新员工入职时填写的登记表单",
            Fields: new List<FormFieldDto>
            {
                new(FieldKey: "name", Label: "姓名", FieldType: FieldType.Input, Required: true, DefaultValue: null, Config: "{}", SortOrder: 0),
                new(FieldKey: "department", Label: "部门", FieldType: FieldType.Select, Required: true, DefaultValue: null, Config: """{"options":["研发部","市场部","行政部"]}""", SortOrder: 1),
                new(FieldKey: "entry_date", Label: "入职日期", FieldType: FieldType.Date, Required: true, DefaultValue: null, Config: "{}", SortOrder: 2),
            }
        );

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

        // Assert
        result.Should().NotBeNull();
        result.Name.Should().Be("员工入职登记表");
        result.Code.Should().Be("EMP_ONBOARD");
        result.Description.Should().Be("新员工入职时填写的登记表单");
        result.Version.Should().Be(1);
        result.Status.Should().Be(FormStatus.Draft);

        // 验证持久化
        var entity = await db.FormDefinitions.FirstAsync();
        entity.Name.Should().Be("员工入职登记表");
        entity.Code.Should().Be("EMP_ONBOARD");
    }

    [Fact]
    public async Task CreateFormDefinition_WithDuplicateCode_ThrowsBusinessException()
    {
        // Arrange
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(CreateFormDefinition_WithDuplicateCode_ThrowsBusinessException),
            seedAction: db =>
            {
                db.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = Guid.NewGuid(),
                    Name = "已存在的表单",
                    Code = "DUPLICATE_CODE",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
            });

        var handler = new CreateFormDefinitionCommandHandler(db);
        var command = new CreateFormDefinitionCommand(
            Name: "新表单",
            Code: "DUPLICATE_CODE",
            Description: "尝试使用重复编码",
            Fields: []
        );

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

        // Assert
        await act.Should().ThrowAsync<BusinessException>()
            .WithMessage("*编码*已存在*");
    }

    // ====================================================================
    // UpdateFormDefinition
    // ====================================================================

    [Fact]
    public async Task UpdateFormDefinition_UpdatesNameAndDescription()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(UpdateFormDefinition_UpdatesNameAndDescription),
            seedAction: db =>
            {
                db.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "旧名称",
                    Code = "FORM_001",
                    Version = 1,
                    Description = "旧描述",
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
            });

        var handler = new UpdateFormDefinitionCommandHandler(db);
        var command = new UpdateFormDefinitionCommand(
            Id: formId,
            Name: "新名称",
            Description: "新描述",
            Fields: []
        );

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

        // Assert
        result.Should().NotBeNull();
        result.Name.Should().Be("新名称");
        result.Description.Should().Be("新描述");

        // 验证持久化
        var entity = await db.FormDefinitions.FindAsync(formId);
        entity.Should().NotBeNull();
        entity!.Name.Should().Be("新名称");
        entity.Description.Should().Be("新描述");
        // Code 不应被修改
        entity.Code.Should().Be("FORM_001");
    }

    [Fact]
    public async Task UpdateFormDefinition_AddsFieldsToForm()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(UpdateFormDefinition_AddsFieldsToForm),
            seedAction: db =>
            {
                db.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "表单",
                    Code = "FORM_ADD_FIELDS",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
            });

        var handler = new UpdateFormDefinitionCommandHandler(db);
        var command = new UpdateFormDefinitionCommand(
            Id: formId,
            Name: "表单",
            Description: null,
            Fields: new List<FormFieldDto>
            {
                new(FieldKey: "email", Label: "邮箱", FieldType: FieldType.Input, Required: false, DefaultValue: null, Config: "{}", SortOrder: 0),
                new(FieldKey: "phone", Label: "电话", FieldType: FieldType.Input, Required: true, DefaultValue: null, Config: "{}", SortOrder: 1),
            }
        );

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

        // Assert
        var form = await db.FormDefinitions
            .Include(f => f.Fields)
            .FirstAsync(f => f.Id == formId);

        form.Fields.Should().HaveCount(2);
        form.Fields.Select(f => f.FieldKey).Should().Contain(["email", "phone"]);
    }

    [Fact]
    public async Task UpdateFormDefinition_RemovesFieldsFromForm()
    {
        // Arrange
        var formId = Guid.NewGuid();
        var fieldId1 = Guid.NewGuid();
        var fieldId2 = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(UpdateFormDefinition_RemovesFieldsFromForm),
            seedAction: db =>
            {
                var form = new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "表单",
                    Code = "FORM_REM_FIELDS",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                };
                form.Fields.Add(new Domain.Entities.FormDefinitionField
                {
                    Id = fieldId1,
                    FormDefinitionId = formId,
                    FieldKey = "keep_me",
                    Label = "保留字段",
                    FieldType = FieldType.Input,
                    Required = false,
                    Config = "{}",
                    SortOrder = 0,
                });
                form.Fields.Add(new Domain.Entities.FormDefinitionField
                {
                    Id = fieldId2,
                    FormDefinitionId = formId,
                    FieldKey = "remove_me",
                    Label = "删除字段",
                    FieldType = FieldType.Input,
                    Required = false,
                    Config = "{}",
                    SortOrder = 1,
                });
                db.FormDefinitions.Add(form);
            });

        var handler = new UpdateFormDefinitionCommandHandler(db);
        // 只传保留的字段，remove_me 不在列表中
        var command = new UpdateFormDefinitionCommand(
            Id: formId,
            Name: "表单",
            Description: null,
            Fields: new List<FormFieldDto>
            {
                new(FieldKey: "keep_me", Label: "保留字段", FieldType: FieldType.Input, Required: false, DefaultValue: null, Config: "{}", SortOrder: 0),
            }
        );

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

        // Assert
        var form = await db.FormDefinitions
            .Include(f => f.Fields)
            .FirstAsync(f => f.Id == formId);

        form.Fields.Should().HaveCount(1);
        form.Fields[0].FieldKey.Should().Be("keep_me");
    }

    [Fact]
    public async Task UpdateFormDefinition_ModifiesFieldProperties()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(UpdateFormDefinition_ModifiesFieldProperties),
            seedAction: db =>
            {
                var form = new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "表单",
                    Code = "FORM_MOD_FIELDS",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                };
                form.Fields.Add(new Domain.Entities.FormDefinitionField
                {
                    Id = Guid.NewGuid(),
                    FormDefinitionId = formId,
                    FieldKey = "age",
                    Label = "年龄",
                    FieldType = FieldType.Input,
                    Required = false,
                    Config = "{}",
                    SortOrder = 0,
                });
                db.FormDefinitions.Add(form);
            });

        var handler = new UpdateFormDefinitionCommandHandler(db);
        // 修改字段：从 Input 改为 Number，从非必填改为必填，标签从 "年龄" 改为 "用户年龄"
        var command = new UpdateFormDefinitionCommand(
            Id: formId,
            Name: "表单",
            Description: null,
            Fields: new List<FormFieldDto>
            {
                new(FieldKey: "age", Label: "用户年龄", FieldType: FieldType.Number, Required: true, DefaultValue: "18", Config: """{"min":0,"max":150}""", SortOrder: 0),
            }
        );

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

        // Assert
        var form = await db.FormDefinitions
            .Include(f => f.Fields)
            .FirstAsync(f => f.Id == formId);

        form.Fields.Should().HaveCount(1);
        var field = form.Fields[0];
        field.Label.Should().Be("用户年龄");
        field.FieldType.Should().Be(FieldType.Number);
        field.Required.Should().BeTrue();
        field.DefaultValue.Should().Be("18");
        field.Config.Should().Contain("min");
    }

    // ====================================================================
    // DeleteFormDefinition (soft delete)
    // ====================================================================

    [Fact]
    public async Task DeleteFormDefinition_SetsIsDeletedTrue()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(DeleteFormDefinition_SetsIsDeletedTrue),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "待删除表单",
                    Code = "DEL_FORM",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
            });

        var handler = new DeleteFormDefinitionCommandHandler(db);
        var command = new DeleteFormDefinitionCommand(Id: formId);

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

        // Assert — 软删除后通过默认查询应找不到
        var normalQuery = await db.FormDefinitions.FindAsync(formId);
        normalQuery.Should().BeNull();

        // 通过 IgnoreQueryFilters 可以找到，且 IsDeleted = true
        var deletedEntity = await db.FormDefinitions
            .IgnoreQueryFilters()
            .FirstAsync(f => f.Id == formId);
        deletedEntity.IsDeleted.Should().BeTrue();
    }

    [Fact]
    public async Task DeleteFormDefinition_DoesNotHardDelete()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(DeleteFormDefinition_DoesNotHardDelete),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "待删除表单",
                    Code = "NO_HARD_DEL",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
            });

        var handler = new DeleteFormDefinitionCommandHandler(db);
        var command = new DeleteFormDefinitionCommand(Id: formId);

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

        // Assert — 物理记录仍然存在于数据库中
        var records = await db.FormDefinitions.IgnoreQueryFilters().CountAsync(f => f.Id == formId);
        records.Should().Be(1);
    }

    // ====================================================================
    // PublishFormDefinition
    // ====================================================================

    [Fact]
    public async Task PublishFormDefinition_ChangesStatusFromDraftToPublished()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(PublishFormDefinition_ChangesStatusFromDraftToPublished),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "待发布表单",
                    Code = "PUB_FORM",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
            });

        var handler = new PublishFormDefinitionCommandHandler(db);
        var command = new PublishFormDefinitionCommand(Id: formId);

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

        // Assert
        var entity = await db.FormDefinitions.FindAsync(formId);
        entity.Should().NotBeNull();
        entity!.Status.Should().Be(FormStatus.Published);
    }

    [Fact]
    public async Task PublishFormDefinition_AlreadyPublished_ThrowsBusinessException()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(PublishFormDefinition_AlreadyPublished_ThrowsBusinessException),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "已发布表单",
                    Code = "PUB_AGAIN",
                    Version = 1,
                    Status = FormStatus.Published,
                    SchemaJson = "{}",
                });
            });

        var handler = new PublishFormDefinitionCommandHandler(db);
        var command = new PublishFormDefinitionCommand(Id: formId);

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

        // Assert
        await act.Should().ThrowAsync<BusinessException>()
            .WithMessage("*已发布*");
    }

    // ====================================================================
    // DisableFormDefinition
    // ====================================================================

    [Fact]
    public async Task DisableFormDefinition_ChangesStatusToDisabled()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(DisableFormDefinition_ChangesStatusToDisabled),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "待禁用表单",
                    Code = "DIS_FORM",
                    Version = 1,
                    Status = FormStatus.Published,
                    SchemaJson = "{}",
                });
            });

        var handler = new DisableFormDefinitionCommandHandler(db);
        var command = new DisableFormDefinitionCommand(Id: formId);

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

        // Assert
        var entity = await db.FormDefinitions.FindAsync(formId);
        entity.Should().NotBeNull();
        entity!.Status.Should().Be(FormStatus.Disabled);
    }

    // ====================================================================
    // GetFormDefinitionList
    // ====================================================================

    [Fact]
    public async Task GetFormDefinitionList_ReturnsPagedResults()
    {
        // Arrange
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(GetFormDefinitionList_ReturnsPagedResults),
            seedAction: ctx =>
            {
                for (int i = 1; i <= 15; i++)
                {
                    ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                    {
                        Id = Guid.NewGuid(),
                        Name = $"表单_{i:D3}",
                        Code = $"FORM_{i:D3}",
                        Version = 1,
                        Status = FormStatus.Draft,
                        SchemaJson = "{}",
                    });
                }
            });

        var handler = new GetFormDefinitionListQueryHandler(db);
        var query = new GetFormDefinitionListQuery(PageIndex: 1, PageSize: 10, Status: null);

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

        // Assert
        result.Should().NotBeNull();
        result.Items.Should().HaveCount(10);
        result.TotalCount.Should().Be(15);
        result.PageIndex.Should().Be(1);
        result.PageSize.Should().Be(10);
    }

    [Fact]
    public async Task GetFormDefinitionList_FiltersByStatus()
    {
        // Arrange
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(GetFormDefinitionList_FiltersByStatus),
            seedAction: ctx =>
            {
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = Guid.NewGuid(),
                    Name = "草稿表单",
                    Code = "DRAFT_001",
                    Version = 1,
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                });
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = Guid.NewGuid(),
                    Name = "已发布表单A",
                    Code = "PUB_001",
                    Version = 1,
                    Status = FormStatus.Published,
                    SchemaJson = "{}",
                });
                ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
                {
                    Id = Guid.NewGuid(),
                    Name = "已发布表单B",
                    Code = "PUB_002",
                    Version = 1,
                    Status = FormStatus.Published,
                    SchemaJson = "{}",
                });
            });

        var handler = new GetFormDefinitionListQueryHandler(db);
        var query = new GetFormDefinitionListQuery(PageIndex: 1, PageSize: 10, Status: FormStatus.Published);

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

        // Assert
        result.Should().NotBeNull();
        result.Items.Should().HaveCount(2);
        result.Items.Should().OnlyContain(dto => dto.Status == FormStatus.Published);
    }

    // ====================================================================
    // GetFormDefinitionById
    // ====================================================================

    [Fact]
    public async Task GetFormDefinitionById_ReturnsDefinitionWithFields()
    {
        // Arrange
        var formId = Guid.NewGuid();
        await using var db = await _fixture.CreateDbContextWithSeedAsync(
            testName: nameof(GetFormDefinitionById_ReturnsDefinitionWithFields),
            seedAction: ctx =>
            {
                var form = new Domain.Entities.FormDefinition
                {
                    Id = formId,
                    Name = "带字段的表单",
                    Code = "WITH_FIELDS",
                    Version = 1,
                    Description = "包含多个字段的表单",
                    Status = FormStatus.Draft,
                    SchemaJson = "{}",
                };
                form.Fields.Add(new Domain.Entities.FormDefinitionField
                {
                    Id = Guid.NewGuid(),
                    FormDefinitionId = formId,
                    FieldKey = "title",
                    Label = "标题",
                    FieldType = FieldType.Input,
                    Required = true,
                    Config = "{}",
                    SortOrder = 0,
                });
                form.Fields.Add(new Domain.Entities.FormDefinitionField
                {
                    Id = Guid.NewGuid(),
                    FormDefinitionId = formId,
                    FieldKey = "content",
                    Label = "内容",
                    FieldType = FieldType.RichText,
                    Required = false,
                    Config = "{}",
                    SortOrder = 1,
                });
                ctx.FormDefinitions.Add(form);
            });

        var handler = new GetFormDefinitionByIdQueryHandler(db);
        var query = new GetFormDefinitionByIdQuery(Id: formId);

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

        // Assert
        result.Should().NotBeNull();
        result.Id.Should().Be(formId);
        result.Name.Should().Be("带字段的表单");
        result.Code.Should().Be("WITH_FIELDS");
        result.Fields.Should().NotBeNull();
        result.Fields.Should().HaveCount(2);
        result.Fields.Select(f => f.FieldKey).Should().Contain(["title", "content"]);
    }

    [Fact]
    public async Task GetFormDefinitionById_NotFound_ThrowsNotFoundException()
    {
        // Arrange
        await using var db = _fixture.CreateDbContext(testName: nameof(GetFormDefinitionById_NotFound_ThrowsNotFoundException));
        var handler = new GetFormDefinitionByIdQueryHandler(db);
        var query = new GetFormDefinitionByIdQuery(Id: Guid.NewGuid());

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

        // Assert
        await act.Should().ThrowAsync<NotFoundException>()
            .WithMessage("*表单*不存在*");
    }
}
