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: 'FinlyticAnalyzer', displayName: 'Analyzer Signal Engine', description: 'Scraper Cron-Schedule & Minimaler Signal-Score', icon: Icons.analytics_outlined, accentColor: Color(0xFFF59E0B), ), ServiceConfigMeta( key: 'FinlyticTrades', displayName: 'Trade Manager', description: 'ATR Stop-Loss Multiplikator, Risiko-Prozente & Positionen', 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), ), ]; 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'), }, 'FinlyticAnalyzer': { 'ScanCronSchedule': TextEditingController(text: '0 */1 * * *'), 'MinSignalScore': TextEditingController(text: '75'), 'EnableLog_MqttHealthPing': TextEditingController(text: 'false'), 'EnableLog_MqttGeneral': TextEditingController(text: 'true'), 'EnableLog_AnalyzerAuto': TextEditingController(text: 'true'), 'EnableLog_AnalyzerManual': TextEditingController(text: 'true'), 'EnableLog_DatabaseOps': TextEditingController(text: 'true'), }, 'FinlyticTrades': { 'AtrStopLossMultiplier': TextEditingController(text: '1.5'), 'RiskPerTradePercentage': TextEditingController(text: '1.0'), 'MaxOpenPositions': TextEditingController(text: '5'), }, 'FinlyticFundamentals': { 'CacheTtlHours': TextEditingController(text: '24'), 'EnableYahooFallback': TextEditingController(text: 'true'), }, }; 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, ), ], ); } }