using Books.Api.EventFlow.Repositories;
using Books.Api.Tests.Helpers;
using Books.Api.Tests.Infrastructure;
using AwesomeAssertions;
using Microsoft.Extensions.DependencyInjection;

namespace Books.Api.Tests.GraphQL;

/// <summary>
/// Integration tests for JournalEntryDraft (Kassekladde) GraphQL operations.
/// </summary>
[Trait("Category", "Integration")]
public class JournalEntryDraftGraphQLTests(TestWebApplicationFactory factory)
    : IntegrationTestBase(factory)
{
    #region Create Draft Tests

    [Fact]
    public async Task Mutation_CreateJournalEntryDraft_CreatesSuccessfully()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Draft Test Company");

        // Act
        var response = await graphqlClient.MutateAsync<CreateDraftResponse>("""
            mutation CreateDraft($input: CreateJournalEntryDraftInput!) {
                createJournalEntryDraft(input: $input) {
                    id
                    companyId
                    name
                    status
                    createdBy
                }
            }
            """,
            new
            {
                input = new
                {
                    companyId,
                    name = "Januar Udgifter"
                }
            });

        // Assert
        response.EnsureNoErrors();
        response.Data.Should().NotBeNull();
        response.Data!.CreateJournalEntryDraft.Should().NotBeNull();
        response.Data.CreateJournalEntryDraft!.Name.Should().Be("Januar Udgifter");
        response.Data.CreateJournalEntryDraft.Status.Should().Be("active");
        response.Data.CreateJournalEntryDraft.CompanyId.Should().Be(companyId);
        response.Data.CreateJournalEntryDraft.Id.Should().StartWith("journalentrydraft-");
    }

    [Fact]
    public async Task Mutation_CreateJournalEntryDraft_FailsWithoutCompanyHeader()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        // Note: We're NOT setting X-Company-Id header

        // Act
        var response = await graphqlClient.MutateAsync<CreateDraftResponse>("""
            mutation CreateDraft($input: CreateJournalEntryDraftInput!) {
                createJournalEntryDraft(input: $input) {
                    id
                }
            }
            """,
            new
            {
                input = new
                {
                    companyId = "some-company-id",
                    name = "Test Draft"
                }
            });

        // Assert
        response.HasErrors.Should().BeTrue();
    }

    [Fact]
    public async Task Mutation_CreateJournalEntryDraft_FailsWithEmptyName()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Empty Name Test Company");

        // Act
        var response = await graphqlClient.MutateAsync<CreateDraftResponse>("""
            mutation CreateDraft($input: CreateJournalEntryDraftInput!) {
                createJournalEntryDraft(input: $input) {
                    id
                }
            }
            """,
            new
            {
                input = new
                {
                    companyId,
                    name = "   " // Whitespace only
                }
            });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("Draft name is required");
    }

    #endregion

    #region Update Draft Tests

    [Fact]
    public async Task Mutation_UpdateJournalEntryDraft_UpdatesSuccessfully()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Update Draft Test Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "Original Name");

        // Wait for eventual consistency
        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        // Act
        var response = await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) {
                    id
                    name
                    documentDate
                    description
                    lines {
                        lineNumber
                        accountId
                        debitAmount
                        creditAmount
                        description
                    }
                }
            }
            """,
            new
            {
                input = new
                {
                    id = draftId,
                    name = "Updated Name",
                    documentDate = "2025-01-15",
                    description = "Test beskrivelse",
                    lines = new[]
                    {
                        new { lineNumber = 1, accountId = (string?)null, debitAmount = 1000m, creditAmount = 0m, description = "Debet linje" },
                        new { lineNumber = 2, accountId = (string?)null, debitAmount = 0m, creditAmount = 1000m, description = "Kredit linje" }
                    }
                }
            });

        // Assert
        response.EnsureNoErrors();
        response.Data!.UpdateJournalEntryDraft!.Name.Should().Be("Updated Name");
        response.Data.UpdateJournalEntryDraft.Description.Should().Be("Test beskrivelse");
        response.Data.UpdateJournalEntryDraft.Lines.Should().HaveCount(2);
        response.Data.UpdateJournalEntryDraft.Lines![0].DebitAmount.Should().Be(1000m);
        response.Data.UpdateJournalEntryDraft.Lines[1].CreditAmount.Should().Be(1000m);
    }

    [Fact]
    public async Task Mutation_UpdateJournalEntryDraft_FailsForNonExistentDraft()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Non-existent Draft Company");
        var nonExistentId = $"journalentrydraft-{Guid.NewGuid():D}";

        // Act
        var response = await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) {
                    id
                }
            }
            """,
            new
            {
                input = new
                {
                    id = nonExistentId,
                    name = "Test",
                    lines = Array.Empty<object>()
                }
            });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("not found");
    }

    #endregion

    #region Query Tests

    [Fact]
    public async Task Query_JournalEntryDrafts_ReturnsActiveDrafts()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Query Drafts Test Company");

        // Create multiple drafts
        await CreateDraftAsync(graphqlClient, companyId, "Draft 1");
        await CreateDraftAsync(graphqlClient, companyId, "Draft 2");
        await CreateDraftAsync(graphqlClient, companyId, "Draft 3");

        // Wait for eventual consistency
        var drafts = await Eventually.GetListAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetActiveByCompanyIdAsync(companyId);
        }, 3, timeout: TimeSpan.FromSeconds(10));

        // Act
        var response = await graphqlClient.QueryAsync<DraftsResponse>("""
            query Drafts($companyId: ID!) {
                journalEntryDrafts(companyId: $companyId) {
                    id
                    name
                    status
                }
            }
            """,
            new { companyId });

        // Assert
        response.EnsureNoErrors();
        response.Data!.JournalEntryDrafts.Should().HaveCount(3);
        response.Data.JournalEntryDrafts.Should().AllSatisfy(d => d.Status.Should().Be("active"));
    }

    [Fact]
    public async Task Query_JournalEntryDraft_ReturnsSingleDraft()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Single Draft Query Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "Query Test Draft");

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        // Act
        var response = await graphqlClient.QueryAsync<SingleDraftResponse>("""
            query Draft($id: ID!) {
                journalEntryDraft(id: $id) {
                    id
                    name
                    companyId
                    status
                    lines {
                        lineNumber
                    }
                }
            }
            """,
            new { id = draftId });

        // Assert
        response.EnsureNoErrors();
        response.Data!.JournalEntryDraft.Should().NotBeNull();
        response.Data.JournalEntryDraft!.Name.Should().Be("Query Test Draft");
        response.Data.JournalEntryDraft.CompanyId.Should().Be(companyId);
    }

    [Fact]
    public async Task Query_JournalEntryDrafts_FailsWithoutCompanyHeader()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        // Not setting company header

        // Act
        var response = await graphqlClient.QueryAsync<DraftsResponse>("""
            query Drafts($companyId: ID!) {
                journalEntryDrafts(companyId: $companyId) {
                    id
                }
            }
            """,
            new { companyId = "some-id" });

        // Assert
        response.HasErrors.Should().BeTrue();
    }

    #endregion

    #region Discard Draft Tests

    [Fact]
    public async Task Mutation_DiscardJournalEntryDraft_DiscardsSuccessfully()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Discard Draft Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "To Be Discarded");

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        // Act
        var response = await graphqlClient.MutateAsync<DiscardDraftResponse>("""
            mutation DiscardDraft($id: ID!) {
                discardJournalEntryDraft(id: $id) {
                    id
                    status
                }
            }
            """,
            new { id = draftId });

        // Assert
        response.EnsureNoErrors();
        response.Data!.DiscardJournalEntryDraft!.Status.Should().Be("discarded");

        // Verify it's no longer in active drafts
        var activeDrafts = await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            var drafts = await repo.GetActiveByCompanyIdAsync(companyId);
            return drafts.Any(d => d.Id == draftId) ? null : drafts;
        });

        activeDrafts.Should().NotContain(d => d.Id == draftId);
    }

    [Fact]
    public async Task Mutation_DiscardJournalEntryDraft_FailsForAlreadyDiscarded()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Double Discard Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "Discard Twice");

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        // Discard once
        await graphqlClient.MutateAsync<DiscardDraftResponse>("""
            mutation DiscardDraft($id: ID!) {
                discardJournalEntryDraft(id: $id) { id status }
            }
            """,
            new { id = draftId });

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            var draft = await repo.GetByIdAsync(draftId);
            return draft?.Status == "discarded" ? draft : null;
        });

        // Act - Try to discard again
        var response = await graphqlClient.MutateAsync<DiscardDraftResponse>("""
            mutation DiscardDraft($id: ID!) {
                discardJournalEntryDraft(id: $id) { id status }
            }
            """,
            new { id = draftId });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("has been discarded");
    }

    #endregion

    #region Post Draft Tests

    [Fact]
    public async Task Mutation_PostJournalEntryDraft_FailsWithUnbalancedLines()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Unbalanced Post Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "Unbalanced Draft");

        // Wait for standard accounts
        var accounts = await Eventually.GetListAsync(async () =>
        {
            var repo = GetService<IAccountRepository>();
            return await repo.GetActiveByCompanyIdAsync(companyId);
        }, 10, timeout: TimeSpan.FromSeconds(30));

        var accountId1 = accounts.First().Id;
        var accountId2 = accounts.Skip(1).First().Id;

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        // Create fiscal year for posting
        var fiscalYearId = await CreateFiscalYearAsync(graphqlClient, companyId);

        // Update with unbalanced lines
        await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    id = draftId,
                    fiscalYearId,
                    lines = new[]
                    {
                        new { lineNumber = 1, accountId = accountId1, debitAmount = 1000m, creditAmount = 0m },
                        new { lineNumber = 2, accountId = accountId2, debitAmount = 0m, creditAmount = 500m } // Unbalanced!
                    }
                }
            });

        await Task.Delay(500); // Wait for update

        // Act - Try to post unbalanced draft
        var response = await graphqlClient.MutateAsync<PostDraftResponse>("""
            mutation PostDraft($id: ID!) {
                postJournalEntryDraft(id: $id) {
                    id
                    status
                    transactionId
                }
            }
            """,
            new { id = draftId });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("must equal credits");
    }

    [Fact]
    public async Task Mutation_PostJournalEntryDraft_FailsWithoutFiscalYear()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "No Fiscal Year Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "No Fiscal Year Draft");

        // Wait for standard accounts
        var accounts = await Eventually.GetListAsync(async () =>
        {
            var repo = GetService<IAccountRepository>();
            return await repo.GetActiveByCompanyIdAsync(companyId);
        }, 10, timeout: TimeSpan.FromSeconds(30));

        var accountId1 = accounts.First().Id;
        var accountId2 = accounts.Skip(1).First().Id;

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        // Update with balanced lines but NO fiscal year
        await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    id = draftId,
                    // No fiscalYearId!
                    lines = new[]
                    {
                        new { lineNumber = 1, accountId = accountId1, debitAmount = 1000m, creditAmount = 0m },
                        new { lineNumber = 2, accountId = accountId2, debitAmount = 0m, creditAmount = 1000m }
                    }
                }
            });

        await Task.Delay(500);

        // Act
        var response = await graphqlClient.MutateAsync<PostDraftResponse>("""
            mutation PostDraft($id: ID!) {
                postJournalEntryDraft(id: $id) {
                    id
                    status
                }
            }
            """,
            new { id = draftId });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("Fiscal year is required");
    }

    [Fact]
    public async Task Mutation_PostJournalEntryDraft_FailsWithMissingAccounts()
    {
        // Arrange
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Missing Account Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "Missing Account Draft");

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        var fiscalYearId = await CreateFiscalYearAsync(graphqlClient, companyId);

        // Update with balanced lines but missing account IDs
        await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    id = draftId,
                    fiscalYearId,
                    lines = new[]
                    {
                        new { lineNumber = 1, accountId = (string?)null, debitAmount = 1000m, creditAmount = 0m },
                        new { lineNumber = 2, accountId = (string?)null, debitAmount = 0m, creditAmount = 1000m }
                    }
                }
            });

        await Task.Delay(500);

        // Act
        var response = await graphqlClient.MutateAsync<PostDraftResponse>("""
            mutation PostDraft($id: ID!) {
                postJournalEntryDraft(id: $id) {
                    id
                }
            }
            """,
            new { id = draftId });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("must have an account");
    }

    [Fact(Skip = "Requires Ledger period setup which is complex; domain logic is tested in JournalEntryDraftAggregateTests")]
    public async Task Mutation_UpdateJournalEntryDraft_FailsForPostedDraft()
    {
        // Arrange - Create and post a draft first
        var graphqlClient = new GraphQLTestClient(Client);
        var companyId = await CreateCompanyAsync(graphqlClient, "Posted Update Company");
        var draftId = await CreateDraftAsync(graphqlClient, companyId, "To Be Posted Then Updated");

        // Wait for accounts
        var accounts = await Eventually.GetListAsync(async () =>
        {
            var repo = GetService<IAccountRepository>();
            return await repo.GetActiveByCompanyIdAsync(companyId);
        }, 10, timeout: TimeSpan.FromSeconds(30));

        var accountId1 = accounts.First().Id;
        var accountId2 = accounts.Skip(1).First().Id;

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            return await repo.GetByIdAsync(draftId);
        });

        var fiscalYearId = await CreateFiscalYearAsync(graphqlClient, companyId);

        // Update draft with valid data for posting
        await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    id = draftId,
                    fiscalYearId,
                    lines = new[]
                    {
                        new { lineNumber = 1, accountId = accountId1, debitAmount = 1000m, creditAmount = 0m },
                        new { lineNumber = 2, accountId = accountId2, debitAmount = 0m, creditAmount = 1000m }
                    }
                }
            });

        await Task.Delay(500);

        // Post the draft
        var postResponse = await graphqlClient.MutateAsync<PostDraftResponse>("""
            mutation PostDraft($id: ID!) {
                postJournalEntryDraft(id: $id) { id status }
            }
            """,
            new { id = draftId });

        postResponse.EnsureNoErrors();

        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IJournalEntryDraftRepository>();
            var draft = await repo.GetByIdAsync(draftId);
            return draft?.Status == "posted" ? draft : null;
        });

        // Act - Try to update the posted draft
        var response = await graphqlClient.MutateAsync<UpdateDraftResponse>("""
            mutation UpdateDraft($input: UpdateJournalEntryDraftInput!) {
                updateJournalEntryDraft(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    id = draftId,
                    name = "Should Fail",
                    lines = Array.Empty<object>()
                }
            });

        // Assert
        response.HasErrors.Should().BeTrue();
        var errorInfo = string.Join(" ", response.Errors!.Select(e =>
            e.Message + " " + (e.Extensions?.GetValueOrDefault("details")?.ToString() ?? "")));
        errorInfo.Should().Contain("already been posted");
    }

    #endregion

    #region Helper Methods

    private async Task<string> CreateCompanyAsync(GraphQLTestClient client, string name)
    {
        var response = await client.MutateAsync<CreateCompanyResponse>("""
            mutation CreateCompany($input: CreateCompanyInput!) {
                createCompany(input: $input) { id }
            }
            """,
            new { input = new { name } });

        response.EnsureNoErrors();
        var companyId = response.Data!.CreateCompany!.Id;
        client.SetCompanyId(companyId);
        return companyId;
    }

    private async Task<string> CreateDraftAsync(GraphQLTestClient client, string companyId, string name)
    {
        var response = await client.MutateAsync<CreateDraftResponse>("""
            mutation CreateDraft($input: CreateJournalEntryDraftInput!) {
                createJournalEntryDraft(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    companyId,
                    name
                }
            });

        response.EnsureNoErrors();
        return response.Data!.CreateJournalEntryDraft!.Id;
    }

    private async Task<string> CreateFiscalYearAsync(GraphQLTestClient client, string companyId)
    {
        var response = await client.MutateAsync<CreateFiscalYearResponse>("""
            mutation CreateFiscalYear($input: CreateFiscalYearInput!) {
                createFiscalYear(input: $input) { id }
            }
            """,
            new
            {
                input = new
                {
                    companyId,
                    name = "2025",
                    startDate = "2025-01-01T00:00:00",
                    endDate = "2025-12-31T00:00:00",
                    isFirstFiscalYear = true
                }
            });

        response.EnsureNoErrors();
        var fiscalYearId = response.Data!.CreateFiscalYear!.Id;

        // Wait for fiscal year to be created
        await Eventually.GetAsync(async () =>
        {
            var repo = GetService<IFiscalYearRepository>();
            return await repo.GetByIdAsync(fiscalYearId);
        });

        return fiscalYearId;
    }

    #endregion

    #region Response DTOs

    private class CreateDraftResponse { public DraftDto? CreateJournalEntryDraft { get; set; } }
    private class UpdateDraftResponse { public DraftDto? UpdateJournalEntryDraft { get; set; } }
    private class PostDraftResponse { public DraftDto? PostJournalEntryDraft { get; set; } }
    private class DiscardDraftResponse { public DraftDto? DiscardJournalEntryDraft { get; set; } }
    private class DraftsResponse { public List<DraftDto> JournalEntryDrafts { get; set; } = []; }
    private class SingleDraftResponse { public DraftDto? JournalEntryDraft { get; set; } }
    private class CreateCompanyResponse { public CompanyDto? CreateCompany { get; set; } }
    private class CreateFiscalYearResponse { public FiscalYearDto? CreateFiscalYear { get; set; } }

    private class DraftDto
    {
        public string Id { get; set; } = string.Empty;
        public string CompanyId { get; set; } = string.Empty;
        public string Name { get; set; } = string.Empty;
        public string? DocumentDate { get; set; }
        public string? Description { get; set; }
        public string? FiscalYearId { get; set; }
        public string Status { get; set; } = string.Empty;
        public string? TransactionId { get; set; }
        public string CreatedBy { get; set; } = string.Empty;
        public List<DraftLineDto>? Lines { get; set; }
    }

    private class DraftLineDto
    {
        public int LineNumber { get; set; }
        public string? AccountId { get; set; }
        public decimal DebitAmount { get; set; }
        public decimal CreditAmount { get; set; }
        public string? Description { get; set; }
    }

    private class CompanyDto { public string Id { get; set; } = string.Empty; }
    private class FiscalYearDto { public string Id { get; set; } = string.Empty; }

    #endregion
}
