using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using PolyTrader.Modules.ResolutionFarming.Models;
using PolyTrader.Modules.ResolutionFarming.Persistence.Ef;
using PolyTrader.Modules.ResolutionFarming.Services;
using PolyTrader.Tests.TestSupport;
using PolyTraderSharp.Services;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>
    /// Tests der Scanner-Bewertung (pure Filterkette inkl. Reject-Gründe) und der Orchestrierung
    /// (Marktquelle → Bewertung → Persistenz) über eine Fake-Marktquelle + reales InMemory-Repo.
    /// </summary>
    public class MarketScannerServiceTests
    {
        private static RfSettings DefaultSettings() => new()
        {
            AccountId = 1, MinPrice = 0.90m, MaxPrice = 0.98m, MaxHoursToResolution = 48,
            CategoryWhitelistCsv = "Sports,Politics,Geopolitics", BlacklistCsv = "", MinEdgePct = 1.5m
        };

        private static ScannedMarket Market(decimal ask, string category = "Sports", double hours = 24,
            string slug = "match-1", string tags = "", int feeBps = 0) => new()
        {
            TokenId = "tok", MarketSlug = slug, EventSlug = "event-1", MarketQuestion = "Frage?",
            Outcome = "Yes", Category = category, Tags = tags, Ask = ask, FeeBps = feeBps,
            HoursToResolution = hours, EndDate = DateTime.UtcNow.AddHours(hours)
        };

        // ----- Evaluate (pure) -----

        [Fact]
        public void Evaluate_accepts_a_good_favorite()
        {
            var c = MarketScannerService.Evaluate(1, Market(0.95m), DefaultSettings());
            Assert.True(c.Accepted);
            Assert.Equal("", c.RejectReason);
            Assert.Equal("event-1", c.ClusterKey);
            Assert.True(c.NetEdgePct > 0m);
        }

        [Fact]
        public void Evaluate_rejects_out_of_price_band()
        {
            var c = MarketScannerService.Evaluate(1, Market(0.85m), DefaultSettings());
            Assert.False(c.Accepted);
            Assert.Contains("Band", c.RejectReason);
        }

        [Fact]
        public void Evaluate_rejects_category_not_whitelisted()
        {
            var c = MarketScannerService.Evaluate(1, Market(0.95m, category: "Crypto"), DefaultSettings());
            Assert.False(c.Accepted);
            Assert.Contains("Whitelist", c.RejectReason);
        }

        [Fact]
        public void Evaluate_rejects_blacklisted_market()
        {
            var s = DefaultSettings();
            s.BlacklistCsv = "dispute";
            var c = MarketScannerService.Evaluate(1, Market(0.95m, slug: "x-dispute"), s);
            Assert.False(c.Accepted);
            Assert.Equal("Blacklist", c.RejectReason);
        }

        [Fact]
        public void Evaluate_rejects_outside_resolution_window()
        {
            var c = MarketScannerService.Evaluate(1, Market(0.95m, hours: 100), DefaultSettings());
            Assert.False(c.Accepted);
            Assert.Contains("Auflösung", c.RejectReason);
        }

        [Fact]
        public void Evaluate_rejects_insufficient_net_edge()
        {
            // Ask 0.98 (im Band), Sports-Fee 75 bps -> Netto-Edge ~1.29% < 1.5%.
            var c = MarketScannerService.Evaluate(1, Market(0.98m), DefaultSettings());
            Assert.False(c.Accepted);
            Assert.Contains("Netto-Edge", c.RejectReason);
        }

        // ----- Orchestrierung -----

        private sealed class FakeSource : IFarmingMarketSource
        {
            private readonly List<ScannedMarket> _markets;
            public FakeSource(params ScannedMarket[] markets) => _markets = new List<ScannedMarket>(markets);
            public Task<IReadOnlyList<ScannedMarket>> GetNearResolutionFavoritesAsync(RfSettings s, CancellationToken ct)
                => Task.FromResult<IReadOnlyList<ScannedMarket>>(_markets);
        }

        [Fact]
        public async Task ScanAccount_persists_accepted_and_rejected_candidates()
        {
            var factory = new InMemoryContextFactory<ResolutionFarmingDbContext>(o => new ResolutionFarmingDbContext(o));
            var candRepo = new EfRfCandidateRepository(factory);
            var settingsRepo = new EfRfSettingsRepository(factory);
            var source = new FakeSource(
                Market(0.95m, slug: "good"),                 // akzeptiert
                Market(0.80m, slug: "too-cheap"));           // abgelehnt (Preisband)

            var svc = new MarketScannerService(source, candRepo, settingsRepo, new TerminalLogger());

            var result = await svc.ScanAccountAsync(1, DefaultSettings(), CancellationToken.None);

            Assert.Equal(2, result.Count);
            var stored = candRepo.GetRecent(1, 10);
            Assert.Equal(2, stored.Count);
            Assert.Contains(stored, c => c.Accepted && c.MarketSlug == "good");
            Assert.Contains(stored, c => !c.Accepted && c.MarketSlug == "too-cheap");
        }
    }
}
