namespace Inspectron.Epson.PrintServer.Printers.Utils.MoneyReturnReceipt;

/// <summary>
/// Builds the print command list for a refund / credit note receipt.
/// Layout follows money_return_receipt.jpg: restaurant header, "Refund / Credit note"
/// title, credit note + original transaction reference block, the negative credit total,
/// the (negated) per-rate VAT breakdown and the thank-you footer.
/// </summary>
public class ReceiptConverter
{
    private readonly int _lineWidth;
    private readonly int _bigFontLineWidth;

    public ReceiptConverter(int lineWidth = 48, int bigFontLineWidth = 24)
    {
        _lineWidth = lineWidth;
        _bigFontLineWidth = bigFontLineWidth;
    }

    public List<PrintCommand> ConvertToPrintCommands(MoneyReturnReceipt receipt)
    {
        var commands = new List<PrintCommand>();

        // Header - Restaurant info
        commands.Add(new PrintCommand(Center(receipt.RestaurantName, false)));
        commands.Add(new PrintCommand(Center(receipt.RestaurantAddressLine1, false)));
        commands.Add(new PrintCommand(Center(receipt.RestaurantAddressLine2, false)));
        commands.Add(new PrintCommand(Center(receipt.RestaurantPhoneNumber, false)));
        if (!string.IsNullOrWhiteSpace(receipt.RestaurantWebsite))
            commands.Add(new PrintCommand(Center(receipt.RestaurantWebsite, false)));
        commands.Add(new PrintCommand(""));
        commands.Add(new PrintCommand(""));

        // Title between separators
        commands.Add(new PrintCommand(new string('-', _lineWidth)));
        commands.Add(new PrintCommand(Center("Refund / Credit note", false), isBold: true));
        commands.Add(new PrintCommand(new string('-', _lineWidth)));
        commands.Add(new PrintCommand(""));

        // Credit note number + issue date
        commands.Add(new PrintCommand(Justify(
            $"Credit note No. {receipt.RefundNumber}",
            FormatTimestamp(receipt.RefundTimestamp))));
        commands.Add(new PrintCommand(""));

        // Original transaction reference block
        commands.Add(new PrintCommand(Justify("Orig. invoice No.:", receipt.OriginalTransactionNumber.ToString())));
        commands.Add(new PrintCommand(Justify("Orig. date:", FormatTimestamp(receipt.OriginalPaymentTimestamp))));
        commands.Add(new PrintCommand(Justify("Orig. payment method:", receipt.OriginalPaymentMethod ?? "")));
        if (!string.IsNullOrEmpty(receipt.RefundType))
            commands.Add(new PrintCommand(Justify("Type:", $"{receipt.RefundType} refund")));
        commands.Add(new PrintCommand(""));

        commands.Add(new PrintCommand(new string('-', _lineWidth)));
        commands.Add(new PrintCommand(""));

        // Included tip
        if (receipt.TipAmount != 0)
        {
            commands.Add(new PrintCommand($"incl. tip {receipt.Currency}: {receipt.TipAmount:F2}".PadLeft(_lineWidth)));
            commands.Add(new PrintCommand(""));
        }

        // Credit total (refund is shown as a negative amount), big + bold, centered
        string creditLine = $"Credit: {-receipt.RefundedAmount:F2} {receipt.Currency}";
        commands.Add(new PrintCommand(Center(creditLine, true), true, true));
        commands.Add(new PrintCommand(""));

        // Refund payment method line (e.g. "Cash: -11.00 CHF")
        commands.Add(new PrintCommand($"{receipt.RefundPaymentMethod}: {-receipt.RefundedAmount:F2} {receipt.Currency}".PadLeft(_lineWidth)));
        commands.Add(new PrintCommand(""));

        // VAT breakdown (amounts negated for the refund)
        if (receipt.NoVAT)
        {
            commands.Add(new PrintCommand("Not subject to value added tax"));
        }
        else
        {
            var rates = new List<(string Category, TaxRateBreakdown Rate)>();
            AddIfPresent(rates, "A", receipt.StandardRate);
            AddIfPresent(rates, "B", receipt.ReducedRate);
            AddIfPresent(rates, "C", receipt.SpecialRateForAccommodation);

            if (rates.Count > 0)
            {
                commands.Add(new PrintCommand("VAT   %".PadRight(_lineWidth / 4)
                    + "Gross".PadLeft(_lineWidth / 4)
                    + "Net".PadLeft(_lineWidth / 4)
                    + "VAT".PadLeft(_lineWidth / 4)));

                foreach (var (category, rate) in rates)
                {
                    commands.Add(new PrintCommand(
                        ($"{category}:" + $"{rate.Rate}%".PadLeft(5)).PadRight(_lineWidth / 4)
                        + $"{-rate.TotalAmount:F2} {receipt.Currency}".PadLeft(_lineWidth / 4)
                        + $"{-rate.TotalAmountNetto:F2} {receipt.Currency}".PadLeft(_lineWidth / 4)
                        + $"{-rate.TotalAmountTax:F2} {receipt.Currency}".PadLeft(_lineWidth / 4)));
                }
            }
        }

        commands.Add(new PrintCommand(""));

        // VAT registration number (if provided)
        if (!string.IsNullOrWhiteSpace(receipt.RestaurantVatNumber))
        {
            commands.Add(new PrintCommand(Center(receipt.RestaurantVatNumber, false)));
        }

        commands.Add(new PrintCommand(""));
        commands.Add(new PrintCommand(""));

        // Footer
        commands.Add(new PrintCommand(Center(receipt.ThanksMessage, false)));

        return commands;
    }

    private static void AddIfPresent(List<(string, TaxRateBreakdown)> rates, string category, TaxRateBreakdown? rate)
    {
        if (rate != null && (rate.Rate != 0 || rate.TotalAmount != 0))
            rates.Add((category, rate));
    }

    private static string FormatTimestamp(long unixMilliseconds)
    {
        return DateTimeOffset.FromUnixTimeMilliseconds(unixMilliseconds).LocalDateTime.ToString("HH:mm dd.MM.yyyy");
    }

    private string Justify(string left, string right)
    {
        left ??= "";
        right ??= "";

        int spaces = _lineWidth - left.Length - right.Length;
        if (spaces < 1) spaces = 1;

        return left + new string(' ', spaces) + right;
    }

    private string Center(string text, bool isBigFont)
    {
        int effectiveLineWidth = isBigFont ? _bigFontLineWidth : _lineWidth;

        if (string.IsNullOrEmpty(text) || text.Length >= effectiveLineWidth)
            return text;

        int totalPadding = effectiveLineWidth - text.Length;
        int leftPadding = totalPadding / 2;

        return new string(' ', leftPadding) + text;
    }
}
