using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using PolyTrader.Tests.Fakes;
using PolyTraderSharp;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>
    /// Verifiziert K1a (Fable-Fix): Der Stale-Order-Cleanup darf die ruhende Order einer aktiven
    /// SELL-Eskalationsleiter NICHT canceln – sonst steckt die Position am Floor fest (Floor-Deadlock).
    /// </summary>
    public class TraderMonitorServiceTests
    {
        private const string Tok = "tok-mon";

        private static (TraderMonitorService svc, TradingState state, CopyTradingState copy, FakeClobClient clob) Build()
        {
            var state = new TradingState();
            var copy = new CopyTradingState();
            var clob = new FakeClobClient();
            var logger = new TerminalLogger();
            var api = new PolymarketApiService(logger, new HttpClient());
            var posRepo = new FakePositionRepository();
            var marketRepo = new FakeMarketRepository();
            var tradeLog = new FakeCopyTradeLogRepository();
            var notify = new FakeNotificationSink();
            var ladder = new SellLadderService(copy, state, clob, logger, notify, posRepo, new FakeOrderEventLog());
            var signalCh = Channel.CreateUnbounded<CopySignal>();
            var closedCh = Channel.CreateUnbounded<ClosedTrade>();

            var svc = new TraderMonitorService(state, copy, api, clob, signalCh.Writer, closedCh.Writer,
                logger, posRepo, marketRepo, tradeLog, ladder, new FakeDecisionJournal(), new FakeOrderEventLog());

            state.Accounts[1] = new AccountState { AccountId = 1, Name = "Live", IsDemo = false };
            return (svc, state, copy, clob);
        }

        [Fact]
        public async Task Cleanup_does_not_cancel_orders_of_active_ladder()
        {
            var (svc, state, copy, clob) = Build();
            // Stale (1h alt) getrackte Order + aktive Leiter für denselben Key.
            copy.PendingOrderTimestamps["1_" + Tok] = (DateTime.UtcNow.AddHours(-1), 7, "SELL");
            copy.ExitLadders["1_" + Tok] = new ExitLadderState { AccountId = 1, TokenId = Tok, Floor = 0.40m, CurrentLimit = 0.40m };
            clob.OpenOrdersByAsset[Tok] = new() { ("o1", "SELL", 0.40m) };

            await svc.CleanupStaleOpenOrdersAsync(CancellationToken.None);

            Assert.Empty(clob.CanceledOrderIds);   // Leiter-Order NICHT gecancelt
        }

        [Fact]
        public async Task Cleanup_cancels_stale_orders_without_ladder()
        {
            // Kontrast: ohne aktive Leiter wird die veraltete Order storniert.
            var (svc, state, copy, clob) = Build();
            copy.PendingOrderTimestamps["1_" + Tok] = (DateTime.UtcNow.AddHours(-1), 7, "SELL");
            clob.OpenOrdersByAsset[Tok] = new() { ("o1", "SELL", 0.40m) };

            await svc.CleanupStaleOpenOrdersAsync(CancellationToken.None);

            Assert.Contains("o1", clob.CanceledOrderIds);
        }
    }
}
