using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using PalladiumWallet.App.Localization;
using PalladiumWallet.Core.Wallet;

namespace PalladiumWallet.App.ViewModels;

/// <summary>Input/output row for the transaction detail tables.</summary>
public sealed record TxIoRow(string Position, string Address, string Amount, bool IsMine);

/// <summary>
/// ViewModel for the transaction detail view: takes a
/// <see cref="TransactionDetails"/> (assembled from server data) and produces
/// all strings already formatted and localised for the view. Read-only.
/// </summary>
public sealed class TransactionDetailsViewModel
{
    private readonly string _unit;

    public Loc Loc { get; }

    public TransactionDetailsViewModel(TransactionDetails d, Loc loc, string unit)
    {
        Loc = loc;
        _unit = unit;

        Txid = d.Txid;
        IsCoinbase = d.IsCoinbase;
        StatusText = BuildStatus(d, loc);
        DateText = d.BlockTime is { } t
            ? t.ToLocalTime().ToString("dd/MM/yyyy HH:mm")
            : loc["tx.mempool"];

        var counterparties = d.CounterpartyAddresses;
        CounterpartyHeader = d.IsIncoming ? loc["tx.from"] : loc["tx.to"];
        // Coinbase has no sender: coins are newly generated by mining.
        CounterpartyText = d.IsCoinbase
            ? loc["tx.coinbase.newcoins"]
            : counterparties.Count > 0
                ? string.Join(Environment.NewLine, counterparties)
                : "—";

        // Debit (outflow to third parties) or Credit (net inflow on our outputs).
        AmountHeader = d.IsIncoming ? loc["tx.credit"] : loc["tx.debit"];
        AmountText = d.IsIncoming
            ? Signed(d.ReceivedSats)
            : Signed(-d.SentToOthersSats);

        FeeText = d.FeeSats is { } fee
            ? (d.IsIncoming ? Abs(fee) : Signed(-fee))
            : "—";

        NetText = Signed(d.NetSats);

        TotalSizeText = $"{d.TotalSize} byte";
        VirtualSizeText = $"{d.VirtualSize} byte";
        FeeRateText = d.FeeRateSatPerVb is { } r
            ? r.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) + " sat/vB"
            : "—";
        VersionText = d.Version.ToString();
        LockTimeText = d.LockTime.ToString();
        RbfText = loc[d.RbfSignaled ? "tx.yes" : "tx.no"];
        Inputs = new ObservableCollection<TxIoRow>(d.Inputs.Select((i, n) => new TxIoRow(
            i.IsCoinbase ? loc["tx.coinbase"] : $"{i.PrevTxid}:{i.PrevIndex}",
            i.IsCoinbase
                ? i.CoinbaseTag is { Length: > 0 } tag
                    ? $"{loc["tx.coinbase.newcoins"]} — {tag}"
                    : loc["tx.coinbase.newcoins"]
                : i.Address ?? "—",
            i.AmountSats is { } a ? CoinAmount.FormatIn(a, unit) : "—",
            i.IsMine)));

        Outputs = new ObservableCollection<TxIoRow>(d.Outputs.Select(o => new TxIoRow(
            $"#{o.Index}",
            o.Address ?? (o.OpReturnText is { Length: > 0 } msg ? $"OP_RETURN: {msg}" : $"({o.ScriptType})"),
            CoinAmount.FormatIn(o.AmountSats, unit),
            o.IsMine)));
    }

    public string Txid { get; }
    public bool IsCoinbase { get; }
    public string StatusText { get; }
    public string DateText { get; }
    public string CounterpartyHeader { get; }
    public string CounterpartyText { get; }
    public string AmountHeader { get; }
    public string AmountText { get; }
    public string FeeText { get; }
    public string NetText { get; }
    public string TotalSizeText { get; }
    public string VirtualSizeText { get; }
    public string FeeRateText { get; }
    public string VersionText { get; }
    public string LockTimeText { get; }
    public string RbfText { get; }
    public ObservableCollection<TxIoRow> Inputs { get; }
    public ObservableCollection<TxIoRow> Outputs { get; }

    private static string BuildStatus(TransactionDetails d, Loc loc)
    {
        if (d.Confirmations <= 0)
            return loc["tx.status.mempool"];
        var status = $"{d.Confirmations} {loc["tx.status.confirmations"]} ({loc["tx.status.block"]} {d.Height})";
        return d.Verified ? status : $"{status} — {loc["history.unverified"]}";
    }

    private string Signed(long sats)
    {
        var sign = sats > 0 ? "+" : sats < 0 ? "-" : "";
        return sign + CoinAmount.FormatIn(Math.Abs(sats), _unit);
    }

    private string Abs(long sats) => CoinAmount.FormatIn(Math.Abs(sats), _unit);
}
