using System.Net;
using System.Text;
using Books.Api.AiBookkeeper;
using AwesomeAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Moq.Protected;

namespace Books.Api.Tests.AiBookkeeper;

/// <summary>
/// Unit tests for AiBookkeeperClient.
/// Tests JSON parsing and suggestion generation from extraction data.
/// </summary>
[Trait("Category", "Unit")]
public class AiBookkeeperClientTests
{
    [Fact]
    public async Task ProcessDocument_WithExtractionButNoSuggestion_GeneratesSuggestionFromExtraction()
    {
        // Arrange
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "invoice",
                "vendor": { "name": "Test Leverandør", "cvr": "12345678" },
                "invoiceNumber": "INV-001",
                "date": "2024-01-15",
                "totalAmount": 1250.00,
                "amountExVat": 1000.00,
                "vatAmount": 250.00,
                "currency": "DKK"
            }
        }
        """;

        var client = CreateClientWithResponse(json);
        var chartOfAccounts = CreateChartOfAccounts();

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            chartOfAccounts);

        // Assert
        result.Success.Should().BeTrue();
        result.Extraction.Should().NotBeNull();
        result.Extraction!.Vendor.Should().Be("Test Leverandør");
        result.Extraction.TotalAmount.Should().Be(1250.00m);
        result.Extraction.AmountExVat.Should().Be(1000.00m);
        result.Extraction.VatAmount.Should().Be(250.00m);

        // Suggestion should be generated
        result.Suggestion.Should().NotBeNull();
        result.Suggestion!.Description.Should().Be("Test Leverandør");
        result.Suggestion.Confidence.Should().Be(0.7m);
        result.Suggestion.Lines.Should().HaveCount(3);

        // Expense line (debit amountExVat) - Erhvervsstyrelsen standard 1610
        var expenseLine = result.Suggestion.Lines[0];
        expenseLine.StandardAccountNumber.Should().Be("1610");
        expenseLine.DebitAmount.Should().Be(1000.00m);
        expenseLine.CreditAmount.Should().Be(0m);
        expenseLine.VatCode.Should().Be("I25"); // 25% VAT detected

        // VAT line (debit vatAmount) - Erhvervsstyrelsen standard 7680
        var vatLine = result.Suggestion.Lines[1];
        vatLine.StandardAccountNumber.Should().Be("7680");
        vatLine.DebitAmount.Should().Be(250.00m);
        vatLine.CreditAmount.Should().Be(0m);

        // Creditor line (credit totalAmount) - Erhvervsstyrelsen standard 7350
        var creditorLine = result.Suggestion.Lines[2];
        creditorLine.StandardAccountNumber.Should().Be("7350");
        creditorLine.DebitAmount.Should().Be(0m);
        creditorLine.CreditAmount.Should().Be(1250.00m);
    }

    [Fact]
    public async Task ProcessDocument_WithNestedTotals_ExtractsTotalsCorrectly()
    {
        // Arrange - AI service returns totals as nested object
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "invoice",
                "vendor": { "name": "Nested Totals Vendor" },
                "totals": {
                    "grandTotal": 625.00,
                    "subtotal": 500.00,
                    "vatTotal": 125.00
                }
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert
        result.Extraction.Should().NotBeNull();
        result.Extraction!.TotalAmount.Should().Be(625.00m);
        result.Extraction.AmountExVat.Should().Be(500.00m);
        result.Extraction.VatAmount.Should().Be(125.00m);

        // Suggestion should be generated with correct amounts
        result.Suggestion.Should().NotBeNull();
        result.Suggestion!.Lines.Should().HaveCount(3);
        result.Suggestion.Lines[0].DebitAmount.Should().Be(500.00m);  // Expense
        result.Suggestion.Lines[1].DebitAmount.Should().Be(125.00m);  // VAT
        result.Suggestion.Lines[2].CreditAmount.Should().Be(625.00m); // Creditor
    }

    [Fact]
    public async Task ProcessDocument_WithoutVat_GeneratesTwoLineEntry()
    {
        // Arrange - No VAT in the extraction
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "receipt",
                "vendor": { "name": "No VAT Vendor" },
                "totalAmount": 500.00,
                "amountExVat": 500.00,
                "vatAmount": 0
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert - Should only have 2 lines (no VAT line)
        result.Suggestion.Should().NotBeNull();
        result.Suggestion!.Lines.Should().HaveCount(2);

        // Expense line - Erhvervsstyrelsen standard 1610
        result.Suggestion.Lines[0].StandardAccountNumber.Should().Be("1610");
        result.Suggestion.Lines[0].DebitAmount.Should().Be(500.00m);
        result.Suggestion.Lines[0].VatCode.Should().BeNull();

        // Creditor line - Erhvervsstyrelsen standard 7350
        result.Suggestion.Lines[1].StandardAccountNumber.Should().Be("7350");
        result.Suggestion.Lines[1].CreditAmount.Should().Be(500.00m);
    }

    [Fact]
    public async Task ProcessDocument_WithOnlyTotalAmount_UsesSameAmountForExpense()
    {
        // Arrange - Only total amount, no breakdown
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "receipt",
                "vendor": { "name": "Simple Receipt" },
                "totalAmount": 299.00
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert
        result.Suggestion.Should().NotBeNull();
        result.Suggestion!.Lines.Should().HaveCount(2);

        // Expense line uses total (no VAT breakdown)
        result.Suggestion.Lines[0].DebitAmount.Should().Be(299.00m);

        // Creditor line
        result.Suggestion.Lines[1].CreditAmount.Should().Be(299.00m);
    }

    [Fact]
    public async Task ProcessDocument_WithNoTotalAmount_DoesNotGenerateSuggestion()
    {
        // Arrange - No amounts in extraction
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "unknown",
                "vendor": { "name": "Unknown Document" }
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert - No suggestion generated (can't book without amounts)
        result.Extraction.Should().NotBeNull();
        result.Suggestion.Should().BeNull();
    }

    [Fact]
    public async Task ProcessDocument_WithExistingSuggestion_UsesProvidedSuggestion()
    {
        // Arrange - AI provides its own suggestion
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "invoice",
                "vendor": { "name": "Test Vendor" },
                "totalAmount": 1000.00
            },
            "suggestedBooking": {
                "description": "AI Suggested Description",
                "confidence": 0.95,
                "lines": [
                    { "standardAccountNumber": "7320", "accountName": "Software", "debit": 800.00, "credit": 0 },
                    { "standardAccountNumber": "6320", "accountName": "Moms", "debit": 200.00, "credit": 0 },
                    { "standardAccountNumber": "6930", "accountName": "Kreditor", "debit": 0, "credit": 1000.00 }
                ]
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert - Should use AI's suggestion, not generate our own
        result.Suggestion.Should().NotBeNull();
        result.Suggestion!.Description.Should().Be("AI Suggested Description");
        result.Suggestion.Confidence.Should().Be(0.95m);
        result.Suggestion.Lines[0].StandardAccountNumber.Should().Be("7320");
    }

    [Fact]
    public async Task ProcessDocument_WithNoVendorName_UsesDefaultDescription()
    {
        // Arrange - No vendor name
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "receipt",
                "totalAmount": 150.00
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert
        result.Suggestion.Should().NotBeNull();
        result.Suggestion!.Description.Should().Be("Udgift");
    }

    [Fact]
    public async Task ProcessDocument_GeneratedSuggestion_IsBalanced()
    {
        // Arrange
        var json = """
        {
            "success": true,
            "extraction": {
                "documentType": "invoice",
                "vendor": { "name": "Balance Test" },
                "totalAmount": 1875.50,
                "amountExVat": 1500.40,
                "vatAmount": 375.10
            }
        }
        """;

        var client = CreateClientWithResponse(json);

        // Act
        var result = await client.ProcessDocumentAsync(
            new MemoryStream([1, 2, 3]),
            "test.pdf",
            "application/pdf",
            CreateChartOfAccounts());

        // Assert - Total debits should equal total credits
        result.Suggestion.Should().NotBeNull();
        var totalDebits = result.Suggestion!.Lines.Sum(l => l.DebitAmount);
        var totalCredits = result.Suggestion.Lines.Sum(l => l.CreditAmount);
        totalDebits.Should().Be(totalCredits);
        totalDebits.Should().Be(1875.50m);
    }

    private static AiBookkeeperClient CreateClientWithResponse(string jsonResponse)
    {
        var mockHandler = new Mock<HttpMessageHandler>();
        mockHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(jsonResponse, Encoding.UTF8, "application/json")
            });

        var httpClient = new HttpClient(mockHandler.Object)
        {
            BaseAddress = new Uri("http://localhost")
        };

        return new AiBookkeeperClient(httpClient, NullLogger<AiBookkeeperClient>.Instance);
    }

    private static ChartOfAccountsDto CreateChartOfAccounts()
    {
        return new ChartOfAccountsDto
        {
            CompanyId = "test-company",
            Accounts =
            [
                new AiAccountDto { AccountNumber = "1000", Name = "Vareforbrug", AccountType = "cogs", VatCodeId = "I25" },
                new AiAccountDto { AccountNumber = "6320", Name = "Indgaaende moms", AccountType = "asset", VatCodeId = null },
                new AiAccountDto { AccountNumber = "6930", Name = "Leverandoerer", AccountType = "liability", VatCodeId = null }
            ]
        };
    }
}
