using System;
using System.Collections.Generic;
using System.Linq;
using PolyTrader.Modules.Accounting.Models;

namespace PolyTrader.Modules.Accounting.Logic
{
    /// <summary>
    /// Reine, seiteneffektfreie Klassifikation roher Quell-Ereignisse → normalisierter Buchungssatz
    /// (Typ, Vorzeichen, Idempotenz-Schlüssel). Geldkritisch → vollständig unit-getestet. Enthält keine
    /// I/O; die Datenbeschaffung liegt in den Ingest-Services hinter Interfaces.
    ///
    /// Vorzeichenkonvention <see cref="LedgerEntry.NetUsdc"/>: Cash-Wirkung aufs Konto (+ Zufluss / − Abfluss).
    /// Annahme (im Export dokumentiert, im Adapter justierbar): Activity-Feld <c>UsdcAmount</c> ist der
    /// BRUTTO-Betrag, <c>Fee</c> separat — BUY kostet Brutto+Fee, SELL bringt Brutto−Fee.
    /// </summary>
    public static class AccountingClassifier
    {
        public static LedgerEntry ClassifyActivity(int accountId, RawActivity a, long ingestBatchId)
        {
            LedgerEventType type = MapActivityType(a);
            decimal gross = Math.Abs(a.UsdcAmount);
            decimal fee = Math.Abs(a.Fee);
            decimal net = type switch
            {
                LedgerEventType.TradeBuy => -(gross + fee),
                LedgerEventType.TradeSell => gross - fee,
                LedgerEventType.Redeem => gross,
                LedgerEventType.Reward => gross,
                LedgerEventType.Fee => -fee,
                _ => 0m   // Split/Merge/Conversion/Other: geldneutral, aber roh erfasst
            };

            return new LedgerEntry
            {
                AccountId = accountId,
                EventType = type,
                Timestamp = a.Timestamp,
                TokenId = a.TokenId,
                MarketSlug = a.MarketSlug,
                Outcome = a.Outcome,
                Side = a.Side,
                Size = a.Size,
                PriceUsdc = a.Price,
                GrossUsdc = gross,
                FeeUsdc = fee,
                NetUsdc = net,
                TxHash = a.TxHash,
                LogIndex = a.LogIndex,
                Source = "polymarket-activity",
                IngestBatchId = ingestBatchId,
                IdempotencyKey = ActivityKey(type, a)
            };
        }

        /// <summary>
        /// Klassifiziert einen On-Chain-USDC-Transfer. Ist der Gegenpart ein Polymarket-System-Contract
        /// (interne Trading-Bewegung, bereits in der Activity erfasst), wird <c>null</c> zurückgegeben.
        /// Nur Safe ↔ EXTERNE Adresse = echte Ein-/Auszahlung.
        /// </summary>
        public static LedgerEntry? ClassifyTransfer(int accountId, RawTransfer t, long ingestBatchId,
            ISet<string> internalContracts)
        {
            string counterparty = (t.IsIncoming ? t.FromAddress : t.ToAddress).Trim().ToLowerInvariant();
            if (internalContracts.Contains(counterparty))
                return null;   // interne Bewegung – nicht als Deposit/Withdrawal buchen

            LedgerEventType type = t.IsIncoming ? LedgerEventType.Deposit : LedgerEventType.Withdrawal;
            decimal gross = Math.Abs(t.UsdcAmount);

            return new LedgerEntry
            {
                AccountId = accountId,
                EventType = type,
                Timestamp = t.Timestamp,
                GrossUsdc = gross,
                NetUsdc = t.IsIncoming ? gross : -gross,
                TxHash = t.TxHash,
                LogIndex = t.LogIndex,
                Source = "onchain-transfer",
                IngestBatchId = ingestBatchId,
                IdempotencyKey = $"XFER|{type}|{t.TxHash.ToLowerInvariant()}|{t.LogIndex}"
            };
        }

        /// <summary>Σ NetUsdc – die Buchhaltungs-Sicht des Kontosaldos (für den Balance-Anker-Abgleich).</summary>
        public static decimal SumNet(IEnumerable<LedgerEntry> entries) => entries.Sum(e => e.NetUsdc);

        // ----- intern -----

        internal static LedgerEventType MapActivityType(RawActivity a) =>
            (a.Type ?? string.Empty).Trim().ToUpperInvariant() switch
            {
                "TRADE" => (a.Side ?? string.Empty).Trim().ToUpperInvariant() == "SELL"
                    ? LedgerEventType.TradeSell : LedgerEventType.TradeBuy,
                "REDEEM" => LedgerEventType.Redeem,
                "REWARD" => LedgerEventType.Reward,
                "FEE" => LedgerEventType.Fee,
                "SPLIT" => LedgerEventType.Split,
                "MERGE" => LedgerEventType.Merge,
                "CONVERSION" => LedgerEventType.Conversion,
                _ => LedgerEventType.Other
            };

        internal static string ActivityKey(LedgerEventType type, RawActivity a) =>
            $"ACT|{type}|{a.TxHash.Trim().ToLowerInvariant()}|{a.LogIndex}|{a.TokenId}";
    }
}
