using System;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Extensions.DependencyInjection;
using PolyTrader.Modules.ResolutionFarming.Models;
using PolyTrader.Modules.ResolutionFarming.Persistence;
using PolyTraderSharp;
namespace PolyTrader.Modules.ResolutionFarming.Ui
{
///
/// Hauptfenster des ResolutionFarming-Moduls: Tabs Kandidaten, Positionen, Historie/Statistik,
/// Settings. Layout im Designer (ResolutionFarmingMainForm.Designer.cs), Verhalten/Daten hier.
/// DB-Zugriffe defensiv (Guarded), damit die UI auch ohne angewendete rf_-Migration bedienbar bleibt.
///
public partial class ResolutionFarmingMainForm : Form
{
private IRfCandidateRepository? _candidates;
private IRfPositionRepository? _positions;
private IRfClosedTradeRepository? _closed;
private IRfSettingsRepository? _settingsRepo;
private TradingState? _state;
private RfSettings? _currentSettings;
public ResolutionFarmingMainForm()
{
InitializeComponent();
btnCandRefresh.Click += (_, _) => RefreshCandidates();
cbCandAccount.SelectedIndexChanged += (_, _) => RefreshCandidates();
btnPosRefresh.Click += (_, _) => RefreshPositions();
btnHistRefresh.Click += (_, _) => RefreshHistory();
btnSettingsSave.Click += (_, _) => SaveSettings();
cbSettingsAccount.SelectedIndexChanged += (_, _) => LoadSettings();
}
/// Injiziert Repos/State (nach DI-Auflösung) und lädt die Ansichten.
public void Initialize(IServiceProvider services)
{
_candidates = services.GetRequiredService();
_positions = services.GetRequiredService();
_closed = services.GetRequiredService();
_settingsRepo = services.GetRequiredService();
_state = services.GetRequiredService();
PopulateAccounts();
RefreshCandidates();
RefreshPositions();
RefreshHistory();
}
// ---------------- Daten ----------------
private void PopulateAccounts()
{
if (_state == null) return;
var items = _state.Accounts.Values
.OrderBy(a => a.AccountId)
.Select(a => new AccountItem(a.AccountId, string.IsNullOrEmpty(a.Name) ? $"#{a.AccountId}" : $"{a.Name} (#{a.AccountId}){(a.IsDemo ? " · Demo" : "")}"))
.ToList();
foreach (var combo in new[] { cbCandAccount, cbSettingsAccount })
{
combo.DisplayMember = nameof(AccountItem.Label);
combo.ValueMember = nameof(AccountItem.Id);
combo.DataSource = items.ToList();
}
if (items.Count > 0) LoadSettings();
else SetStatus("Keine Accounts vorhanden.");
}
private int? SelectedAccountId(ComboBox combo) =>
combo.SelectedItem is AccountItem it ? it.Id : (int?)null;
private void RefreshCandidates()
{
if (_candidates == null || SelectedAccountId(cbCandAccount) is not int accId) return;
Guarded("Kandidaten", () => dgvCandidates.DataSource = _candidates.GetRecent(accId, 200));
}
private void RefreshPositions()
{
if (_positions == null) return;
Guarded("Positionen", () => dgvPositions.DataSource = _positions.GetAllOpen());
}
private void RefreshHistory()
{
if (_closed == null) return;
Guarded("Historie", () =>
{
var trades = _closed.Find(_ => true);
dgvHistory.DataSource = trades;
lblHistSummary.Text = BuildSummary(trades);
});
}
private static string BuildSummary(System.Collections.Generic.List trades)
{
if (trades.Count == 0) return "Noch keine abgeschlossenen Trades.";
int wins = trades.Count(t => t.RealizedPnl > 0m);
decimal pnl = trades.Sum(t => t.RealizedPnl);
decimal fees = trades.Sum(t => t.TotalFees);
double winrate = 100.0 * wins / trades.Count;
// Winrate je Preisband (Kalibrierung: realisierte Winrate sollte > Band-Mitte liegen).
var bands = trades
.GroupBy(t => $"{Math.Floor(t.EntryPrice * 20m) / 20m:F2}") // 5-¢-Bänder
.OrderBy(g => g.Key)
.Select(g => $"{g.Key}: {100.0 * g.Count(x => x.RealizedPnl > 0m) / g.Count():F0}% ({g.Count()})");
return $"Trades: {trades.Count} | Winrate: {winrate:F1}% | Netto-PnL: {pnl:F2} USDC | Fees: {fees:F2}\n" +
$"Winrate je Preisband: {string.Join(" | ", bands)}";
}
private void LoadSettings()
{
if (_settingsRepo == null || SelectedAccountId(cbSettingsAccount) is not int accId) return;
Guarded("Settings", () =>
{
_currentSettings = _settingsRepo.Get(accId) ?? new RfSettings { AccountId = accId };
pgSettings.SelectedObject = _currentSettings;
SetStatus($"Einstellungen für Konto #{accId}.");
});
}
private void SaveSettings()
{
if (_settingsRepo == null || _currentSettings == null) return;
Guarded("Speichern", () =>
{
_settingsRepo.Upsert(_currentSettings);
SetStatus($"Gespeichert für Konto #{_currentSettings.AccountId} um {DateTime.Now:HH:mm:ss}.");
});
}
private void Guarded(string what, Action action)
{
try { action(); }
catch (Exception ex) { SetStatus($"{what}: DB nicht bereit ({ex.Message}). Migration angewendet?"); }
}
private void SetStatus(string text) => lblRfStatus.Text = text;
private sealed record AccountItem(int Id, string Label);
}
}