using Books.Api.Domain;
using Books.Api.Domain.JournalEntryDrafts;
using AwesomeAssertions;

namespace Books.Api.Tests.Domain;

/// <summary>
/// Unit tests for VatCalculationService.
/// Tests Danish VAT calculation logic for SKAT compliance.
/// </summary>
[Trait("Category", "Unit")]
public class VatCalculationServiceTests
{
    private readonly VatCalculationService _sut = new();
    private const string InputVatAccount = "5610";   // Købsmoms (indgående moms)
    private const string OutputVatAccount = "5611";  // Salgsmoms (udgående moms)

    #region Sales (U25) Tests

    [Fact]
    public void CalculateVat_SalesWithU25_Exclusive_CalculatesCorrectVat()
    {
        // Arrange - Sale of 1000 kr excl. VAT
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 1000m, "Salg af varer", "U25")  // Credit = sales revenue
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        result.VatLines.Should().HaveCount(1);
        var vatLine = result.VatLines[0];
        vatLine.CreditAmount.Should().Be(250m);  // 25% of 1000
        vatLine.DebitAmount.Should().Be(0m);
        vatLine.AccountId.Should().Be(OutputVatAccount);
        vatLine.VatCode.Should().Be("U25");

        result.VatSummary.Should().HaveCount(1);
        result.VatSummary[0].BaseAmount.Should().Be(1000m);
        result.VatSummary[0].VatAmount.Should().Be(250m);
        result.VatSummary[0].IsInputVat.Should().BeFalse();
    }

    [Fact]
    public void CalculateVat_SalesWithU25_Inclusive_ExtractsCorrectVat()
    {
        // Arrange - Sale of 1250 kr incl. VAT (1000 + 250 VAT)
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 1250m, "Salg inkl moms", "U25")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Inclusive, InputVatAccount, OutputVatAccount);

        // Assert
        var vatLine = result.VatLines[0];
        vatLine.CreditAmount.Should().Be(250m);  // 1250 * 0.25 / 1.25 = 250
        vatLine.DebitAmount.Should().Be(0m);

        result.VatSummary[0].BaseAmount.Should().Be(1000m);  // 1250 - 250
        result.VatSummary[0].VatAmount.Should().Be(250m);
    }

    #endregion

    #region Purchases (I25) Tests

    [Fact]
    public void CalculateVat_PurchaseWithI25_Exclusive_CalculatesCorrectVat()
    {
        // Arrange - Purchase of 1000 kr excl. VAT
        var lines = new List<DraftLine>
        {
            new(1, "2000", 1000m, 0m, "Køb af varer", "I25")  // Debit = expense
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        result.VatLines.Should().HaveCount(1);
        var vatLine = result.VatLines[0];
        vatLine.DebitAmount.Should().Be(250m);  // 25% of 1000 - debit because it's an asset (receivable)
        vatLine.CreditAmount.Should().Be(0m);
        vatLine.AccountId.Should().Be(InputVatAccount);
        vatLine.VatCode.Should().Be("I25");

        result.VatSummary[0].IsInputVat.Should().BeTrue();
    }

    [Fact]
    public void CalculateVat_PurchaseWithI25_Inclusive_ExtractsCorrectVat()
    {
        // Arrange - Purchase of 1250 kr incl. VAT
        var lines = new List<DraftLine>
        {
            new(1, "2000", 1250m, 0m, "Køb inkl moms", "I25")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Inclusive, InputVatAccount, OutputVatAccount);

        // Assert
        var vatLine = result.VatLines[0];
        vatLine.DebitAmount.Should().Be(250m);
        vatLine.CreditAmount.Should().Be(0m);

        result.VatSummary[0].BaseAmount.Should().Be(1000m);
        result.VatSummary[0].VatAmount.Should().Be(250m);
    }

    #endregion

    #region No VAT Tests

    [Fact]
    public void CalculateVat_NoVatCode_ReturnsNoVatLines()
    {
        // Arrange
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 1000m, "Momsfrit salg")  // No VAT code
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        result.VatLines.Should().BeEmpty();
        result.VatSummary.Should().BeEmpty();
        result.TotalVatAmount.Should().Be(0m);
    }

    [Fact]
    public void CalculateVat_WithINGEN_ReturnsNoVatLines()
    {
        // Arrange
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 1000m, "Momsfrit", "INGEN")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        result.VatLines.Should().BeEmpty();
    }

    [Fact]
    public void CalculateVat_WithUEU_ReturnsNoVatLines()
    {
        // Arrange - EU sale (no VAT charged)
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 10000m, "EU-salg af varer", "UEU")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert - UEU has 0% rate, so no VAT line generated
        result.VatLines.Should().BeEmpty();
    }

    #endregion

    #region Multiple Lines Tests

    [Fact]
    public void CalculateVat_MultipleLines_AggregatesByVatCode()
    {
        // Arrange
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 1000m, "Salg 1", "U25"),
            new(2, "1000", 0m, 2000m, "Salg 2", "U25"),
            new(3, "2000", 500m, 0m, "Køb 1", "I25")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        result.VatLines.Should().HaveCount(3);

        // Check VAT summary aggregates correctly
        result.VatSummary.Should().HaveCount(2);  // U25 and I25

        var u25Summary = result.VatSummary.First(s => s.VatCode == "U25");
        u25Summary.BaseAmount.Should().Be(3000m);  // 1000 + 2000
        u25Summary.VatAmount.Should().Be(750m);    // 25% of 3000

        var i25Summary = result.VatSummary.First(s => s.VatCode == "I25");
        i25Summary.BaseAmount.Should().Be(500m);
        i25Summary.VatAmount.Should().Be(125m);    // 25% of 500
    }

    #endregion

    #region Rounding Tests

    [Fact]
    public void CalculateVat_OddAmount_RoundsCorrectly()
    {
        // Arrange - Amount that results in rounding
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 100.01m, "Salg med øreafrunding", "U25")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        // 100.01 * 0.25 = 25.0025, rounded to 25.00
        result.VatLines[0].CreditAmount.Should().Be(25.00m);
    }

    [Fact]
    public void CalculateVat_InclusiveOddAmount_RoundsCorrectly()
    {
        // Arrange - 125.01 incl VAT
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 125.01m, "Salg inkl moms", "U25")
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Inclusive, InputVatAccount, OutputVatAccount);

        // Assert
        // 125.01 * 0.25 / 1.25 = 25.002, rounded to 25.00
        result.VatLines[0].CreditAmount.Should().Be(25.00m);
        result.VatSummary[0].BaseAmount.Should().Be(100.01m);  // 125.01 - 25.00
    }

    #endregion

    #region Total VAT Tests

    [Fact]
    public void CalculateVat_MixedInputOutput_CalculatesTotalCorrectly()
    {
        // Arrange - Sales and purchases
        var lines = new List<DraftLine>
        {
            new(1, "1000", 0m, 1000m, "Salg", "U25"),    // Output VAT: 250 credit
            new(2, "2000", 400m, 0m, "Køb", "I25")       // Input VAT: 100 debit
        };

        // Act
        var result = _sut.CalculateVat(lines, VatCalculationMode.Exclusive, InputVatAccount, OutputVatAccount);

        // Assert
        // Total = debit (input VAT asset) - credit (output VAT liability)
        // = 100 - 250 = -150 (negative means net VAT payable to SKAT)
        result.TotalVatAmount.Should().Be(-150m);
    }

    #endregion
}
