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 PolyTrader.Modules.ResolutionFarming.Logic;
using PolyTrader.Modules.ResolutionFarming.Models;
using PolyTrader.Modules.ResolutionFarming.Persistence;
using PolyTraderSharp;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;

namespace PolyTrader.Modules.ResolutionFarming.Services
{
    /// <summary>
    /// Eröffnet Farming-Positionen aus akzeptierten Kandidaten (Phase RF-2, Demo). Auswahl/Sizing per
    /// reinem <see cref="FarmingExecutionPlanner"/> (Score-Priorisierung unter allen Risiko-Limits),
    /// Demo-Fill per <see cref="FarmingFillModel"/> (Maker-Einstieg, 0 Fee – der Netto-Edge-Check nutzte
    /// bereits konservativ den Taker-Satz). Positionen landen in rf_positions.
    ///
    /// Live-Execution (Maker-GTC via CLOB, Taker-Fallback nach Timeout) ist bewusst Zielland-Arbeit und
    /// hier nur geloggt – die Entscheidungs-/Sizing-Logik ist identisch und getestet.
    /// </summary>
    public class FarmingExecutionService : BackgroundService
    {
        private static readonly TimeSpan Interval = TimeSpan.FromMinutes(5);

        private readonly TradingState _state;
        private readonly IRfSettingsRepository _settingsRepo;
        private readonly IRfCandidateRepository _candidateRepo;
        private readonly IRfPositionRepository _positionRepo;
        private readonly IRfClosedTradeRepository _closedRepo;
        private readonly TerminalLogger _logger;
        private readonly IDecisionJournal _journal;

        public FarmingExecutionService(
            TradingState state, IRfSettingsRepository settingsRepo, IRfCandidateRepository candidateRepo,
            IRfPositionRepository positionRepo, IRfClosedTradeRepository closedRepo, TerminalLogger logger,
            IDecisionJournal journal)
        {
            _state = state;
            _settingsRepo = settingsRepo;
            _candidateRepo = candidateRepo;
            _positionRepo = positionRepo;
            _closedRepo = closedRepo;
            _logger = logger;
            _journal = journal;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Delay(TimeSpan.FromSeconds(45), stoppingToken); // nach dem Scanner anlaufen
            _logger.Info("ResolutionFarming-Execution gestartet.");

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    foreach (var settings in _settingsRepo.GetAll().Where(s => s.Enabled))
                    {
                        if (stoppingToken.IsCancellationRequested) break;
                        if (!_state.Accounts.TryGetValue(settings.AccountId, out var account)) continue;

                        if (account.IsDemo)
                        {
                            var opened = RunDemoForAccount(settings.AccountId, account.TotalBalance, settings);
                            if (opened.Count > 0)
                                _logger.Trade($"🌱 [RF-Demo] Konto {account.Name}: {opened.Count} Position(en) eröffnet.");
                        }
                        else
                        {
                            _logger.Info($"[RF-Execution] Konto {account.Name}: Live-Execution (Maker-GTC via CLOB) ist Zielland-Arbeit – übersprungen.");
                        }
                    }
                }
                catch (OperationCanceledException) { break; }
                catch (Exception ex) { _logger.Error($"RF-Execution Fehler: {ex.Message}"); }

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

        /// <summary>
        /// Plant und eröffnet Demo-Positionen für einen Account (testbarer Kern). Liefert die eröffneten
        /// Positionen. Bankroll = Referenz für die %-Limits; die Demo-Balance wird bewusst NICHT mutiert
        /// (kein Konflikt mit anderen Modulen auf einem geteilten Konto – PnL fließt über rf_closed_trades).
        /// </summary>
        internal List<RfPosition> RunDemoForAccount(int accountId, decimal bankrollUsd, RfSettings settings)
        {
            var accepted = _candidateRepo.GetRecent(accountId, 200).Where(c => c.Accepted).ToList();
            var open = _positionRepo.GetOpen(accountId);
            DateTime today = DateTime.UtcNow.Date;
            int todayCount = _positionRepo.CountOpenedSince(accountId, today);
            decimal dailyPnl = _closedRepo.RealizedPnlSince(accountId, today);

            var plan = FarmingExecutionPlanner.Plan(accepted, open, settings, bankrollUsd, todayCount, dailyPnl);

            var opened = new List<RfPosition>();
            foreach (var (c, sizeUsd) in plan)
            {
                decimal shares = FarmingFillModel.SharesForBudget(sizeUsd, c.Ask);
                if (shares <= 0m) continue;

                const int makerFeeBps = 0; // Maker-Einstieg fee-frei
                var pos = new RfPosition
                {
                    AccountId = accountId,
                    TokenId = c.TokenId,
                    MarketSlug = c.MarketSlug,
                    MarketQuestion = c.MarketQuestion,
                    Outcome = c.Outcome,
                    Category = c.Category,
                    ClusterKey = c.ClusterKey,
                    EntryPrice = c.Ask,
                    Size = shares,
                    AmountUsd = FarmingFillModel.EntryCostWithFee(shares, c.Ask, makerFeeBps),
                    EntryFeeBps = makerFeeBps,
                    IsDemo = true,
                    OpenedAt = DateTime.UtcNow,
                    EndDate = c.EndDate,
                    Status = "Open"
                };
                _positionRepo.Upsert(pos);
                opened.Add(pos);

                // S-0: Entscheidung strukturiert journalisieren (Rejects stehen bereits in rf_candidates).
                _journal.Write(new DecisionRecord
                {
                    SignalId = Guid.NewGuid().ToString("N"),
                    ModuleName = "ResolutionFarming",
                    AccountId = accountId,
                    IsDemo = true,
                    TokenId = c.TokenId,
                    MarketSlug = c.MarketSlug,
                    MarketQuestion = c.MarketQuestion,
                    Side = "BUY",
                    SignalPrice = c.Ask,
                    MarketEndDate = c.EndDate,
                    Decision = TradeDecision.Executed,
                    Reason = DecisionReason.DemoFilled,
                    Message = $"RF-Demo-Einstieg {shares:F2} Shares @ {c.Ask:F3} (Score {c.Score:F2})",
                    ContextJson = System.Text.Json.JsonSerializer.Serialize(new { shares, sizeUsd, score = c.Score, netEdgePct = c.NetEdgePct, cluster = c.ClusterKey })
                });
            }
            return opened;
        }
    }
}
