using Books.Api.Domain.JournalEntryDrafts;

namespace Books.Api.Domain;

/// <summary>
/// Service for calculating VAT (moms) on journal entry lines.
/// Handles Danish VAT rules including automatic calculation of VAT amounts
/// and generation of VAT posting lines.
/// </summary>
public interface IVatCalculationService
{
    /// <summary>
    /// Calculate VAT for a list of draft lines.
    /// Returns the original lines plus generated VAT posting lines.
    /// </summary>
    VatCalculationResult CalculateVat(
        IEnumerable<DraftLine> lines,
        VatCalculationMode mode,
        string inputVatAccountId,
        string outputVatAccountId);
}

/// <summary>
/// Determines how amounts on lines should be interpreted.
/// </summary>
public enum VatCalculationMode
{
    /// <summary>
    /// Amounts are exclusive of VAT (ekskl. moms).
    /// VAT will be added to the total.
    /// </summary>
    Exclusive,

    /// <summary>
    /// Amounts are inclusive of VAT (inkl. moms).
    /// VAT will be extracted from the total.
    /// </summary>
    Inclusive
}

/// <summary>
/// Result of VAT calculation containing original lines, VAT lines, and totals.
/// </summary>
public class VatCalculationResult
{
    /// <summary>
    /// The original lines (unchanged).
    /// </summary>
    public required List<DraftLine> OriginalLines { get; init; }

    /// <summary>
    /// Generated VAT posting lines (to be added to the journal entry).
    /// </summary>
    public required List<VatPostingLine> VatLines { get; init; }

    /// <summary>
    /// Breakdown of VAT by code for reporting purposes.
    /// </summary>
    public required List<VatSummary> VatSummary { get; init; }

    /// <summary>
    /// Total VAT amount (sum of all VAT lines).
    /// </summary>
    public decimal TotalVatAmount { get; init; }
}

/// <summary>
/// A generated VAT posting line.
/// </summary>
public record VatPostingLine
{
    public required string AccountId { get; init; }
    public required decimal DebitAmount { get; init; }
    public required decimal CreditAmount { get; init; }
    public required string Description { get; init; }
    /// <summary>
    /// The VAT code this line relates to.
    /// </summary>
    public required string VatCode { get; init; }
    /// <summary>
    /// The original line number this VAT line was calculated from.
    /// </summary>
    public required int SourceLineNumber { get; init; }
}

/// <summary>
/// Summary of VAT for a specific VAT code.
/// </summary>
public record VatSummary
{
    public required string VatCode { get; init; }
    public required decimal Rate { get; init; }
    /// <summary>
    /// Total base amount (before VAT) for this VAT code.
    /// </summary>
    public required decimal BaseAmount { get; init; }
    /// <summary>
    /// Total VAT amount for this VAT code.
    /// </summary>
    public required decimal VatAmount { get; init; }
    /// <summary>
    /// Is this input VAT (købsmoms/fradrag) or output VAT (salgsmoms/skyldig)?
    /// </summary>
    public required bool IsInputVat { get; init; }
}

public class VatCalculationService : IVatCalculationService
{
    public VatCalculationResult CalculateVat(
        IEnumerable<DraftLine> lines,
        VatCalculationMode mode,
        string inputVatAccountId,
        string outputVatAccountId)
    {
        var linesList = lines.ToList();
        var vatLines = new List<VatPostingLine>();
        var vatByCode = new Dictionary<string, VatSummary>();

        foreach (var line in linesList)
        {
            if (string.IsNullOrEmpty(line.VatCode) || line.VatCode == VatCodes.INGEN)
                continue;

            var rate = VatCodes.GetRate(line.VatCode);
            if (rate == 0)
                continue;

            // Determine if this is a debit or credit line
            var isDebit = line.DebitAmount > 0;
            var amount = isDebit ? line.DebitAmount : line.CreditAmount;

            // Calculate base and VAT amounts based on mode
            decimal baseAmount;
            decimal vatAmount;

            if (mode == VatCalculationMode.Inclusive)
            {
                // Amount includes VAT, extract it
                // VAT = Amount * rate / (1 + rate)
                vatAmount = Math.Round(amount * rate / (1 + rate), 2, MidpointRounding.AwayFromZero);
                baseAmount = amount - vatAmount;
            }
            else
            {
                // Amount excludes VAT, calculate it
                baseAmount = amount;
                vatAmount = Math.Round(amount * rate, 2, MidpointRounding.AwayFromZero);
            }

            // Determine if this is input or output VAT
            var isInputVat = VatCodes.IsInputVat(line.VatCode);
            var vatAccountId = isInputVat ? inputVatAccountId : outputVatAccountId;

            // Create VAT posting line
            // For sales (output VAT): we credit the VAT account
            // For purchases (input VAT): we debit the VAT account
            var vatLine = new VatPostingLine
            {
                AccountId = vatAccountId,
                DebitAmount = isInputVat && isDebit ? vatAmount : (isInputVat ? 0 : 0),
                CreditAmount = !isInputVat && !isDebit ? vatAmount : (!isInputVat && isDebit ? vatAmount : 0),
                Description = $"Moms {line.VatCode} ({rate * 100:0}%)",
                VatCode = line.VatCode,
                SourceLineNumber = line.LineNumber
            };

            // Correct the debit/credit logic:
            // - For SALES (U25): revenue is credit, VAT should ALSO be credit (liability to SKAT)
            // - For PURCHASES (I25): expense is debit, VAT should ALSO be debit (asset/receivable from SKAT)
            // The key insight: VAT follows the same direction as the base transaction
            if (isInputVat)
            {
                // Purchases: VAT follows the expense direction (typically debit)
                vatLine = vatLine with
                {
                    DebitAmount = isDebit ? vatAmount : 0,
                    CreditAmount = !isDebit ? vatAmount : 0
                };
            }
            else
            {
                // Sales: VAT follows the revenue direction (typically credit)
                vatLine = vatLine with
                {
                    DebitAmount = isDebit ? vatAmount : 0,
                    CreditAmount = !isDebit ? vatAmount : 0
                };
            }

            vatLines.Add(vatLine);

            // Accumulate VAT summary
            if (!vatByCode.TryGetValue(line.VatCode, out var summary))
            {
                summary = new VatSummary
                {
                    VatCode = line.VatCode,
                    Rate = rate,
                    BaseAmount = 0,
                    VatAmount = 0,
                    IsInputVat = isInputVat
                };
                vatByCode[line.VatCode] = summary;
            }

            vatByCode[line.VatCode] = summary with
            {
                BaseAmount = summary.BaseAmount + baseAmount,
                VatAmount = summary.VatAmount + vatAmount
            };
        }

        return new VatCalculationResult
        {
            OriginalLines = linesList,
            VatLines = vatLines,
            VatSummary = vatByCode.Values.ToList(),
            TotalVatAmount = vatLines.Sum(v => v.DebitAmount - v.CreditAmount)
        };
    }
}
