using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using PolyTrader.Core.Persistence;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;

namespace PolyTrader.Modules.Supervisor.Counterfactual
{
    /// <summary>
    /// Auflösungsstatus-Quelle für Counterfactuals. Interface, damit der Job ohne echte
    /// Polymarket-API testbar ist; die Live-Implementierung wickelt den PolymarketApiService.
    /// </summary>
    public interface ICounterfactualResolutionSource
    {
        Task<(bool isResolved, bool isWinner)> CheckAsync(string marketSlug, string tokenId, CancellationToken ct);
    }

    /// <summary>Live-Quelle: dünner Adapter um <see cref="PolymarketApiService.CheckMarketResolutionAsync"/> (read-only).</summary>
    public sealed class ApiCounterfactualResolutionSource : ICounterfactualResolutionSource
    {
        private readonly PolymarketApiService _api;
        public ApiCounterfactualResolutionSource(PolymarketApiService api) => _api = api;
        public Task<(bool isResolved, bool isWinner)> CheckAsync(string marketSlug, string tokenId, CancellationToken ct)
            => _api.CheckMarketResolutionAsync(marketSlug, tokenId);
    }

    /// <summary>
    /// Counterfactual-Job (S-3): nimmt ABGELEHNTE BUY-Entscheidungen aus dem Journal, deren Markt
    /// inzwischen abgelaufen ist, prüft die Auflösung und speichert den hypothetischen Ausgang
    /// (sup_counterfactuals; ein Ergebnis je Entscheidung). Läuft alle 6 h, API-schonend gedrosselt.
    /// </summary>
    public sealed class CounterfactualJob : BackgroundService
    {
        private static readonly TimeSpan Interval = TimeSpan.FromHours(6);
        private const int BatchLimit = 100;   // API-Last je Lauf begrenzen

        private readonly IDecisionJournal _journal;
        private readonly Persistence.ISupervisorCounterfactualRepository _repo;
        private readonly ICounterfactualResolutionSource _resolution;
        private readonly TerminalLogger _logger;

        public CounterfactualJob(IDecisionJournal journal, Persistence.ISupervisorCounterfactualRepository repo,
            ICounterfactualResolutionSource resolution, TerminalLogger logger)
        {
            _journal = journal;
            _repo = repo;
            _resolution = resolution;
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken);   // nach den Trading-Services
            _logger.Info("Counterfactual-Job gestartet (abgelehnte Signale vs. Marktausgang).");

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    int added = await CheckOnceAsync(stoppingToken);
                    if (added > 0)
                        _logger.Info($"🔮 [Counterfactual] {added} abgelehnte Signale nach Auflösung ausgewertet.");
                }
                catch (OperationCanceledException) { break; }
                catch (Exception ex) { _logger.Error($"Counterfactual-Job Fehler: {ex.Message}"); }

                await Task.Delay(Interval, stoppingToken);
            }
        }

        /// <summary>Testbarer Kern: einen Prüf-Durchlauf ausführen. Liefert die Anzahl neuer Ergebnisse.</summary>
        internal async Task<int> CheckOnceAsync(CancellationToken ct)
        {
            DateTime now = DateTime.UtcNow;
            var candidates = _journal.Query(d =>
                    d.Decision == TradeDecision.Rejected &&
                    d.Side == "BUY" &&
                    d.MarketEndDate != null && d.MarketEndDate < now &&
                    d.MarketSlug != "" && d.TokenId != "", BatchLimit * 3)
                .ToList();
            if (candidates.Count == 0) return 0;

            var known = _repo.ExistingDecisionIds(candidates.Select(c => c.Id));
            int added = 0;

            foreach (var d in candidates.Where(c => !known.Contains(c.Id)).Take(BatchLimit))
            {
                if (ct.IsCancellationRequested) break;

                (bool isResolved, bool isWinner) result;
                try { result = await _resolution.CheckAsync(d.MarketSlug, d.TokenId, ct); }
                catch (Exception ex)
                {
                    _logger.Warning($"[Counterfactual] Resolution-Check fehlgeschlagen ({d.MarketSlug}): {ex.Message}");
                    continue;
                }
                if (!result.isResolved) continue;   // noch nicht aufgelöst → späterer Lauf

                _repo.Insert(new CounterfactualRecord
                {
                    DecisionId = d.Id,
                    SignalId = d.SignalId,
                    ModuleName = d.ModuleName,
                    AccountId = d.AccountId,
                    TokenId = d.TokenId,
                    MarketSlug = d.MarketSlug,
                    MarketQuestion = d.MarketQuestion,
                    Reason = d.Reason.ToString(),
                    SignalPrice = d.SignalPrice,
                    IsWinner = result.isWinner,
                    HypotheticalPnlPerShare = CounterfactualMath.PnlPerShare(d.SignalPrice, result.isWinner)
                });
                added++;
            }
            return added;
        }
    }
}
