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 '../repositories/admin_repository.dart'; import 'service_settings_form.dart'; class ServiceConfigMeta { final String key; final String displayName; final String description; final IconData icon; final Color accentColor; const ServiceConfigMeta({ required this.key, required this.displayName, required this.description, required this.icon, required this.accentColor, }); } class PipelineSettingsWidget extends StatefulWidget { final ApiClient? apiClient; final AdminRepository? repository; const PipelineSettingsWidget({super.key, this.apiClient, this.repository}); @override State createState() => _PipelineSettingsWidgetState(); } class _PipelineSettingsWidgetState extends State { late final AdminRepository _repository; String _selectedServiceKey = 'FinlyticAssets'; bool _isLoading = false; bool _isSaving = false; static const List _services = [ ServiceConfigMeta( key: 'FinlyticAssets', displayName: 'Asset Katalog & Logos', description: 'Verwaltet ISIN Asset-Stammdaten & Trade Republic Logo Fetcher', icon: Icons.inventory_2_outlined, accentColor: Color(0xFF00E5FF), ), ServiceConfigMeta( key: 'FinlyticNews', displayName: 'News Scraper & AI', description: 'RSS Web Scraper Intervall & Entwurf-Retention', icon: Icons.newspaper_outlined, accentColor: Color(0xFF3B82F6), ), ServiceConfigMeta( key: 'FinlyticTechnicalAnalysis', displayName: 'Technische Analyse', description: 'EMA/SMA Perioden, RSI Grenzwerte & Supertrend Multiplikator', icon: Icons.show_chart_outlined, accentColor: Color(0xFF8B5CF6), ), ServiceConfigMeta( key: 'FinlyticSentiment', displayName: 'Sentiment NLP', description: 'NLP Vertrauens-Schwellenwerte & Text-Batching', icon: Icons.psychology_outlined, accentColor: Color(0xFFEC4899), ), ServiceConfigMeta( key: 'FinlyticEngine', displayName: 'Trading Engine', description: 'Strategy Screener, Trade Lifecycle & Risikomanagement', icon: Icons.candlestick_chart_outlined, accentColor: Color(0xFF10B981), ), ServiceConfigMeta( key: 'FinlyticFundamentals', displayName: 'Fundamentaldaten', description: 'Cache TTL Dauer & Yahoo Finance Fallback', icon: Icons.corporate_fare_outlined, accentColor: Color(0xFF06B6D4), ), ServiceConfigMeta( key: 'FinlyticBot', displayName: 'FinlyticBot (Paper)', description: 'Alpaca Paper Trading, Risikomanagement & Sizing Engine', icon: Icons.smart_toy_outlined, accentColor: Color(0xFF10B981), ), ]; final Map> _controllers = { 'FinlyticAssets': { 'TradeRepublicMaxRequestPageSize': TextEditingController(text: '100'), 'AssetUpdateTypeDelay': TextEditingController(text: '0'), 'BatchAssetUpdateDelay': TextEditingController(text: '5'), }, 'FinlyticNews': { 'ScrapingIntervalMinutes': TextEditingController(text: '15'), 'PollingFrequencyMinutes': TextEditingController(text: '15'), 'ArticleRetentionDays': TextEditingController(text: '90'), 'DefaultPageSize': TextEditingController(text: '20'), }, 'FinlyticTechnicalAnalysis': { 'EmaShortPeriod': TextEditingController(text: '20'), 'SmaMediumPeriod': TextEditingController(text: '50'), 'SmaLongPeriod': TextEditingController(text: '200'), 'RsiOverboughtLimit': TextEditingController(text: '70'), 'RsiOversoldLimit': TextEditingController(text: '30'), 'SupertrendMultiplier': TextEditingController(text: '3.0'), }, 'FinlyticSentiment': { 'MinConfidenceScore': TextEditingController(text: '0.70'), 'MaxBatchSize': TextEditingController(text: '50'), }, 'FinlyticEngine': { 'Engine.MinCompositeScore': TextEditingController(text: '75.0'), 'Engine.WeightTechnical': TextEditingController(text: '0.45'), 'Engine.WeightSentiment': TextEditingController(text: '0.35'), 'Engine.WeightFundamental': TextEditingController(text: '0.20'), 'Engine.EarningsLockoutDays': TextEditingController(text: '2'), 'Engine.MinDerivativeLeverage': TextEditingController(text: '5.0'), 'Engine.TargetDefaultLeverage': TextEditingController(text: '7.0'), 'Engine.KnockOutSafetyBufferPercent': TextEditingController(text: '2.0'), 'Engine.EnableAiValidation': TextEditingController(text: 'true'), 'Engine.EnablePaperTradingBot': TextEditingController(text: 'false'), 'Engine.PollingIntervalSeconds': TextEditingController(text: '120'), 'Engine.MonitoringIntervalSeconds': TextEditingController(text: '60'), }, 'FinlyticFundamentals': { 'CacheTtlHours': TextEditingController(text: '24'), 'EnableYahooFallback': TextEditingController(text: 'true'), }, 'FinlyticBot': { 'Alpaca.KeyId': TextEditingController(text: ''), 'Alpaca.SecretKey': TextEditingController(text: ''), 'Alpaca.IsPaper': TextEditingController(text: 'true'), 'Bot.EnableAutoExecution': TextEditingController(text: 'true'), 'Bot.RiskPerTradePercent': TextEditingController(text: '1.0'), 'Bot.MaxPositionAllocationPercent': TextEditingController(text: '20.0'), 'Bot.MaxConcurrentPositions': TextEditingController(text: '5'), 'Bot.DailyLossLimitPercent': TextEditingController(text: '3.0'), 'Bot.MonitoringIntervalSeconds': TextEditingController(text: '15'), }, }; bool _initialized = false; @override void didChangeDependencies() { super.didChangeDependencies(); if (!_initialized) { _initialized = true; final client = widget.apiClient ?? context.read(); _repository = widget.repository ?? AdminRepository(apiClient: client); _fetchSettings(); } } Future _fetchSettings() async { setState(() => _isLoading = true); try { final settings = await _repository.fetchSettings(); settings.forEach((svc, items) { _controllers.putIfAbsent(svc, () => {}); for (final s in items) { if (_controllers[svc]!.containsKey(s.key)) { _controllers[svc]![s.key]!.text = s.value; } else { _controllers[svc]![s.key] = TextEditingController(text: s.value); } } }); } catch (_) { } finally { if (mounted) setState(() => _isLoading = false); } } Future _saveSettings() async { setState(() => _isSaving = true); try { final currentSvcControllers = _controllers[_selectedServiceKey] ?? {}; final payload = {}; currentSvcControllers.forEach((k, v) { payload[k] = v.text; }); await _repository.updateServiceSettings(_selectedServiceKey, payload); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.check_circle_outline, color: Colors.black), const SizedBox(width: 8), Expanded( child: Text( 'Einstellungen für $_selectedServiceKey gespeichert & synchronisiert.', style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w600), ), ), ], ), backgroundColor: AppTheme.primaryEmerald, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), ); } } catch (ex) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Fehler beim Speichern: $ex'), backgroundColor: AppTheme.accentRed, behavior: SnackBarBehavior.floating, ), ); } } finally { if (mounted) setState(() => _isSaving = false); } } @override Widget build(BuildContext context) { final activeService = _services.firstWhere((s) => s.key == _selectedServiceKey, orElse: () => _services.first); final activeControllers = _controllers[_selectedServiceKey] ?? {}; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: _services.map((svc) { final isSelected = svc.key == _selectedServiceKey; return Padding( padding: const EdgeInsets.only(right: 8, bottom: 12), child: InkWell( onTap: () => setState(() => _selectedServiceKey = svc.key), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( color: isSelected ? svc.accentColor.withValues(alpha: 0.15) : AppTheme.glassSurface, borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? svc.accentColor : AppTheme.glassBorder, width: isSelected ? 1.5 : 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(svc.icon, size: 16, color: isSelected ? svc.accentColor : AppTheme.textMuted), const SizedBox(width: 8), Text( svc.displayName, style: TextStyle( color: isSelected ? Colors.white : AppTheme.textMuted, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, fontSize: 12, ), ), ], ), ), ), ); }).toList(), ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(activeService.displayName, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)), const SizedBox(height: 2), Text(activeService.description, style: TextStyle(fontSize: 12, color: AppTheme.textMuted)), ], ), ElevatedButton.icon( onPressed: _isSaving ? null : _saveSettings, icon: _isSaving ? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black)) : const Icon(Icons.save_outlined, size: 16), label: Text(_isSaving ? 'Speichere...' : 'Speichern'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryEmerald, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), ), ), ], ), const SizedBox(height: 14), if (_isLoading) Center(child: CircularProgressIndicator(color: AppTheme.primaryEmerald)) else ServiceSettingsForm( controllers: activeControllers, accentColor: activeService.accentColor, ), ], ); } }