import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/network/api_client.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../../core/widgets/status_badge.dart'; import '../../asset_detail/repositories/asset_repository.dart'; import '../models/trade_model.dart'; import '../models/trade_acceptance_dto.dart'; import '../models/derivative_item_model.dart'; import 'derivative_picker_modal.dart'; import 'trade_execution_ai_plan_card.dart'; class TradeExecutionCockpit extends StatefulWidget { final TradeModel trade; final String defaultSymbol; final bool isActive; final Function(TradeAcceptanceDto dto) onAccept; final Function(String tradeId)? onReject; const TradeExecutionCockpit({ super.key, required this.trade, required this.defaultSymbol, this.isActive = false, required this.onAccept, this.onReject, }); static Future show( BuildContext context, { required TradeModel trade, required String defaultSymbol, bool isActive = false, required Function(TradeAcceptanceDto dto) onAccept, Function(String tradeId)? onReject, }) { return showDialog( context: context, barrierDismissible: true, builder: (dialogContext) => TradeExecutionCockpit( trade: trade, defaultSymbol: defaultSymbol, isActive: isActive, onAccept: onAccept, onReject: onReject, ), ); } @override State createState() => _TradeExecutionCockpitState(); } class _InstrumentOption { final String label; final String value; const _InstrumentOption(this.label, this.value); } class _TradeExecutionCockpitState extends State { late TextEditingController _entryPriceCtrl; late TextEditingController _positionSizeCtrl; late TextEditingController _leverageCtrl; late TextEditingController _stopLossCtrl; late TextEditingController _takeProfitCtrl; late TextEditingController _derivativeIsinCtrl; late TextEditingController _entryFeeCtrl; late TextEditingController _exitFeeCtrl; late String _selectedInstrument; DerivativeItemModel? _selectedDerivative; double? _derivativePrice; bool _isFetchingDerivativePrice = false; List<_InstrumentOption> get _availableInstruments { final assetType = widget.trade.assetType.toLowerCase(); final categories = widget.trade.derivativeProductCategories; final hasCfd = widget.trade.hasCfd; final list = <_InstrumentOption>[]; if (assetType == 'crypto') { list.add(const _InstrumentOption('Krypto (Spot)', 'Crypto')); if (hasCfd) { list.add(const _InstrumentOption('Krypto CFD', 'CFD')); } } else if (assetType == 'etf') { list.add(const _InstrumentOption('ETF (Spot)', 'Stock')); if (categories.isEmpty || categories.contains('knockOutProduct')) { list.add(const _InstrumentOption('⚡ Knock-Out (Turbo)', 'KnockOut')); } if (categories.contains('vanillaWarrant')) { list.add(const _InstrumentOption('Optionsschein', 'Option')); } if (categories.contains('factorCertificate')) { list.add(const _InstrumentOption('Faktor-Zertifikat', 'Factor')); } if (hasCfd) { list.add(const _InstrumentOption('CFD', 'CFD')); } } else { // Default: Stock or other list.add(const _InstrumentOption('Aktie (Spot)', 'Stock')); if (categories.isEmpty || categories.contains('knockOutProduct')) { list.add(const _InstrumentOption('⚡ Knock-Out (Turbo)', 'KnockOut')); } if (categories.contains('vanillaWarrant')) { list.add(const _InstrumentOption('Optionsschein', 'Option')); } if (categories.contains('factorCertificate')) { list.add(const _InstrumentOption('Faktor-Zertifikat', 'Factor')); } if (hasCfd) { list.add(const _InstrumentOption('CFD', 'CFD')); } } return list; } String _formatCurrencyNum(double val) { if (val <= 0) return '0'; if (val == val.roundToDouble()) { return val.toInt().toString(); } var s = val.toStringAsFixed(2); if (s.endsWith('0')) { s = s.substring(0, s.length - 1); } return s.replaceAll('.', ','); } String _formatLeverage(double lev) { if (lev <= 0) return '1x'; if (lev == lev.roundToDouble()) { return '${lev.toInt()}x'; } var s = lev.toStringAsFixed(2); if (s.endsWith('0')) { s = s.substring(0, s.length - 1); } return '${s.replaceAll('.', ',')}x'; } String _formatLeverageNum(double lev) { if (lev <= 0) return '1'; if (lev == lev.roundToDouble()) { return lev.toInt().toString(); } var s = lev.toStringAsFixed(2); if (s.endsWith('0')) { s = s.substring(0, s.length - 1); } return s.replaceAll('.', ','); } @override void initState() { super.initState(); final initEntry = widget.trade.actualEntryPrice > 0 ? widget.trade.actualEntryPrice : (widget.trade.entryPrice > 0 ? widget.trade.entryPrice : 100.0); final initPos = widget.trade.positionSize > 0 ? widget.trade.positionSize : 1000.0; final initLev = widget.trade.leverageUsed > 0 ? widget.trade.leverageUsed : (widget.trade.maxLeverage > 0 ? widget.trade.maxLeverage : 1.0); _entryPriceCtrl = TextEditingController(text: initEntry.toStringAsFixed(2)); _positionSizeCtrl = TextEditingController(text: _formatCurrencyNum(initPos)); _leverageCtrl = TextEditingController(text: _formatLeverageNum(initLev)); _stopLossCtrl = TextEditingController(text: widget.trade.stopLoss > 0 ? widget.trade.stopLoss.toStringAsFixed(2) : ''); _takeProfitCtrl = TextEditingController(text: widget.trade.takeProfit > 0 ? widget.trade.takeProfit.toStringAsFixed(2) : ''); _derivativeIsinCtrl = TextEditingController(text: widget.trade.derivativeIsin); _entryFeeCtrl = TextEditingController(text: widget.trade.entryFee >= 0 ? _formatCurrencyNum(widget.trade.entryFee > 0 ? widget.trade.entryFee : 1.0) : '1.00'); _exitFeeCtrl = TextEditingController(text: widget.trade.exitFee >= 0 ? _formatCurrencyNum(widget.trade.exitFee > 0 ? widget.trade.exitFee : 1.0) : '1.00'); final available = _availableInstruments; final normalized = _normalizeInstrumentType(widget.trade.instrumentType); if (available.any((opt) => opt.value == normalized)) { _selectedInstrument = normalized; } else { _selectedInstrument = available.isNotEmpty ? available.first.value : 'Stock'; } if (widget.trade.derivativeIsin.isNotEmpty) { _fetchLiveDerivativePrice(widget.trade.derivativeIsin); } _entryPriceCtrl.addListener(() => setState(() {})); _positionSizeCtrl.addListener(() => setState(() {})); _leverageCtrl.addListener(() => setState(() {})); _stopLossCtrl.addListener(() => setState(() {})); _takeProfitCtrl.addListener(() => setState(() {})); _entryFeeCtrl.addListener(() => setState(() {})); _exitFeeCtrl.addListener(() => setState(() {})); } @override void dispose() { _entryPriceCtrl.dispose(); _positionSizeCtrl.dispose(); _leverageCtrl.dispose(); _stopLossCtrl.dispose(); _takeProfitCtrl.dispose(); _derivativeIsinCtrl.dispose(); _entryFeeCtrl.dispose(); _exitFeeCtrl.dispose(); super.dispose(); } String _normalizeInstrumentType(String raw) { final clean = raw.toLowerCase().trim(); if (clean.contains('knock') || clean.contains('zertifikat') || clean.contains('turbo')) return 'KnockOut'; if (clean.contains('factor') || clean.contains('faktor')) return 'Factor'; if (clean.contains('option') || clean.contains('warrant')) return 'Option'; if (clean.contains('cfd')) return 'CFD'; if (clean.contains('crypto') || clean.contains('krypto')) return 'Crypto'; return 'Stock'; } double _parse(TextEditingController ctrl, double fallback) { final clean = ctrl.text.replaceAll(',', '.').trim(); return double.tryParse(clean) ?? fallback; } double get _entryPrice => _parse(_entryPriceCtrl, widget.trade.entryPrice > 0 ? widget.trade.entryPrice : 1.0); double get _positionSize => _parse(_positionSizeCtrl, 1000.0); double get _leverage => _parse(_leverageCtrl, 1.0); double get _stopLoss => _parse(_stopLossCtrl, widget.trade.stopLoss); double get _takeProfit => _parse(_takeProfitCtrl, widget.trade.takeProfit); double get _entryFee => _parse(_entryFeeCtrl, 1.0); double get _exitFee => _parse(_exitFeeCtrl, 1.0); double get _totalFees => _entryFee + _exitFee; double get _effectiveLeverage { final isLeveraged = _selectedInstrument == 'KnockOut' || _selectedInstrument == 'Option' || _selectedInstrument == 'Factor' || _selectedInstrument == 'CFD'; return isLeveraged && _leverage > 0 ? _leverage : 1.0; } double get _quantity => (_entryPrice > 0 && _positionSize > 0) ? (_positionSize / _entryPrice) : 0.0; int get _derivativeQuantity => (_derivativePrice != null && _derivativePrice! > 0 && _positionSize > 0) ? (_positionSize / _derivativePrice!).floor() : 0; double get _riskAmountAbs { if (_entryPrice <= 0 || _stopLoss <= 0) return 0.0; final isShort = widget.trade.signalType.toUpperCase() == 'SELL' || widget.trade.signalType.toUpperCase() == 'SHORT'; final movePct = isShort ? ((_stopLoss - _entryPrice) / _entryPrice) : ((_entryPrice - _stopLoss) / _entryPrice); // Leveraged loss on position final rawLoss = (movePct * _positionSize * _effectiveLeverage).abs(); // Knock-Out and options cannot lose more than the invested position capital (Totalverlust-Kappung) final isDerivative = _selectedInstrument == 'KnockOut' || _selectedInstrument == 'Option' || _selectedInstrument == 'Factor'; final cappedLoss = isDerivative ? rawLoss.clamp(0.0, _positionSize) : rawLoss; return cappedLoss + _totalFees; } double get _rewardAmountAbs { if (_entryPrice <= 0 || _takeProfit <= 0) return 0.0; final isShort = widget.trade.signalType.toUpperCase() == 'SELL' || widget.trade.signalType.toUpperCase() == 'SHORT'; final movePct = isShort ? ((_entryPrice - _takeProfit) / _entryPrice) : ((_takeProfit - _entryPrice) / _entryPrice); final rawProfit = (movePct * _positionSize * _effectiveLeverage); final profitAfterFees = rawProfit - _totalFees; return profitAfterFees > 0 ? profitAfterFees : 0.0; } double get _crv { if (_riskAmountAbs <= 0 || _rewardAmountAbs <= 0) return 0.0; return _rewardAmountAbs / _riskAmountAbs; } List get _availableTpTargets { if (widget.trade.takeProfitTargets.isNotEmpty) { return widget.trade.takeProfitTargets; } if (widget.trade.takeProfit > 0) { return [widget.trade.takeProfit]; } return []; } double _calculateRewardForTarget(double targetPrice) { if (_entryPrice <= 0 || targetPrice <= 0) return 0.0; final isShort = widget.trade.signalType.toUpperCase() == 'SELL' || widget.trade.signalType.toUpperCase() == 'SHORT'; final movePct = isShort ? ((_entryPrice - targetPrice) / _entryPrice) : ((targetPrice - _entryPrice) / _entryPrice); final rawProfit = (movePct * _positionSize * _effectiveLeverage); final profitAfterFees = rawProfit - _totalFees; return profitAfterFees > 0 ? profitAfterFees : 0.0; } double _calculateCrvForTarget(double targetPrice) { final reward = _calculateRewardForTarget(targetPrice); if (_riskAmountAbs <= 0 || reward <= 0) return 0.0; return reward / _riskAmountAbs; } Future _openDerivativeFinder() async { final isinVal = widget.trade.isin.isNotEmpty ? widget.trade.isin : widget.defaultSymbol; final symVal = widget.trade.symbol.isNotEmpty ? widget.trade.symbol : widget.defaultSymbol; final nameVal = widget.trade.companyName.isNotEmpty ? widget.trade.companyName : symVal; final priceVal = _entryPrice > 0 ? _entryPrice : (widget.trade.currentPrice > 0 ? widget.trade.currentPrice : widget.trade.entryPrice); final selected = await DerivativePickerModal.show( context, underlyingIsin: isinVal, underlyingSymbol: symVal, underlyingName: nameVal, initialSignalType: widget.trade.signalType, currentUnderlyingPrice: priceVal, ); if (selected != null) { setState(() { _selectedDerivative = selected; _derivativeIsinCtrl.text = selected.isin; _selectedInstrument = 'KnockOut'; if (selected.leverage > 0) { _leverageCtrl.text = _formatLeverageNum(selected.leverage); } }); _fetchLiveDerivativePrice(selected.isin); } } Future _fetchLiveDerivativePrice(String isin) async { if (isin.trim().isEmpty) return; setState(() => _isFetchingDerivativePrice = true); try { final apiClient = context.read(); final assetRepo = AssetRepository(apiClient: apiClient); final technicals = await assetRepo.getAssetTechnical(isin.trim().toUpperCase(), true); double? fetchedPrice; if (technicals != null) { if (technicals.candles.isNotEmpty) { fetchedPrice = technicals.candles.last.close; } else if (technicals.currentPrice != null && technicals.currentPrice! > 0) { fetchedPrice = technicals.currentPrice; } } if (fetchedPrice != null && fetchedPrice > 0 && mounted) { setState(() { _derivativePrice = fetchedPrice; }); } } catch (_) { } finally { if (mounted) setState(() => _isFetchingDerivativePrice = false); } } TradeAcceptanceDto _buildDto() { final isinVal = widget.trade.isin.isNotEmpty ? widget.trade.isin : (widget.trade.symbol.isNotEmpty ? widget.trade.symbol : widget.defaultSymbol); final symbolVal = widget.trade.symbol.isNotEmpty ? widget.trade.symbol : widget.defaultSymbol; return TradeAcceptanceDto( userId: widget.trade.userId, tradeId: widget.trade.id, analysisId: widget.trade.analysisId, isin: isinVal, symbol: symbolVal, actualEntryPrice: _entryPrice, positionSize: _positionSize, leverageUsed: _leverage, entryFee: _entryFee, exitFee: _exitFee, quantity: _quantity, executionTimestamp: DateTime.now().toUtc(), signalType: widget.trade.signalType, entryPrice: widget.trade.entryPrice, stopLoss: _stopLoss, takeProfit: _takeProfit, instrumentType: _selectedInstrument, derivativeIsin: _derivativeIsinCtrl.text.trim(), timeframe: widget.trade.timeframe, reasoning: widget.trade.reasoning, ); } @override Widget build(BuildContext context) { final isBuy = widget.trade.signalType.toUpperCase() == 'BUY' || widget.trade.signalType.toUpperCase() == 'LONG'; final signalColor = isBuy ? AppTheme.primaryEmerald : AppTheme.accentRed; return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), child: Container( width: 640, constraints: const BoxConstraints(maxHeight: 820), decoration: BoxDecoration( color: AppTheme.cardSurface, borderRadius: BorderRadius.circular(24), border: Border.all(color: AppTheme.glassBorder), boxShadow: [ BoxShadow(color: Colors.black.withValues(alpha: 0.6), blurRadius: 30, offset: const Offset(0, 10)), ], ), child: Column( children: [ // Header Padding( padding: const EdgeInsets.fromLTRB(20, 18, 16, 14), child: Row( children: [ StatusBadge(label: widget.trade.signalType.toUpperCase(), color: signalColor), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.isActive ? 'Einstellungen für Trade #${widget.trade.id}' : '1-Click Trade Execution Cockpit', style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ), Text( '${widget.trade.companyName.isNotEmpty ? widget.trade.companyName : widget.defaultSymbol} (${widget.trade.isin.isNotEmpty ? widget.trade.isin : widget.defaultSymbol})', style: TextStyle(color: AppTheme.textMuted, fontSize: 12), ), ], ), ), IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close, color: Colors.white54), ), ], ), ), const Divider(color: Colors.white12, height: 1), // Scrollable Content Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // AI Plan Summary TradeExecutionAiPlanCard(trade: widget.trade), const SizedBox(height: 18), // SECTION 1: INSTRUMENT & DERIVATIVES const Text('1. FINANZINSTRUMENT & DERIVATE', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w900, letterSpacing: 0.5)), const SizedBox(height: 10), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: _availableInstruments.map((opt) { return _instrumentChip(opt.label, opt.value); }).toList(), ), ), const SizedBox(height: 12), // Derivat Picker Button if (_selectedInstrument == 'KnockOut' || _selectedInstrument == 'Option' || _selectedInstrument == 'Factor') ...[ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppTheme.accentCyan.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppTheme.accentCyan.withValues(alpha: 0.3)), ), child: Row( children: [ Icon(Icons.bolt, color: AppTheme.accentCyan, size: 20), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _selectedDerivative != null ? '${_selectedDerivative!.issuerDisplayName} ${_selectedDerivative!.productCategoryName} (${_formatLeverage(_selectedDerivative!.leverage)})' : (_derivativeIsinCtrl.text.isNotEmpty ? 'Derivat ISIN: ${_derivativeIsinCtrl.text}' : 'Kein Derivat gewählt (Basiswert aktiv)'), style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12), ), if (_selectedDerivative != null) ...[ const SizedBox(height: 2), Wrap( spacing: 8, children: [ if (_selectedDerivative!.barrier > 0) Text('KO-Schwelle: €${_selectedDerivative!.barrier.toStringAsFixed(2)}', style: const TextStyle(color: Colors.orangeAccent, fontSize: 11)), if (_derivativePrice != null && _derivativePrice! > 0) Text('Derivatkurs: €${_derivativePrice!.toStringAsFixed(2)} ($_derivativeQuantity Stk.)', style: TextStyle(color: AppTheme.accentCyan, fontSize: 11, fontWeight: FontWeight.bold)), ], ), ], ], ), ), ElevatedButton.icon( onPressed: _openDerivativeFinder, icon: const Icon(Icons.search, size: 14), label: Text(_selectedDerivative != null ? 'Ändern' : 'Derivat wählen', style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentCyan, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), ), ], ), ), const SizedBox(height: 16), ], // SECTION 2: POSITION & KAPITAL const Text('2. INVESTITION & POSITION', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w900, letterSpacing: 0.5)), const SizedBox(height: 10), // Quick Capital Pills Row( children: [ _capitalPill('250 €', 250), _capitalPill('500 €', 500), _capitalPill('1.000 €', 1000), _capitalPill('2.500 €', 2500), _capitalPill('5.000 €', 5000), ], ), const SizedBox(height: 10), Row( children: [ Expanded( child: _buildInput('Investitionsbetrag (€)', _positionSizeCtrl, Icons.account_balance_wallet), ), const SizedBox(width: 12), Expanded( child: _buildInput( 'Einstiegskurs (€)', _entryPriceCtrl, Icons.price_change, suffixWidget: _isFetchingDerivativePrice ? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.cyan)) : null, ), ), ], ), const SizedBox(height: 12), // Stop-Loss & Take-Profit Row( children: [ Expanded(child: _buildInput('Stop-Loss (€)', _stopLossCtrl, Icons.shield_outlined, accentColor: AppTheme.accentRed)), const SizedBox(width: 12), Expanded(child: _buildInput('Take-Profit (€)', _takeProfitCtrl, Icons.trending_up, accentColor: AppTheme.primaryEmerald)), ], ), if (_availableTpTargets.isNotEmpty) ...[ const SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( 'KI-Ziele (TP-Stufen):', style: TextStyle(color: AppTheme.textMuted, fontSize: 11, fontWeight: FontWeight.bold), ), const SizedBox(width: 8), Expanded( child: Wrap( spacing: 6, runSpacing: 4, children: _availableTpTargets.asMap().entries.map((entry) { final idx = entry.key; final targetPrice = entry.value; final isShort = widget.trade.signalType.toUpperCase() == 'SELL' || widget.trade.signalType.toUpperCase() == 'SHORT'; final diffPct = _entryPrice > 0 ? (isShort ? ((_entryPrice - targetPrice) / _entryPrice * 100) : ((targetPrice - _entryPrice) / _entryPrice * 100)) : 0.0; final isSelected = (_takeProfit - targetPrice).abs() < 0.001; return InkWell( onTap: () { setState(() { _takeProfitCtrl.text = targetPrice.toStringAsFixed(2); }); }, borderRadius: BorderRadius.circular(8), child: AnimatedContainer( duration: const Duration(milliseconds: 150), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: isSelected ? AppTheme.primaryEmerald.withValues(alpha: 0.2) : Colors.white.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(8), border: Border.all( color: isSelected ? AppTheme.primaryEmerald : Colors.white.withValues(alpha: 0.15), width: isSelected ? 1.5 : 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( 'TP${idx + 1}: €${targetPrice.toStringAsFixed(2)}', style: TextStyle( color: isSelected ? AppTheme.primaryEmerald : Colors.white, fontSize: 11, fontWeight: isSelected ? FontWeight.w900 : FontWeight.bold, ), ), if (diffPct != 0) ...[ const SizedBox(width: 4), Text( '(${diffPct >= 0 ? "+" : ""}${diffPct.toStringAsFixed(1)}%)', style: TextStyle( color: diffPct >= 0 ? AppTheme.primaryEmerald : AppTheme.accentRed, fontSize: 10, fontWeight: FontWeight.bold, ), ), ], ], ), ), ); }).toList(), ), ), ], ), ], const SizedBox(height: 18), // SECTION 3: AUTOMATISCH ERRECHNETE KENNZAHLEN (ZONE 2) const Text('3. LIVE-KALKULATION (AUTOMATISCH)', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w900, letterSpacing: 0.5)), const SizedBox(height: 10), Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _statTile( _selectedInstrument == 'KnockOut' || _selectedInstrument == 'Option' || _selectedInstrument == 'Factor' ? 'Stückzahl (${_derivativePrice != null ? "Derivat" : "Basiswert"})' : 'Stückzahl', _derivativePrice != null && _derivativePrice! > 0 ? '$_derivativeQuantity Stk.' : '${_quantity.toStringAsFixed(2)} Stk.', Colors.white, ), _statTile('Max. Verlust (SL)', '-€${_riskAmountAbs.toStringAsFixed(2)}', AppTheme.accentRed), _statTile('Gewinn-Potenzial (TP)', '+€${_rewardAmountAbs.toStringAsFixed(2)}', AppTheme.primaryEmerald), _statTile('Chance-Risiko (CRV)', _crv > 0 ? '1 : ${_crv.toStringAsFixed(2)}' : '-', AppTheme.accentCyan), ], ), const Divider(color: Colors.white10, height: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Pauschalgebühren (€${_totalFees.toStringAsFixed(2)} gesamt):', style: TextStyle(color: AppTheme.textMuted, fontSize: 11)), if (_effectiveLeverage > 1.0) Text('Effektiver Hebel: ${_formatLeverage(_effectiveLeverage)}', style: TextStyle(color: AppTheme.accentCyan, fontSize: 11, fontWeight: FontWeight.bold)), ], ), const SizedBox(height: 6), Row( children: [ Expanded( child: _buildSmallFeeInput('Kauf (€)', _entryFeeCtrl), ), const SizedBox(width: 8), Expanded( child: _buildSmallFeeInput('Verkauf (€)', _exitFeeCtrl), ), const SizedBox(width: 8), InkWell( onTap: () { setState(() { if (_entryFee == 0) { _entryFeeCtrl.text = '1,00'; } else { _entryFeeCtrl.text = '0,00'; } }); }, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), decoration: BoxDecoration( color: _entryFee == 0 ? AppTheme.accentCyan.withValues(alpha: 0.15) : Colors.white.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(8), border: Border.all(color: _entryFee == 0 ? AppTheme.accentCyan : Colors.white12), ), child: Text( _entryFee == 0 ? '✓ Im Investitionsbetrag enthalten' : 'Kaufgebühr im Betrag enthalten?', style: TextStyle( color: _entryFee == 0 ? AppTheme.accentCyan : AppTheme.textMuted, fontSize: 10, fontWeight: FontWeight.w600, ), ), ), ), ], ), const SizedBox(height: 4), Text( 'ℹ️ Hinweis: Rechnerischer Näherungswert. Der effektive Hebel eines Derivats verändert sich dynamisch mit dem Kurs des Basiswerts (Omega/Delta).', style: TextStyle(color: AppTheme.textMuted, fontSize: 11, fontStyle: FontStyle.italic), ), ], ), // MULTI-TARGET GEWINNSTUFEN (TP1 - TP3) if (_availableTpTargets.length > 1) ...[ const Divider(color: Colors.white10, height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '🎯 MEHRSTUFIGE GEWINN-KALKULATION', style: TextStyle( color: AppTheme.primaryEmerald, fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 0.5, ), ), Text( 'Klick zum Aktivieren', style: TextStyle(color: AppTheme.textMuted, fontSize: 10), ), ], ), const SizedBox(height: 8), ..._availableTpTargets.asMap().entries.map((entry) { final idx = entry.key; final targetPrice = entry.value; final isSelected = (_takeProfit - targetPrice).abs() < 0.001; final reward = _calculateRewardForTarget(targetPrice); final crv = _calculateCrvForTarget(targetPrice); final isShort = widget.trade.signalType.toUpperCase() == 'SELL' || widget.trade.signalType.toUpperCase() == 'SHORT'; final movePct = _entryPrice > 0 ? (isShort ? ((_entryPrice - targetPrice) / _entryPrice * 100) : ((targetPrice - _entryPrice) / _entryPrice * 100)) : 0.0; final retPct = _positionSize > 0 ? (reward / _positionSize * 100) : 0.0; return InkWell( onTap: () { setState(() { _takeProfitCtrl.text = targetPrice.toStringAsFixed(2); }); }, borderRadius: BorderRadius.circular(8), child: Container( margin: const EdgeInsets.only(bottom: 5), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( color: isSelected ? AppTheme.primaryEmerald.withValues(alpha: 0.14) : Colors.white.withValues(alpha: 0.03), borderRadius: BorderRadius.circular(8), border: Border.all( color: isSelected ? AppTheme.primaryEmerald.withValues(alpha: 0.7) : Colors.white.withValues(alpha: 0.07), width: isSelected ? 1.5 : 1, ), ), child: Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: isSelected ? AppTheme.primaryEmerald : Colors.white12, borderRadius: BorderRadius.circular(4), ), child: Text( 'TP${idx + 1}', style: TextStyle( color: isSelected ? Colors.black : Colors.white, fontSize: 10, fontWeight: FontWeight.w900, ), ), ), const SizedBox(width: 8), Text( '€${targetPrice.toStringAsFixed(2)}', style: TextStyle( color: Colors.white, fontSize: 12, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), const SizedBox(width: 6), Text( '(${movePct >= 0 ? "+" : ""}${movePct.toStringAsFixed(1)}% Basiswert)', style: TextStyle(color: AppTheme.textMuted, fontSize: 10.5), ), const Spacer(), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '+€${reward.toStringAsFixed(2)} (+${retPct.toStringAsFixed(1)}%)', style: TextStyle( color: AppTheme.primaryEmerald, fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( 'CRV 1 : ${crv > 0 ? crv.toStringAsFixed(2) : "-"}', style: TextStyle(color: AppTheme.accentCyan, fontSize: 10), ), ], ), ], ), ), ); }), ], ], ), ), const SizedBox(height: 14), // SECTION 4: RECHTLICHER DISCLAIMER Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.amber.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.amber.withValues(alpha: 0.25)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.gavel_outlined, size: 16, color: Colors.amber.withValues(alpha: 0.85)), const SizedBox(width: 10), Expanded( child: Text( 'Rechtlicher Hinweis: Keine Anlageberatung. Sämtliche Analysen, Kennzahlen und Simulationen dienen ausschließlich Informations- und Bildungszwecken. Der Handel mit Hebelprodukten (Derivate, CFDs) birgt erhebliche Risiken bis hin zum Totalverlust des eingesetzten Kapitals.', style: TextStyle(color: AppTheme.textMuted, fontSize: 10.5, height: 1.35), ), ), ], ), ), ], ), ), ), // Actions Footer Padding( padding: const EdgeInsets.all(16), child: Row( children: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Abbrechen', style: TextStyle(color: AppTheme.textMuted)), ), const Spacer(), if (!widget.isActive && widget.onReject != null) ...[ OutlinedButton.icon( onPressed: () { widget.onReject!(widget.trade.id); Navigator.pop(context); }, icon: Icon(Icons.cancel, color: AppTheme.accentRed, size: 16), label: Text('Ablehnen', style: TextStyle(color: AppTheme.accentRed)), style: OutlinedButton.styleFrom(side: BorderSide(color: AppTheme.accentRed.withValues(alpha: 0.5))), ), const SizedBox(width: 10), ], ElevatedButton.icon( onPressed: () { final dto = _buildDto(); widget.onAccept(dto); Navigator.pop(context); }, icon: Icon(widget.isActive ? Icons.save : Icons.check_circle, size: 18), label: Text( widget.isActive ? 'Einstellungen Speichern' : 'Trade Jetzt Eröffnen', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14), ), style: ElevatedButton.styleFrom( backgroundColor: widget.isActive ? AppTheme.accentCyan : AppTheme.primaryEmerald, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ), ], ), ), ], ), ), ); } Widget _instrumentChip(String label, String value) { final isSelected = _selectedInstrument == value; return GestureDetector( onTap: () => setState(() => _selectedInstrument = value), child: Container( margin: const EdgeInsets.only(right: 8), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: isSelected ? AppTheme.primaryEmerald.withValues(alpha: 0.2) : AppTheme.glassSurface, borderRadius: BorderRadius.circular(10), border: Border.all(color: isSelected ? AppTheme.primaryEmerald : AppTheme.glassBorder), ), child: Text( label, style: TextStyle( color: isSelected ? AppTheme.primaryEmerald : Colors.white70, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, fontSize: 12, ), ), ), ); } Widget _capitalPill(String label, double val) { return GestureDetector( onTap: () => setState(() => _positionSizeCtrl.text = val.toStringAsFixed(0)), child: Container( margin: const EdgeInsets.only(right: 8), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.06), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.white12), ), child: Text(label, style: const TextStyle(color: Colors.white70, fontSize: 11, fontWeight: FontWeight.bold)), ), ); } Widget _buildInput(String label, TextEditingController controller, IconData icon, {Color? accentColor, Widget? suffixWidget}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: TextStyle(color: accentColor ?? AppTheme.textSecondary, fontSize: 11, fontWeight: FontWeight.bold)), const SizedBox(height: 6), TextField( controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true), style: const TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.bold), decoration: InputDecoration( prefixIcon: Icon(icon, size: 16, color: accentColor ?? Colors.white54), suffixIcon: suffixWidget, filled: true, fillColor: AppTheme.glassSurface, contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppTheme.glassBorder)), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppTheme.glassBorder)), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: accentColor ?? AppTheme.accentCyan)), ), ), ], ); } Widget _buildSmallFeeInput(String label, TextEditingController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: TextStyle(color: AppTheme.textMuted, fontSize: 10)), const SizedBox(height: 3), SizedBox( height: 32, child: TextField( controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true), style: const TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold), decoration: InputDecoration( filled: true, fillColor: AppTheme.glassSurface, contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), border: OutlineInputBorder(borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: AppTheme.glassBorder)), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: AppTheme.glassBorder)), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: AppTheme.accentCyan)), ), ), ), ], ); } Widget _statTile(String label, String value, Color col) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: TextStyle(color: AppTheme.textMuted, fontSize: 10)), const SizedBox(height: 2), Text(value, style: TextStyle(color: col, fontWeight: FontWeight.bold, fontSize: 13)), ], ); } }