using System.Text.Json;

namespace Books.Api.EventFlow.ReadModels;

/// <summary>
/// DTO for Order read model, used for Dapper queries.
/// </summary>
public class OrderReadModelDto
{
    public string Id { get; set; } = string.Empty;
    public string CompanyId { get; set; } = string.Empty;
    public string FiscalYearId { get; set; } = string.Empty;
    public string CustomerId { get; set; } = string.Empty;
    public string CustomerName { get; set; } = string.Empty;
    public string CustomerNumber { get; set; } = string.Empty;
    public string OrderNumber { get; set; } = string.Empty;
    public DateTime? OrderDate { get; set; }
    public DateTime? ExpectedDeliveryDate { get; set; }
    public string Status { get; set; } = "draft";
    public decimal AmountExVat { get; set; }
    public decimal AmountVat { get; set; }
    public decimal AmountTotal { get; set; }
    public string Currency { get; set; } = "DKK";
    public string LinesJson { get; set; } = "[]";
    public string? InvoiceId { get; set; }
    public string? InvoiceNumber { get; set; }
    public string? Notes { get; set; }
    public string? Reference { get; set; }
    public DateTime? ConfirmedAt { get; set; }
    public string? ConfirmedBy { get; set; }
    public DateTime? InvoicedAt { get; set; }
    public string? InvoicedBy { get; set; }
    public DateTime? CancelledAt { get; set; }
    public string? CancelledBy { get; set; }
    public string? CancelledReason { get; set; }
    public string CreatedBy { get; set; } = string.Empty;
    public string? UpdatedBy { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }

    /// <summary>
    /// Parses the Lines JSON into a list of OrderLineDto.
    /// </summary>
    public IReadOnlyList<OrderLineDto> GetLines()
    {
        if (string.IsNullOrEmpty(LinesJson))
            return [];

        try
        {
            return JsonSerializer.Deserialize<List<OrderLineDto>>(LinesJson) ?? [];
        }
        catch
        {
            return [];
        }
    }

    /// <summary>
    /// Returns lines that have not yet been invoiced.
    /// </summary>
    public IReadOnlyList<OrderLineDto> GetUninvoicedLines()
    {
        return GetLines().Where(l => !l.IsInvoiced).ToList();
    }

    /// <summary>
    /// Returns the count of uninvoiced lines.
    /// </summary>
    public int UninvoicedLineCount => GetLines().Count(l => !l.IsInvoiced);

    /// <summary>
    /// Returns the total amount of uninvoiced lines.
    /// </summary>
    public decimal UninvoicedAmount => GetLines().Where(l => !l.IsInvoiced).Sum(l => l.AmountTotal);
}

/// <summary>
/// DTO for order lines stored as JSON in the read model.
/// </summary>
public record OrderLineDto
{
    public int LineNumber { get; init; }
    public string? ProductId { get; init; }
    public string Description { get; init; } = string.Empty;
    public decimal Quantity { get; init; }
    public string? Unit { get; init; }
    public decimal UnitPrice { get; init; }
    public decimal DiscountPercent { get; init; }
    public string VatCode { get; init; } = "U25";
    public string? AccountId { get; init; }

    // Invoicing tracking
    public bool IsInvoiced { get; init; }
    public string? InvoiceId { get; init; }
    public DateTimeOffset? InvoicedAt { get; init; }

    public decimal AmountExVat => Math.Round(UnitPrice * Quantity * (1 - DiscountPercent / 100m), 2);
    public decimal AmountVat => Math.Round(AmountExVat * GetVatRate(), 2);
    public decimal AmountTotal => AmountExVat + AmountVat;

    private decimal GetVatRate() => VatCode switch
    {
        "U25" or "I25" => 0.25m,
        "UEU" or "IEU" => 0m,
        "UEXP" or "IEXP" => 0m,
        "INGEN" => 0m,
        _ => 0.25m
    };
}
