using System.Net;
using System.Net.Http.Json;
using System.Text;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Reporting;

namespace CashLineERP.Api.Tests;

public sealed class FinanceStatementQaTests : IClassFixture<ApiTestFactory>
{
    private readonly HttpClient _client;

    public FinanceStatementQaTests(ApiTestFactory factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Finance_Maker_And_Checker_Can_Govern_Accounting_Rules_And_Export_Statement_Pack()
    {
        await _client.AuthenticateAsFinanceMakerAsync();

        var createRuleResponse = await _client.PostAsJsonAsync("/api/v1/finance/accounting-rules", new CreateFinanceAccountingRuleRequest(
            RuleName: "Unearned revenue classification",
            GeneralLedgerAccountId: 2201,
            GeneralLedgerAccountCode: "2201",
            GeneralLedgerAccountName: "Unearned Revenue",
            AccountCategoryCode: "LIABILITY",
            StatementSectionCode: "CURRENT_LIABILITY",
            CashFlowCategoryCode: "NON_CASH",
            IsCashAccount: false,
            DisplayOrder: 2201,
            EffectiveFromDate: new DateOnly(2026, 4, 1),
            EffectiveToDate: null,
            Notes: "HTTP QA accounting rule."));
        Assert.True(createRuleResponse.IsSuccessStatusCode, await createRuleResponse.Content.ReadAsStringAsync());
        var createdRule = await createRuleResponse.Content.ReadFromJsonAsync<FinanceAccountingRuleResponse>();
        Assert.NotNull(createdRule);
        Assert.Equal("DRAFT", createdRule!.StatusCode);

        var submitRuleResponse = await _client.PostAsync($"/api/v1/finance/accounting-rules/{createdRule.FinanceAccountingRuleId}/submit", null);
        Assert.True(submitRuleResponse.IsSuccessStatusCode, await submitRuleResponse.Content.ReadAsStringAsync());

        var createInvoiceResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-invoices", new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: 1,
            DispatchOrderId: 1,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 4, 28),
            TaxDate: new DateOnly(2026, 4, 28),
            DueDate: new DateOnly(2026, 4, 30),
            CurrencyCode: "USD",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 1m, "EA", 15m, "VAT14")
            ]));
        Assert.True(createInvoiceResponse.IsSuccessStatusCode, await createInvoiceResponse.Content.ReadAsStringAsync());
        var createdInvoice = await createInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
        Assert.NotNull(createdInvoice);

        var submitInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{createdInvoice!.AccountsReceivableInvoiceId}/submit", null);
        Assert.True(submitInvoiceResponse.IsSuccessStatusCode, await submitInvoiceResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveRuleResponse = await _client.PostAsync($"/api/v1/finance/accounting-rules/{createdRule.FinanceAccountingRuleId}/approve", null);
        Assert.True(approveRuleResponse.IsSuccessStatusCode, await approveRuleResponse.Content.ReadAsStringAsync());

        var activateRuleResponse = await _client.PostAsync($"/api/v1/finance/accounting-rules/{createdRule.FinanceAccountingRuleId}/activate", null);
        Assert.True(activateRuleResponse.IsSuccessStatusCode, await activateRuleResponse.Content.ReadAsStringAsync());
        var activatedRule = await activateRuleResponse.Content.ReadFromJsonAsync<FinanceAccountingRuleResponse>();
        Assert.NotNull(activatedRule);
        Assert.True(activatedRule!.IsActive);

        var approveInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{createdInvoice.AccountsReceivableInvoiceId}/approve", null);
        Assert.True(approveInvoiceResponse.IsSuccessStatusCode, await approveInvoiceResponse.Content.ReadAsStringAsync());

        var postInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{createdInvoice.AccountsReceivableInvoiceId}/post", null);
        Assert.True(postInvoiceResponse.IsSuccessStatusCode, await postInvoiceResponse.Content.ReadAsStringAsync());
        var postedInvoice = await postInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
        Assert.NotNull(postedInvoice);

        await _client.AuthenticateAsFinanceMakerAsync();

        var createReceiptResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-receipts", new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 4, 29),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "USD",
            AmountAmount: postedInvoice!.TotalAmount,
            ReferenceNumber: "HTTP-FIN-STMT-01",
            Notes: "HTTP statement receipt",
            Allocations:
            [
                new CreateAccountsReceivableReceiptAllocationRequest(postedInvoice.AccountsReceivableInvoiceId, postedInvoice.TotalAmount, new DateOnly(2026, 4, 29))
            ]));
        Assert.True(createReceiptResponse.IsSuccessStatusCode, await createReceiptResponse.Content.ReadAsStringAsync());
        var createdReceipt = await createReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(createdReceipt);

        var submitReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{createdReceipt!.AccountsReceivableReceiptId}/submit", null);
        Assert.True(submitReceiptResponse.IsSuccessStatusCode, await submitReceiptResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{createdReceipt.AccountsReceivableReceiptId}/approve", null);
        Assert.True(approveReceiptResponse.IsSuccessStatusCode, await approveReceiptResponse.Content.ReadAsStringAsync());

        var postReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{createdReceipt.AccountsReceivableReceiptId}/post", null);
        Assert.True(postReceiptResponse.IsSuccessStatusCode, await postReceiptResponse.Content.ReadAsStringAsync());

        var activeRulesResponse = await _client.GetAsync("/api/v1/finance/accounting-rules?activeOnly=true");
        Assert.Equal(HttpStatusCode.OK, activeRulesResponse.StatusCode);
        var activeRules = await activeRulesResponse.Content.ReadFromJsonAsync<List<FinanceAccountingRuleResponse>>();
        Assert.NotNull(activeRules);
        Assert.Contains(activeRules!, rule => rule.FinanceAccountingRuleId == createdRule.FinanceAccountingRuleId && rule.IsActive);

        var balanceSheetResponse = await _client.GetAsync("/api/v1/reports/finance-statements/BALANCE_SHEET?asOfDate=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, balanceSheetResponse.StatusCode);
        var balanceSheet = await balanceSheetResponse.Content.ReadFromJsonAsync<FinancialStatementReportResponse>();
        Assert.NotNull(balanceSheet);
        Assert.Equal("BALANCE_SHEET", balanceSheet!.StatementCode);
        Assert.NotEmpty(balanceSheet.Rows);

        var incomeStatementResponse = await _client.GetAsync("/api/v1/reports/finance-statements/INCOME_STATEMENT?dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, incomeStatementResponse.StatusCode);
        var incomeStatement = await incomeStatementResponse.Content.ReadFromJsonAsync<FinancialStatementReportResponse>();
        Assert.NotNull(incomeStatement);
        Assert.Contains(incomeStatement!.Metrics, metric => metric.MetricCode == "NET_INCOME");

        var cashFlowResponse = await _client.GetAsync("/api/v1/reports/finance-statements/CASH_FLOW?dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, cashFlowResponse.StatusCode);
        var cashFlow = await cashFlowResponse.Content.ReadFromJsonAsync<FinancialStatementReportResponse>();
        Assert.NotNull(cashFlow);
        Assert.Contains(cashFlow!.Metrics, metric => metric.MetricCode == "CLOSING_CASH");

        var trialBalanceResponse = await _client.GetAsync("/api/v1/reports/finance-statements/TRIAL_BALANCE?dateFrom=2026-04-01&dateTo=2026-04-29&asOfDate=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, trialBalanceResponse.StatusCode);
        var trialBalance = await trialBalanceResponse.Content.ReadFromJsonAsync<FinancialStatementReportResponse>();
        Assert.NotNull(trialBalance);
        Assert.NotEmpty(trialBalance!.Rows);

        var accountingEntriesResponse = await _client.GetAsync("/api/v1/reports/finance-statements/ACCOUNTING_ENTRIES?dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, accountingEntriesResponse.StatusCode);
        var accountingEntries = await accountingEntriesResponse.Content.ReadFromJsonAsync<FinancialStatementReportResponse>();
        Assert.NotNull(accountingEntries);
        Assert.Contains(accountingEntries!.Rows, row => row.DocumentType == "AR_RECEIPT");

        var stakeholderSummaryResponse = await _client.GetAsync("/api/v1/reports/finance-stakeholders?dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, stakeholderSummaryResponse.StatusCode);
        var stakeholderSummary = await stakeholderSummaryResponse.Content.ReadFromJsonAsync<FinanceStakeholderSummaryReportResponse>();
        Assert.NotNull(stakeholderSummary);
        Assert.NotEmpty(stakeholderSummary!.Rows);
        Assert.Contains(stakeholderSummary.Rows, row => row.StakeholderType == "CUSTOMER");
        Assert.Contains(stakeholderSummary.Rows, row => row.StakeholderType == "SUPPLIER");

        var customerDetailResponse = await _client.GetAsync("/api/v1/reports/finance-stakeholders/CUSTOMER/2?currencyCode=USD&dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, customerDetailResponse.StatusCode);
        var customerDetail = await customerDetailResponse.Content.ReadFromJsonAsync<FinanceStakeholderDetailReportResponse>();
        Assert.NotNull(customerDetail);
        Assert.Equal("CUSTOMER", customerDetail!.StakeholderType);
        Assert.Equal(2, customerDetail.StakeholderId);
        Assert.Contains(customerDetail.Rows, row => row.ActivityType == "AR_INVOICE");
        Assert.Contains(customerDetail.Rows, row => row.ActivityType == "AR_RECEIPT");

        var csvExportResponse = await _client.GetAsync("/api/v1/reports/finance-statements/BALANCE_SHEET/export?format=CSV&asOfDate=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, csvExportResponse.StatusCode);
        Assert.Equal("text/csv", csvExportResponse.Content.Headers.ContentType?.MediaType);
        Assert.Contains("SectionCode,SectionTitle", await csvExportResponse.Content.ReadAsStringAsync());

        var excelExportResponse = await _client.GetAsync("/api/v1/reports/finance-statements/INCOME_STATEMENT/export?format=EXCEL&dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, excelExportResponse.StatusCode);
        Assert.Equal("application/vnd.ms-excel", excelExportResponse.Content.Headers.ContentType?.MediaType);
        Assert.Contains("<Workbook", await excelExportResponse.Content.ReadAsStringAsync());

        var pdfExportResponse = await _client.GetAsync("/api/v1/reports/finance-statements/ACCOUNTING_ENTRIES/export?format=PDF&dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, pdfExportResponse.StatusCode);
        Assert.Equal("application/pdf", pdfExportResponse.Content.Headers.ContentType?.MediaType);
        var pdfBytes = await pdfExportResponse.Content.ReadAsByteArrayAsync();
        Assert.StartsWith("%PDF-1.4", Encoding.ASCII.GetString(pdfBytes));

        var stakeholderExportResponse = await _client.GetAsync("/api/v1/reports/finance-stakeholders/export?dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, stakeholderExportResponse.StatusCode);
        Assert.Equal("text/csv", stakeholderExportResponse.Content.Headers.ContentType?.MediaType);
        Assert.Contains("StakeholderType,StakeholderId,StakeholderCode", await stakeholderExportResponse.Content.ReadAsStringAsync());

        var stakeholderDetailExportResponse = await _client.GetAsync("/api/v1/reports/finance-stakeholders/CUSTOMER/2/export?currencyCode=USD&dateFrom=2026-04-01&dateTo=2026-04-29");
        Assert.Equal(HttpStatusCode.OK, stakeholderDetailExportResponse.StatusCode);
        Assert.Equal("text/csv", stakeholderDetailExportResponse.Content.Headers.ContentType?.MediaType);
        Assert.Contains("ActivityDate,ActivityType,RecordId,RecordNumber", await stakeholderDetailExportResponse.Content.ReadAsStringAsync());
    }

    [Fact]
    public async Task Internal_User_Without_Finance_Role_Cannot_Access_Finance_Apis()
    {
        await _client.AuthenticateAsync("DEMO_PROC_MKR", "Demo@2026!", "INTERNAL");

        var financeStatementResponse = await _client.GetAsync("/api/v1/reports/finance-statements/BALANCE_SHEET?asOfDate=2026-04-29");
        Assert.Equal(HttpStatusCode.Forbidden, financeStatementResponse.StatusCode);

        var accountingRulesResponse = await _client.GetAsync("/api/v1/finance/accounting-rules");
        Assert.Equal(HttpStatusCode.Forbidden, accountingRulesResponse.StatusCode);
    }
}
