import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/remote/collection_source_remote_ext.dart'; // per appendRemoteEntriesFromDb() import 'package:aves/remote/remote_controller.dart'; import 'package:aves/remote/remote_sync_bus.dart'; import 'remote_settings.dart'; import 'remote_http.dart'; import 'remote_origin.dart'; class RemoteSettingsPage extends StatefulWidget { const RemoteSettingsPage({super.key}); @override State createState() => _RemoteSettingsPageState(); } class _RemoteSettingsPageState extends State { final _form = GlobalKey(); bool _loaded = false; bool _saving = false; bool _enabled = RemoteSettings.defaultEnabled; final _baseUrl = TextEditingController(text: RemoteSettings.defaultBaseUrl); final _indexPath = TextEditingController(text: RemoteSettings.defaultIndexPath); final _email = TextEditingController(); final _password = TextEditingController(); @override void initState() { super.initState(); _load(); } @override void dispose() { _baseUrl.dispose(); _indexPath.dispose(); _email.dispose(); _password.dispose(); super.dispose(); } Future _load() async { try { final s = await RemoteSettings.load(); if (!mounted) return; setState(() { _enabled = s.enabled; _baseUrl.text = s.baseUrl; _indexPath.text = s.indexPath; _email.text = s.email; _password.text = s.password; _loaded = true; }); } catch (e) { _showSnack('Impossibile leggere le impostazioni sicure: $e'); if (!mounted) return; setState(() { _enabled = RemoteSettings.defaultEnabled; _baseUrl.text = RemoteSettings.defaultBaseUrl; _indexPath.text = RemoteSettings.defaultIndexPath; _email.text = ''; _password.text = ''; _loaded = true; }); } } String? _validateBaseUrl(String? v) { final s = (v ?? '').trim(); if (s.isEmpty) return 'Obbligatorio'; final uri = Uri.tryParse(s); if (uri == null || !(uri.hasScheme && (uri.scheme == 'http' || uri.scheme == 'https'))) { return 'URL non valida (deve iniziare con http/https)'; } if (RegExp(r'[\u200B-\u200F\u202A-\u202E\u2060-\u2064\uFEFF]').hasMatch(s)) { return 'URL contiene caratteri non validi (invisibili)'; } return null; } String? _validateIndex(String? v) { final s = (v ?? '').trim(); if (s.isEmpty) return 'Obbligatorio'; return null; } Future _applyRuntimeEffects() async { // Applica subito l'effetto ON/OFF in UI try { final source = context.read(); if (!_enabled) { // OFF: nascondi remoti dalla UI e imposta icona grigia final remotesInMemory = source.allEntries.where((e) => e.origin == RemoteOrigin.value).toSet(); if (remotesInMemory.isNotEmpty) { source.removeEntriesFromMemory(remotesInMemory); } RemoteSyncBus.instance.setDisabled(); } else { // ON: mostra subito remoti da DB (senza full sync qui) await source.appendRemoteEntriesFromDb(); await RemoteController.instance.initBusFromSettings(); } } catch (_) { // se la pagina non è nel contesto con Provider(CollectionSource), non facciamo crash } } Future _save() async { if (!(_form.currentState?.validate() ?? false)) return; setState(() => _saving = true); try { final s = RemoteSettings( enabled: _enabled, baseUrl: _baseUrl.text.trim(), indexPath: _indexPath.text.trim(), email: _email.text.trim(), password: _password.text, ); await s.save(); // aggiorna headers/token await RemoteHttp.refreshFromSettings(); await RemoteHttp.warmUp(); // ✅ applica subito ON/OFF live await _applyRuntimeEffects(); if (!mounted) return; _showSnack('Impostazioni remote salvate'); Navigator.of(context).maybePop(); } catch (e) { if (!mounted) return; _showSnack('Salvataggio fallito: $e'); } finally { if (mounted) setState(() => _saving = false); } } void _showSnack(String msg) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( behavior: SnackBarBehavior.fixed, content: Text(msg), duration: const Duration(seconds: 3), ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar(title: const Text('Remote Settings')), body: !_loaded ? const Center(child: CircularProgressIndicator()) : AbsorbPointer( absorbing: _saving, child: Form( key: _form, child: ListView( padding: const EdgeInsets.all(16), children: [ SwitchListTile( title: const Text('Abilita sync remoto'), value: _enabled, onChanged: (v) => setState(() => _enabled = v), ), const SizedBox(height: 8), TextFormField( controller: _baseUrl, decoration: const InputDecoration( labelText: 'Base URL (es. https://server.tld)', hintText: 'https://example.org', ), keyboardType: TextInputType.url, autovalidateMode: AutovalidateMode.onUserInteraction, validator: _validateBaseUrl, ), const SizedBox(height: 12), TextFormField( controller: _indexPath, decoration: const InputDecoration( labelText: 'Index path (es. photos/)', hintText: 'photos/', ), autovalidateMode: AutovalidateMode.onUserInteraction, validator: _validateIndex, ), const SizedBox(height: 12), TextFormField( controller: _email, decoration: const InputDecoration( labelText: 'User/Email', hintText: 'utente@example.org', ), keyboardType: TextInputType.emailAddress, ), const SizedBox(height: 12), TextFormField( controller: _password, obscureText: true, decoration: const InputDecoration( labelText: 'Password', ), ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _saving ? null : _save, icon: _saving ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( theme.colorScheme.onPrimary, ), ), ) : const Icon(Icons.save), label: Text(_saving ? 'Salvataggio in corso...' : 'Salva'), ), ), ], ), ), ), ); } }